aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChressie Himpel <chressie@google.com>2022-04-20 16:56:54 +0200
committerChressie Himpel <chressie@google.com>2022-04-20 16:57:46 +0200
commite07d63964b6db13c5439f6d0a5d136e43da74016 (patch)
treec93cc95ae1f0143f24256be1cd7c93a3ed0754e9
parent1f11660f54db8674223127be6ef9c1081ad55437 (diff)
parentd68a8d0f27bb3599b49cacd119d7ac3202248050 (diff)
downloadgo-e07d63964b6db13c5439f6d0a5d136e43da74016.tar.gz
go-e07d63964b6db13c5439f6d0a5d136e43da74016.zip
[dev.boringcrypto] all: merge master into dev.boringcrypto
Change-Id: I52009bf809dda4fbcff03aa82d0ea8aa2a978fa2
-rw-r--r--AUTHORS1
-rw-r--r--CONTRIBUTORS1
-rw-r--r--api/next/35044.txt1
-rw-r--r--api/next/42710.txt2
-rw-r--r--api/next/50340.txt1
-rw-r--r--api/next/50674.txt9
-rw-r--r--api/next/51082.txt61
-rw-r--r--api/next/51644.txt2
-rw-r--r--doc/go1.19.html47
-rw-r--r--misc/cgo/gmp/gmp.go7
-rw-r--r--misc/cgo/testcshared/cshared_test.go49
-rw-r--r--misc/cgo/testcshared/testdata/issue36233/issue36233.go29
-rw-r--r--misc/ios/go_ios_exec.go8
-rw-r--r--src/archive/tar/common.go9
-rw-r--r--src/archive/tar/reader.go6
-rw-r--r--src/archive/tar/strconv.go1
-rw-r--r--src/archive/zip/reader.go3
-rw-r--r--src/archive/zip/reader_test.go27
-rw-r--r--src/builtin/builtin.go10
-rw-r--r--src/bytes/bytes.go21
-rw-r--r--src/bytes/bytes_test.go2
-rw-r--r--src/cmd/addr2line/main.go1
-rw-r--r--src/cmd/asm/doc.go4
-rw-r--r--src/cmd/asm/internal/asm/parse.go8
-rw-r--r--src/cmd/asm/internal/lex/tokenizer.go2
-rw-r--r--src/cmd/buildid/doc.go1
-rw-r--r--src/cmd/cgo/doc.go18
-rw-r--r--src/cmd/cgo/gcc.go28
-rw-r--r--src/cmd/cgo/out.go42
-rw-r--r--src/cmd/compile/internal/abi/abiutils.go2
-rw-r--r--src/cmd/compile/internal/amd64/ssa.go35
-rw-r--r--src/cmd/compile/internal/amd64/versions_test.go1
-rw-r--r--src/cmd/compile/internal/arm/ssa.go2
-rw-r--r--src/cmd/compile/internal/arm64/ssa.go4
-rw-r--r--src/cmd/compile/internal/deadcode/deadcode.go80
-rw-r--r--src/cmd/compile/internal/dwarfgen/dwarf.go6
-rw-r--r--src/cmd/compile/internal/escape/desugar.go4
-rw-r--r--src/cmd/compile/internal/escape/escape.go3
-rw-r--r--src/cmd/compile/internal/escape/stmt.go3
-rw-r--r--src/cmd/compile/internal/gc/obj.go3
-rw-r--r--src/cmd/compile/internal/inline/inl.go6
-rw-r--r--src/cmd/compile/internal/ir/const.go2
-rw-r--r--src/cmd/compile/internal/ir/expr.go10
-rw-r--r--src/cmd/compile/internal/ir/node.go4
-rw-r--r--src/cmd/compile/internal/ir/node_gen.go22
-rw-r--r--src/cmd/compile/internal/ir/op_string.go21
-rw-r--r--src/cmd/compile/internal/ir/stmt.go32
-rw-r--r--src/cmd/compile/internal/liveness/plive.go12
-rw-r--r--src/cmd/compile/internal/mips/ggen.go4
-rw-r--r--src/cmd/compile/internal/mips/ssa.go2
-rw-r--r--src/cmd/compile/internal/mips64/ssa.go2
-rw-r--r--src/cmd/compile/internal/noder/expr.go9
-rw-r--r--src/cmd/compile/internal/noder/noder.go38
-rw-r--r--src/cmd/compile/internal/noder/stencil.go1
-rw-r--r--src/cmd/compile/internal/noder/unified.go28
-rw-r--r--src/cmd/compile/internal/pkginit/init.go6
-rw-r--r--src/cmd/compile/internal/ppc64/ggen.go6
-rw-r--r--src/cmd/compile/internal/ppc64/ssa.go4
-rw-r--r--src/cmd/compile/internal/reflectdata/alg.go114
-rw-r--r--src/cmd/compile/internal/reflectdata/alg_test.go76
-rw-r--r--src/cmd/compile/internal/reflectdata/reflect.go32
-rw-r--r--src/cmd/compile/internal/riscv64/ggen.go2
-rw-r--r--src/cmd/compile/internal/riscv64/ssa.go4
-rw-r--r--src/cmd/compile/internal/s390x/ggen.go2
-rw-r--r--src/cmd/compile/internal/s390x/ssa.go8
-rw-r--r--src/cmd/compile/internal/ssa/addressingmodes.go37
-rw-r--r--src/cmd/compile/internal/ssa/block.go47
-rw-r--r--src/cmd/compile/internal/ssa/branchelim.go10
-rw-r--r--src/cmd/compile/internal/ssa/check.go4
-rw-r--r--src/cmd/compile/internal/ssa/compile.go4
-rw-r--r--src/cmd/compile/internal/ssa/config.go11
-rw-r--r--src/cmd/compile/internal/ssa/cse.go17
-rw-r--r--src/cmd/compile/internal/ssa/debug.go38
-rw-r--r--src/cmd/compile/internal/ssa/debug_test.go2
-rw-r--r--src/cmd/compile/internal/ssa/expand_calls.go42
-rw-r--r--src/cmd/compile/internal/ssa/export_test.go3
-rw-r--r--src/cmd/compile/internal/ssa/func.go17
-rw-r--r--src/cmd/compile/internal/ssa/fuse.go18
-rw-r--r--src/cmd/compile/internal/ssa/fuse_branchredirect.go27
-rw-r--r--src/cmd/compile/internal/ssa/fuse_comparisons.go20
-rw-r--r--src/cmd/compile/internal/ssa/gen/AMD64.rules99
-rw-r--r--src/cmd/compile/internal/ssa/gen/AMD64Ops.go34
-rw-r--r--src/cmd/compile/internal/ssa/gen/generic.rules14
-rw-r--r--src/cmd/compile/internal/ssa/gen/genericOps.go13
-rw-r--r--src/cmd/compile/internal/ssa/gen/rulegen.go2
-rw-r--r--src/cmd/compile/internal/ssa/location.go20
-rw-r--r--src/cmd/compile/internal/ssa/loopbce.go28
-rw-r--r--src/cmd/compile/internal/ssa/looprotate.go24
-rw-r--r--src/cmd/compile/internal/ssa/magic.go6
-rw-r--r--src/cmd/compile/internal/ssa/op.go19
-rw-r--r--src/cmd/compile/internal/ssa/opGen.go483
-rw-r--r--src/cmd/compile/internal/ssa/phielim.go16
-rw-r--r--src/cmd/compile/internal/ssa/phiopt.go22
-rw-r--r--src/cmd/compile/internal/ssa/poset.go10
-rw-r--r--src/cmd/compile/internal/ssa/prove.go122
-rw-r--r--src/cmd/compile/internal/ssa/rewrite.go24
-rw-r--r--src/cmd/compile/internal/ssa/rewriteAMD64.go3142
-rw-r--r--src/cmd/compile/internal/ssa/rewriteCond_test.go6
-rw-r--r--src/cmd/compile/internal/ssa/rewritegeneric.go500
-rw-r--r--src/cmd/compile/internal/ssa/schedule.go16
-rw-r--r--src/cmd/compile/internal/ssa/shift_test.go2
-rw-r--r--src/cmd/compile/internal/ssa/shortcircuit.go30
-rw-r--r--src/cmd/compile/internal/ssa/sparsetree.go1
-rw-r--r--src/cmd/compile/internal/ssa/trim.go10
-rw-r--r--src/cmd/compile/internal/ssa/value.go3
-rw-r--r--src/cmd/compile/internal/ssa/writebarrier.go6
-rw-r--r--src/cmd/compile/internal/ssagen/pgen.go4
-rw-r--r--src/cmd/compile/internal/ssagen/ssa.go127
-rw-r--r--src/cmd/compile/internal/syntax/branches.go8
-rw-r--r--src/cmd/compile/internal/test/float_test.go1
-rw-r--r--src/cmd/compile/internal/test/inl_test.go1
-rw-r--r--src/cmd/compile/internal/test/shift_test.go10
-rw-r--r--src/cmd/compile/internal/test/switch_test.go137
-rw-r--r--src/cmd/compile/internal/test/testdata/addressed_test.go4
-rw-r--r--src/cmd/compile/internal/test/testdata/arith_test.go1
-rw-r--r--src/cmd/compile/internal/test/testdata/ctl_test.go1
-rw-r--r--src/cmd/compile/internal/test/testdata/fp_test.go2
-rw-r--r--src/cmd/compile/internal/test/testdata/loadstore_test.go1
-rw-r--r--src/cmd/compile/internal/typecheck/builtin.go1
-rw-r--r--src/cmd/compile/internal/typecheck/const.go3
-rw-r--r--src/cmd/compile/internal/typecheck/expr.go8
-rw-r--r--src/cmd/compile/internal/typecheck/iexport.go2
-rw-r--r--src/cmd/compile/internal/typecheck/mkbuiltin.go1
-rw-r--r--src/cmd/compile/internal/typecheck/syms.go3
-rw-r--r--src/cmd/compile/internal/typecheck/typecheck.go12
-rw-r--r--src/cmd/compile/internal/types/type.go13
-rw-r--r--src/cmd/compile/internal/types2/api.go1
-rw-r--r--src/cmd/compile/internal/types2/api_test.go12
-rw-r--r--src/cmd/compile/internal/types2/check_test.go2
-rw-r--r--src/cmd/compile/internal/types2/expr.go46
-rw-r--r--src/cmd/compile/internal/types2/infer.go8
-rw-r--r--src/cmd/compile/internal/types2/lookup.go16
-rw-r--r--src/cmd/compile/internal/types2/selection.go7
-rw-r--r--src/cmd/compile/internal/types2/sizes.go26
-rw-r--r--src/cmd/compile/internal/types2/testdata/fixedbugs/issue52031.go33
-rw-r--r--src/cmd/compile/internal/types2/typeterm.go8
-rw-r--r--src/cmd/compile/internal/walk/assign.go78
-rw-r--r--src/cmd/compile/internal/walk/builtin.go26
-rw-r--r--src/cmd/compile/internal/walk/compare.go6
-rw-r--r--src/cmd/compile/internal/walk/expr.go3
-rw-r--r--src/cmd/compile/internal/walk/order.go30
-rw-r--r--src/cmd/compile/internal/walk/stmt.go4
-rw-r--r--src/cmd/compile/internal/walk/switch.go115
-rw-r--r--src/cmd/compile/internal/x86/ssa.go6
-rw-r--r--src/cmd/cover/cover.go2
-rw-r--r--src/cmd/cover/cover_test.go4
-rw-r--r--src/cmd/cover/doc.go1
-rw-r--r--src/cmd/dist/build.go61
-rw-r--r--src/cmd/dist/doc.go20
-rw-r--r--src/cmd/dist/test.go4
-rw-r--r--src/cmd/doc/doc_test.go6
-rw-r--r--src/cmd/doc/main.go5
-rw-r--r--src/cmd/doc/pkg.go23
-rw-r--r--src/cmd/fix/cftype.go8
-rw-r--r--src/cmd/fix/doc.go3
-rw-r--r--src/cmd/fix/egltype.go16
-rw-r--r--src/cmd/fix/jnitype.go8
-rw-r--r--src/cmd/go/alldocs.go2258
-rw-r--r--src/cmd/go/go_test.go4
-rw-r--r--src/cmd/go/internal/bug/bug.go2
-rw-r--r--src/cmd/go/internal/cfg/cfg.go6
-rw-r--r--src/cmd/go/internal/clean/clean.go2
-rw-r--r--src/cmd/go/internal/doc/doc.go2
-rw-r--r--src/cmd/go/internal/envcmd/env.go22
-rw-r--r--src/cmd/go/internal/fix/fix.go2
-rw-r--r--src/cmd/go/internal/fmtcmd/fmt.go2
-rw-r--r--src/cmd/go/internal/generate/generate.go6
-rw-r--r--src/cmd/go/internal/generate/generate_test.go20
-rw-r--r--src/cmd/go/internal/get/get.go2
-rw-r--r--src/cmd/go/internal/help/help.go2
-rw-r--r--src/cmd/go/internal/imports/build.go21
-rw-r--r--src/cmd/go/internal/list/list.go4
-rw-r--r--src/cmd/go/internal/load/pkg.go38
-rw-r--r--src/cmd/go/internal/load/test.go6
-rw-r--r--src/cmd/go/internal/lockedfile/lockedfile_plan9.go6
-rw-r--r--src/cmd/go/internal/lockedfile/lockedfile_test.go1
-rw-r--r--src/cmd/go/internal/lockedfile/transform_test.go1
-rw-r--r--src/cmd/go/internal/modcmd/mod.go2
-rw-r--r--src/cmd/go/internal/modget/get.go10
-rw-r--r--src/cmd/go/internal/modload/buildlist.go84
-rw-r--r--src/cmd/go/internal/modload/edit.go22
-rw-r--r--src/cmd/go/internal/modload/load.go16
-rw-r--r--src/cmd/go/internal/modload/query.go28
-rw-r--r--src/cmd/go/internal/modload/stat_openfile.go2
-rw-r--r--src/cmd/go/internal/robustio/robustio.go6
-rw-r--r--src/cmd/go/internal/run/run.go2
-rw-r--r--src/cmd/go/internal/str/path.go9
-rw-r--r--src/cmd/go/internal/str/str.go2
-rw-r--r--src/cmd/go/internal/str/str_test.go71
-rw-r--r--src/cmd/go/internal/test/testflag.go1
-rw-r--r--src/cmd/go/internal/tool/tool.go2
-rw-r--r--src/cmd/go/internal/version/version.go2
-rw-r--r--src/cmd/go/internal/vet/vet.go2
-rw-r--r--src/cmd/go/internal/work/build.go48
-rw-r--r--src/cmd/go/internal/work/exec.go5
-rw-r--r--src/cmd/go/internal/work/security.go2
-rw-r--r--src/cmd/go/internal/workcmd/work.go2
-rw-r--r--src/cmd/go/script_test.go4
-rw-r--r--src/cmd/go/testdata/script/README2
-rw-r--r--src/cmd/go/testdata/script/build_buildvcs_auto.txt87
-rw-r--r--src/cmd/go/testdata/script/build_trimpath_goroot.txt45
-rw-r--r--src/cmd/go/testdata/script/test_buildvcs.txt2
-rw-r--r--src/cmd/go/testdata/script/test_fuzz_minimize_interesting.txt104
-rw-r--r--src/cmd/go/testdata/script/version_buildvcs_nested.txt2
-rw-r--r--src/cmd/gofmt/doc.go8
-rw-r--r--src/cmd/internal/bio/buf_mmap.go12
-rw-r--r--src/cmd/internal/gcprog/gcprog.go3
-rw-r--r--src/cmd/internal/goobj/objfile.go63
-rw-r--r--src/cmd/internal/obj/arm64/asm7.go2
-rw-r--r--src/cmd/internal/obj/arm64/asm_arm64_test.go11
-rw-r--r--src/cmd/internal/obj/arm64/asm_arm64_test.s7
-rw-r--r--src/cmd/internal/obj/arm64/doc.go201
-rw-r--r--src/cmd/internal/obj/inl.go38
-rw-r--r--src/cmd/internal/obj/link.go27
-rw-r--r--src/cmd/internal/obj/mips/asm0.go6
-rw-r--r--src/cmd/internal/obj/mips/obj0.go10
-rw-r--r--src/cmd/internal/obj/objfile.go16
-rw-r--r--src/cmd/internal/obj/ppc64/asm9.go4
-rw-r--r--src/cmd/internal/obj/ppc64/doc.go151
-rw-r--r--src/cmd/internal/obj/ppc64/obj9.go9
-rw-r--r--src/cmd/internal/obj/riscv/obj.go76
-rw-r--r--src/cmd/internal/obj/s390x/asmz.go4
-rw-r--r--src/cmd/internal/obj/s390x/objz.go6
-rw-r--r--src/cmd/internal/obj/x86/asm6.go47
-rw-r--r--src/cmd/internal/obj/x86/evex.go9
-rw-r--r--src/cmd/internal/objabi/funcid.go40
-rw-r--r--src/cmd/internal/objabi/symkind.go1
-rw-r--r--src/cmd/internal/src/pos.go6
-rw-r--r--src/cmd/internal/sys/arch.go255
-rw-r--r--src/cmd/internal/test2json/test2json.go2
-rw-r--r--src/cmd/link/doc.go4
-rw-r--r--src/cmd/link/internal/benchmark/bench.go40
-rw-r--r--src/cmd/link/internal/ld/asmb.go10
-rw-r--r--src/cmd/link/internal/ld/data.go11
-rw-r--r--src/cmd/link/internal/ld/deadcode.go15
-rw-r--r--src/cmd/link/internal/ld/decodesym.go1
-rw-r--r--src/cmd/link/internal/ld/dwarf.go2
-rw-r--r--src/cmd/link/internal/ld/dwarf_test.go4
-rw-r--r--src/cmd/link/internal/ld/elf.go53
-rw-r--r--src/cmd/link/internal/ld/lib.go222
-rw-r--r--src/cmd/link/internal/ld/link.go18
-rw-r--r--src/cmd/link/internal/ld/main.go5
-rw-r--r--src/cmd/link/internal/ld/outbuf.go44
-rw-r--r--src/cmd/link/internal/ld/outbuf_darwin.go1
-rw-r--r--src/cmd/link/internal/ld/pcln.go24
-rw-r--r--src/cmd/link/internal/ld/stackcheck.go421
-rw-r--r--src/cmd/link/internal/ld/stackcheck_test.go89
-rw-r--r--src/cmd/link/internal/ld/symtab.go2
-rw-r--r--src/cmd/link/internal/ld/testdata/stackcheck/main.go20
-rw-r--r--src/cmd/link/internal/ld/testdata/stackcheck/main.s40
-rw-r--r--src/cmd/link/internal/ld/xcoff.go4
-rw-r--r--src/cmd/link/internal/loader/loader.go55
-rw-r--r--src/cmd/link/internal/ppc64/asm.go51
-rw-r--r--src/cmd/link/internal/sym/symkind.go1
-rw-r--r--src/cmd/link/link_test.go98
-rw-r--r--src/cmd/nm/doc.go2
-rw-r--r--src/cmd/pack/doc.go3
-rw-r--r--src/cmd/test2json/main.go3
-rw-r--r--src/cmd/trace/annotations_test.go6
-rw-r--r--src/cmd/trace/doc.go20
-rw-r--r--src/cmd/vet/doc.go52
-rw-r--r--src/compress/bzip2/bzip2.go4
-rw-r--r--src/compress/flate/dict_decoder.go24
-rw-r--r--src/compress/flate/huffman_bit_writer.go12
-rw-r--r--src/compress/lzw/reader.go4
-rw-r--r--src/container/heap/heap.go1
-rw-r--r--src/container/list/list.go2
-rw-r--r--src/context/context.go16
-rw-r--r--src/crypto/aes/asm_ppc64le.s321
-rw-r--r--src/crypto/aes/cbc_ppc64le.go6
-rw-r--r--src/crypto/aes/cbc_s390x.go1
-rw-r--r--src/crypto/aes/cipher_ppc64le.go43
-rw-r--r--src/crypto/aes/cipher_s390x.go1
-rw-r--r--src/crypto/aes/ctr_s390x.go1
-rw-r--r--src/crypto/aes/gcm_s390x.go3
-rw-r--r--src/crypto/cipher/gcm.go9
-rw-r--r--src/crypto/crypto.go14
-rw-r--r--src/crypto/ecdsa/ecdsa_s390x.go1
-rw-r--r--src/crypto/ed25519/internal/edwards25519/doc.go2
-rw-r--r--src/crypto/ed25519/internal/edwards25519/field/fe_alias_test.go4
-rw-r--r--src/crypto/ed25519/internal/edwards25519/field/fe_amd64.go2
-rw-r--r--src/crypto/ed25519/internal/edwards25519/scalar.go22
-rw-r--r--src/crypto/elliptic/elliptic_test.go4
-rw-r--r--src/crypto/elliptic/fuzz_test.go53
-rw-r--r--src/crypto/elliptic/internal/fiat/p224_fiat64.go123
-rw-r--r--src/crypto/elliptic/internal/fiat/p384_fiat64.go123
-rw-r--r--src/crypto/elliptic/internal/fiat/p521_fiat64.go123
-rw-r--r--src/crypto/elliptic/p256.go50
-rw-r--r--src/crypto/elliptic/p256_asm.go12
-rw-r--r--src/crypto/rand/rand_batched_test.go50
-rw-r--r--src/crypto/rand/rand_dragonfly.go9
-rw-r--r--src/crypto/rand/rand_freebsd.go9
-rw-r--r--src/crypto/rand/rand_getentropy.go20
-rw-r--r--src/crypto/rand/rand_getrandom.go (renamed from src/crypto/rand/rand_batched.go)40
-rw-r--r--src/crypto/rand/rand_linux.go14
-rw-r--r--src/crypto/rand/rand_solaris.go10
-rw-r--r--src/crypto/rand/rand_unix.go57
-rw-r--r--src/crypto/rsa/pkcs1v15.go14
-rw-r--r--src/crypto/rsa/pkcs1v15_test.go3
-rw-r--r--src/crypto/subtle/constant_time.go3
-rw-r--r--src/crypto/tls/cipher_suites.go54
-rw-r--r--src/crypto/tls/conn.go6
-rw-r--r--src/crypto/tls/handshake_client_test.go2
-rw-r--r--src/crypto/tls/handshake_server_test.go2
-rw-r--r--src/crypto/x509/cert_pool.go8
-rw-r--r--src/crypto/x509/cert_pool_test.go124
-rw-r--r--src/crypto/x509/internal/macos/corefoundation.go1
-rw-r--r--src/crypto/x509/internal/macos/security.go1
-rw-r--r--src/crypto/x509/parser.go225
-rw-r--r--src/crypto/x509/pkcs8_test.go15
-rw-r--r--src/crypto/x509/pkix/pkix.go18
-rw-r--r--src/crypto/x509/root.go1
-rw-r--r--src/crypto/x509/sec1.go6
-rw-r--r--src/crypto/x509/verify.go208
-rw-r--r--src/crypto/x509/verify_test.go449
-rw-r--r--src/crypto/x509/x509.go183
-rw-r--r--src/crypto/x509/x509_test.go315
-rw-r--r--src/database/sql/driver/driver.go32
-rw-r--r--src/database/sql/driver/types.go30
-rw-r--r--src/database/sql/fakedb_test.go21
-rw-r--r--src/database/sql/sql.go82
-rw-r--r--src/debug/dwarf/entry.go35
-rw-r--r--src/debug/dwarf/line.go2
-rw-r--r--src/debug/dwarf/line_test.go29
-rw-r--r--src/debug/dwarf/type.go2
-rw-r--r--src/debug/pe/string.go25
-rw-r--r--src/embed/embed.go9
-rw-r--r--src/encoding/asn1/asn1.go2
-rw-r--r--src/encoding/base32/base32.go2
-rw-r--r--src/encoding/binary/varint.go32
-rw-r--r--src/encoding/binary/varint_test.go12
-rw-r--r--src/encoding/gob/debug.go28
-rw-r--r--src/encoding/gob/decoder.go2
-rw-r--r--src/encoding/gob/doc.go6
-rw-r--r--src/encoding/json/decode.go2
-rw-r--r--src/encoding/json/encode.go30
-rw-r--r--src/encoding/json/fold.go5
-rw-r--r--src/encoding/pem/pem.go182
-rw-r--r--src/encoding/pem/pem_test.go28
-rw-r--r--src/encoding/xml/marshal.go60
-rw-r--r--src/encoding/xml/read.go88
-rw-r--r--src/expvar/expvar.go2
-rw-r--r--src/flag/flag.go23
-rw-r--r--src/fmt/doc.go48
-rw-r--r--src/fmt/print.go2
-rw-r--r--src/go/ast/ast.go6
-rw-r--r--src/go/ast/ast_test.go3
-rw-r--r--src/go/build/build.go23
-rw-r--r--src/go/build/deps_test.go16
-rw-r--r--src/go/build/doc.go9
-rw-r--r--src/go/build/read.go2
-rw-r--r--src/go/constant/value.go33
-rw-r--r--src/go/doc/comment.go543
-rw-r--r--src/go/doc/comment/html.go169
-rw-r--r--src/go/doc/comment/markdown.go188
-rwxr-xr-xsrc/go/doc/comment/mkstd.sh24
-rw-r--r--src/go/doc/comment/old_test.go80
-rw-r--r--src/go/doc/comment/parse.go1151
-rw-r--r--src/go/doc/comment/parse_test.go12
-rw-r--r--src/go/doc/comment/print.go290
-rw-r--r--src/go/doc/comment/std.go44
-rw-r--r--src/go/doc/comment/std_test.go35
-rw-r--r--src/go/doc/comment/testdata/README.md42
-rw-r--r--src/go/doc/comment/testdata/blank.txt12
-rw-r--r--src/go/doc/comment/testdata/code.txt94
-rw-r--r--src/go/doc/comment/testdata/code2.txt31
-rw-r--r--src/go/doc/comment/testdata/code3.txt33
-rw-r--r--src/go/doc/comment/testdata/doclink.txt21
-rw-r--r--src/go/doc/comment/testdata/doclink2.txt8
-rw-r--r--src/go/doc/comment/testdata/doclink3.txt8
-rw-r--r--src/go/doc/comment/testdata/doclink4.txt7
-rw-r--r--src/go/doc/comment/testdata/doclink5.txt5
-rw-r--r--src/go/doc/comment/testdata/doclink6.txt5
-rw-r--r--src/go/doc/comment/testdata/doclink7.txt4
-rw-r--r--src/go/doc/comment/testdata/escape.txt55
-rw-r--r--src/go/doc/comment/testdata/head.txt92
-rw-r--r--src/go/doc/comment/testdata/head2.txt36
-rw-r--r--src/go/doc/comment/testdata/head3.txt7
-rw-r--r--src/go/doc/comment/testdata/hello.txt35
-rw-r--r--src/go/doc/comment/testdata/link.txt17
-rw-r--r--src/go/doc/comment/testdata/link2.txt31
-rw-r--r--src/go/doc/comment/testdata/link3.txt14
-rw-r--r--src/go/doc/comment/testdata/link4.txt77
-rw-r--r--src/go/doc/comment/testdata/link5.txt36
-rw-r--r--src/go/doc/comment/testdata/link6.txt50
-rw-r--r--src/go/doc/comment/testdata/link7.txt25
-rw-r--r--src/go/doc/comment/testdata/list.txt48
-rw-r--r--src/go/doc/comment/testdata/list2.txt57
-rw-r--r--src/go/doc/comment/testdata/list3.txt32
-rw-r--r--src/go/doc/comment/testdata/list4.txt38
-rw-r--r--src/go/doc/comment/testdata/list5.txt40
-rw-r--r--src/go/doc/comment/testdata/list6.txt129
-rw-r--r--src/go/doc/comment/testdata/list7.txt98
-rw-r--r--src/go/doc/comment/testdata/list8.txt56
-rw-r--r--src/go/doc/comment/testdata/para.txt17
-rw-r--r--src/go/doc/comment/testdata/quote.txt12
-rw-r--r--src/go/doc/comment/testdata/text.txt62
-rw-r--r--src/go/doc/comment/testdata/text2.txt14
-rw-r--r--src/go/doc/comment/testdata/text3.txt28
-rw-r--r--src/go/doc/comment/testdata/text4.txt29
-rw-r--r--src/go/doc/comment/testdata/text5.txt38
-rw-r--r--src/go/doc/comment/testdata/text6.txt18
-rw-r--r--src/go/doc/comment/testdata/text7.txt21
-rw-r--r--src/go/doc/comment/testdata/text8.txt94
-rw-r--r--src/go/doc/comment/testdata/text9.txt12
-rw-r--r--src/go/doc/comment/testdata/words.txt10
-rw-r--r--src/go/doc/comment/testdata_test.go202
-rw-r--r--src/go/doc/comment/text.go338
-rw-r--r--src/go/doc/comment/wrap_test.go141
-rw-r--r--src/go/doc/comment_test.go274
-rw-r--r--src/go/doc/doc.go132
-rw-r--r--src/go/doc/doc_test.go9
-rw-r--r--src/go/doc/example.go8
-rw-r--r--src/go/doc/reader.go95
-rw-r--r--src/go/doc/synopsis.go91
-rw-r--r--src/go/doc/synopsis_test.go14
-rw-r--r--src/go/doc/testdata/pkgdoc/doc.go19
-rw-r--r--src/go/doc/testdata/testing.go2
-rw-r--r--src/go/format/benchmark_test.go21
-rw-r--r--src/go/internal/gccgoimporter/parser.go26
-rw-r--r--src/go/parser/parser.go1
-rw-r--r--src/go/printer/comment.go154
-rw-r--r--src/go/printer/nodes.go56
-rw-r--r--src/go/printer/printer.go19
-rw-r--r--src/go/printer/printer_test.go4
-rw-r--r--src/go/printer/testdata/comments.golden17
-rw-r--r--src/go/printer/testdata/comments.input24
-rw-r--r--src/go/printer/testdata/comments.x1
-rw-r--r--src/go/printer/testdata/comments2.golden1
-rw-r--r--src/go/printer/testdata/doc.golden21
-rw-r--r--src/go/printer/testdata/doc.input20
-rw-r--r--src/go/scanner/scanner.go1
-rw-r--r--src/go/token/token.go1
-rw-r--r--src/go/types/api.go1
-rw-r--r--src/go/types/check_test.go2
-rw-r--r--src/go/types/gotype.go4
-rw-r--r--src/go/types/infer.go10
-rw-r--r--src/go/types/lookup.go16
-rw-r--r--src/go/types/selection.go7
-rw-r--r--src/go/types/sizes.go26
-rw-r--r--src/go/types/typeterm.go8
-rw-r--r--src/hash/adler32/adler32.go1
-rw-r--r--src/hash/crc32/crc32_amd64.go3
-rw-r--r--src/hash/crc32/crc32_ppc64le.go1
-rw-r--r--src/hash/crc32/crc32_s390x.go2
-rw-r--r--src/hash/maphash/maphash.go55
-rw-r--r--src/hash/maphash/maphash_test.go60
-rw-r--r--src/html/template/context.go4
-rw-r--r--src/html/template/doc.go132
-rw-r--r--src/html/template/error.go17
-rw-r--r--src/html/template/escape.go12
-rw-r--r--src/html/template/html.go10
-rw-r--r--src/html/template/template.go2
-rw-r--r--src/html/template/url.go18
-rw-r--r--src/image/draw/draw.go59
-rw-r--r--src/image/draw/draw_test.go64
-rw-r--r--src/image/image.go2
-rw-r--r--src/image/jpeg/writer.go28
-rw-r--r--src/image/png/reader.go4
-rw-r--r--src/image/ycbcr.go1
-rw-r--r--src/index/suffixarray/suffixarray.go1
-rw-r--r--src/internal/bytealg/indexbyte_ppc64x.s87
-rw-r--r--src/internal/fmtsort/sort.go24
-rw-r--r--src/internal/goarch/goarch.go1
-rw-r--r--src/internal/goos/goos.go1
-rw-r--r--src/internal/intern/intern.go1
-rw-r--r--src/internal/nettrace/nettrace.go3
-rw-r--r--src/internal/poll/fcntl_libc.go1
-rw-r--r--src/internal/poll/fd_opendir_darwin.go1
-rw-r--r--src/internal/poll/fd_poll_runtime.go1
-rw-r--r--src/internal/poll/fd_writev_darwin.go1
-rw-r--r--src/internal/poll/sendfile_solaris.go1
-rw-r--r--src/internal/profile/legacy_profile.go30
-rw-r--r--src/internal/reflectlite/export_test.go2
-rw-r--r--src/internal/reflectlite/type.go3
-rw-r--r--src/internal/reflectlite/value.go1
-rw-r--r--src/internal/syscall/unix/nonblocking_libc.go1
-rw-r--r--src/internal/syscall/windows/registry/key.go1
-rw-r--r--src/internal/testenv/testenv.go8
-rw-r--r--src/internal/txtar/archive.go8
-rw-r--r--src/io/ioutil/ioutil.go11
-rw-r--r--src/log/log.go11
-rw-r--r--src/log/syslog/doc.go2
-rwxr-xr-xsrc/make.bash8
-rw-r--r--src/make.bat6
-rwxr-xr-xsrc/make.rc6
-rw-r--r--src/math/abs.go1
-rw-r--r--src/math/acosh.go1
-rw-r--r--src/math/asin.go2
-rw-r--r--src/math/asinh.go1
-rw-r--r--src/math/atan.go5
-rw-r--r--src/math/atan2.go1
-rw-r--r--src/math/atanh.go1
-rw-r--r--src/math/big/arith_ppc64x.s154
-rw-r--r--src/math/big/example_rat_test.go9
-rw-r--r--src/math/big/float.go8
-rw-r--r--src/math/big/floatconv.go24
-rw-r--r--src/math/big/int.go52
-rw-r--r--src/math/big/intconv.go4
-rw-r--r--src/math/big/nat.go2
-rw-r--r--src/math/big/natconv.go23
-rw-r--r--src/math/big/rat.go6
-rw-r--r--src/math/big/ratconv.go16
-rw-r--r--src/math/big/sqrt.go4
-rw-r--r--src/math/bits.go4
-rw-r--r--src/math/cbrt.go1
-rw-r--r--src/math/cmplx/isnan.go2
-rw-r--r--src/math/cmplx/pow.go1
-rw-r--r--src/math/copysign.go10
-rw-r--r--src/math/dim.go3
-rw-r--r--src/math/erf.go2
-rw-r--r--src/math/erfinv.go2
-rw-r--r--src/math/exp.go2
-rw-r--r--src/math/expm1.go2
-rw-r--r--src/math/floor.go5
-rw-r--r--src/math/frexp.go1
-rw-r--r--src/math/gamma.go1
-rw-r--r--src/math/hypot.go1
-rw-r--r--src/math/j0.go2
-rw-r--r--src/math/j1.go2
-rw-r--r--src/math/jn.go2
-rw-r--r--src/math/ldexp.go1
-rw-r--r--src/math/lgamma.go1
-rw-r--r--src/math/log.go1
-rw-r--r--src/math/log1p.go1
-rw-r--r--src/math/logb.go2
-rw-r--r--src/math/mod.go1
-rw-r--r--src/math/modf.go1
-rw-r--r--src/math/nextafter.go2
-rw-r--r--src/math/pow.go1
-rw-r--r--src/math/pow10.go1
-rw-r--r--src/math/rand/exp.go2
-rw-r--r--src/math/rand/normal.go2
-rw-r--r--src/math/rand/rand.go4
-rw-r--r--src/math/remainder.go1
-rw-r--r--src/math/sin.go2
-rw-r--r--src/math/sincos.go1
-rw-r--r--src/math/sinh.go2
-rw-r--r--src/math/sqrt.go1
-rw-r--r--src/math/tan.go1
-rw-r--r--src/math/tanh.go1
-rw-r--r--src/math/trig_reduce.go2
-rw-r--r--src/mime/multipart/multipart.go3
-rw-r--r--src/mime/testdata/test.types.globs22
-rw-r--r--src/mime/type.go10
-rw-r--r--src/mime/type_unix.go6
-rw-r--r--src/mime/type_unix_test.go2
-rw-r--r--src/net/cgo_unix.go12
-rw-r--r--src/net/conf.go16
-rw-r--r--src/net/dial.go3
-rw-r--r--src/net/fcntl_libc_test.go1
-rw-r--r--src/net/http/cgi/host.go9
-rw-r--r--src/net/http/client.go46
-rw-r--r--src/net/http/cookie.go12
-rw-r--r--src/net/http/cookiejar/jar.go6
-rw-r--r--src/net/http/cookiejar/jar_test.go19
-rw-r--r--src/net/http/doc.go1
-rw-r--r--src/net/http/fcgi/fcgi_test.go8
-rw-r--r--src/net/http/filetransport.go10
-rw-r--r--src/net/http/fs.go2
-rw-r--r--src/net/http/h2_bundle.go18
-rw-r--r--src/net/http/httptest/recorder.go24
-rw-r--r--src/net/http/httptest/recorder_test.go3
-rw-r--r--src/net/http/httptest/server.go2
-rw-r--r--src/net/http/internal/chunked.go9
-rw-r--r--src/net/http/pprof/pprof.go8
-rw-r--r--src/net/http/request.go10
-rw-r--r--src/net/http/request_test.go14
-rw-r--r--src/net/http/response.go26
-rw-r--r--src/net/http/serve_test.go28
-rw-r--r--src/net/http/server.go15
-rw-r--r--src/net/http/status.go198
-rw-r--r--src/net/http/transfer.go9
-rw-r--r--src/net/http/transfer_test.go2
-rw-r--r--src/net/http/transport_test.go15
-rw-r--r--src/net/ipsock_posix.go38
-rw-r--r--src/net/mac.go1
-rw-r--r--src/net/mail/message.go12
-rw-r--r--src/net/net.go3
-rw-r--r--src/net/netip/slow_test.go36
-rw-r--r--src/net/rpc/server.go19
-rw-r--r--src/net/smtp/smtp.go4
-rw-r--r--src/net/sock_linux.go4
-rw-r--r--src/net/textproto/reader.go26
-rw-r--r--src/net/textproto/reader_test.go29
-rw-r--r--src/net/textproto/textproto.go1
-rw-r--r--src/net/url/url.go42
-rw-r--r--src/net/url/url_test.go25
-rw-r--r--src/os/file.go1
-rw-r--r--src/os/file_plan9.go2
-rw-r--r--src/os/file_unix.go2
-rw-r--r--src/os/file_windows.go9
-rw-r--r--src/os/pipe_test.go1
-rw-r--r--src/os/rawconn_test.go1
-rw-r--r--src/os/signal/doc.go17
-rw-r--r--src/os/signal/signal_test.go14
-rw-r--r--src/os/stat_solaris.go10
-rw-r--r--src/path/filepath/path.go18
-rw-r--r--src/path/filepath/path_windows_test.go16
-rw-r--r--src/path/filepath/symlink_windows.go11
-rw-r--r--src/path/path.go16
-rw-r--r--src/plugin/plugin_dlopen.go1
-rw-r--r--src/reflect/abi_test.go1
-rw-r--r--src/reflect/all_test.go14
-rw-r--r--src/reflect/deepequal.go2
-rw-r--r--src/reflect/makefunc.go7
-rw-r--r--src/reflect/type.go1
-rw-r--r--src/reflect/value.go37
-rw-r--r--src/reflect/visiblefields_test.go2
-rw-r--r--src/regexp/exec_test.go4
-rw-r--r--src/regexp/regexp.go18
-rw-r--r--src/regexp/syntax/doc.go185
-rw-r--r--src/regexp/syntax/parse.go11
-rw-r--r--src/regexp/syntax/prog.go2
-rwxr-xr-xsrc/run.bash7
-rw-r--r--src/run.bat27
-rwxr-xr-xsrc/run.rc6
-rw-r--r--src/runtime/asan.go1
-rw-r--r--src/runtime/callers_test.go30
-rw-r--r--src/runtime/cgo/callbacks.go1
-rw-r--r--src/runtime/cgo/callbacks_aix.go1
-rw-r--r--src/runtime/cgo/openbsd.go1
-rw-r--r--src/runtime/cgo_mmap.go3
-rw-r--r--src/runtime/cgo_ppc64x.go1
-rw-r--r--src/runtime/cgo_sigaction.go2
-rw-r--r--src/runtime/cgocall.go4
-rw-r--r--src/runtime/cgocheck.go5
-rw-r--r--src/runtime/chan.go43
-rw-r--r--src/runtime/chan_test.go15
-rw-r--r--src/runtime/cpuprof.go2
-rw-r--r--src/runtime/crash_test.go44
-rw-r--r--src/runtime/debug.go5
-rw-r--r--src/runtime/debug/garbage.go4
-rw-r--r--src/runtime/debug/heapdump_test.go26
-rw-r--r--src/runtime/env_plan9.go4
-rw-r--r--src/runtime/env_posix.go2
-rw-r--r--src/runtime/error.go3
-rw-r--r--src/runtime/export_test.go1
-rw-r--r--src/runtime/extern.go22
-rw-r--r--src/runtime/float.go3
-rw-r--r--src/runtime/histogram.go1
-rw-r--r--src/runtime/internal/atomic/atomic_386.go1
-rw-r--r--src/runtime/internal/atomic/atomic_amd64.go1
-rw-r--r--src/runtime/internal/atomic/atomic_arm.go2
-rw-r--r--src/runtime/internal/atomic/atomic_mipsx.go1
-rw-r--r--src/runtime/internal/atomic/atomic_s390x.go1
-rw-r--r--src/runtime/internal/atomic/atomic_wasm.go1
-rw-r--r--src/runtime/lock_futex.go1
-rw-r--r--src/runtime/lock_sema.go3
-rw-r--r--src/runtime/lockrank_off.go2
-rw-r--r--src/runtime/lockrank_on.go11
-rw-r--r--src/runtime/malloc.go2
-rw-r--r--src/runtime/map_test.go5
-rw-r--r--src/runtime/mbarrier.go1
-rw-r--r--src/runtime/mbitmap.go15
-rw-r--r--src/runtime/mem_aix.go2
-rw-r--r--src/runtime/mem_bsd.go2
-rw-r--r--src/runtime/mem_darwin.go2
-rw-r--r--src/runtime/mem_js.go2
-rw-r--r--src/runtime/mem_linux.go2
-rw-r--r--src/runtime/mem_windows.go2
-rw-r--r--src/runtime/memclr_ppc64x.s83
-rw-r--r--src/runtime/memclr_riscv64.s30
-rw-r--r--src/runtime/memmove_amd64.s4
-rw-r--r--src/runtime/memmove_ppc64x.s56
-rw-r--r--src/runtime/memmove_riscv64.s54
-rw-r--r--src/runtime/metrics/doc.go8
-rw-r--r--src/runtime/mfinal.go22
-rw-r--r--src/runtime/mgc.go2
-rw-r--r--src/runtime/mgcmark.go3
-rw-r--r--src/runtime/mgcstack.go1
-rw-r--r--src/runtime/mgcsweep.go3
-rw-r--r--src/runtime/mgcwork.go16
-rw-r--r--src/runtime/mheap.go17
-rw-r--r--src/runtime/mpagealloc_64bit.go3
-rw-r--r--src/runtime/msan.go1
-rw-r--r--src/runtime/mstats.go3
-rw-r--r--src/runtime/mwbbuf.go10
-rw-r--r--src/runtime/netpoll.go24
-rw-r--r--src/runtime/netpoll_aix.go1
-rw-r--r--src/runtime/norace_linux_test.go1
-rw-r--r--src/runtime/norace_test.go1
-rw-r--r--src/runtime/os2_aix.go1
-rw-r--r--src/runtime/os3_solaris.go2
-rw-r--r--src/runtime/os_aix.go2
-rw-r--r--src/runtime/os_darwin.go4
-rw-r--r--src/runtime/os_dragonfly.go3
-rw-r--r--src/runtime/os_freebsd.go7
-rw-r--r--src/runtime/os_js.go1
-rw-r--r--src/runtime/os_linux.go11
-rw-r--r--src/runtime/os_netbsd.go4
-rw-r--r--src/runtime/os_openbsd.go2
-rw-r--r--src/runtime/os_openbsd_libc.go1
-rw-r--r--src/runtime/os_openbsd_syscall.go1
-rw-r--r--src/runtime/os_openbsd_syscall2.go1
-rw-r--r--src/runtime/os_plan9.go2
-rw-r--r--src/runtime/os_windows.go5
-rw-r--r--src/runtime/panic.go12
-rw-r--r--src/runtime/pprof/pprof.go64
-rw-r--r--src/runtime/pprof/pprof_test.go1
-rw-r--r--src/runtime/pprof/proto.go14
-rw-r--r--src/runtime/proc.go46
-rw-r--r--src/runtime/proc_test.go1
-rw-r--r--src/runtime/race.go4
-rw-r--r--src/runtime/runtime1.go8
-rw-r--r--src/runtime/runtime2.go3
-rw-r--r--src/runtime/runtime_test.go1
-rw-r--r--src/runtime/sema.go4
-rw-r--r--src/runtime/signal_unix.go20
-rw-r--r--src/runtime/sigqueue.go7
-rw-r--r--src/runtime/sigqueue_plan9.go5
-rw-r--r--src/runtime/stack.go4
-rw-r--r--src/runtime/string.go9
-rw-r--r--src/runtime/stubs.go5
-rw-r--r--src/runtime/stubs2.go1
-rw-r--r--src/runtime/stubs_linux.go1
-rw-r--r--src/runtime/stubs_ppc64.go1
-rw-r--r--src/runtime/symtab.go3
-rw-r--r--src/runtime/symtab_test.go17
-rw-r--r--src/runtime/sys_darwin.go5
-rw-r--r--src/runtime/sys_libc.go1
-rw-r--r--src/runtime/sys_openbsd2.go2
-rw-r--r--src/runtime/syscall_aix.go2
-rw-r--r--src/runtime/syscall_solaris.go2
-rw-r--r--src/runtime/syscall_windows.go1
-rw-r--r--src/runtime/syscall_windows_test.go1
-rw-r--r--src/runtime/testdata/testprog/crash.go73
-rw-r--r--src/runtime/testdata/testprogcgo/dropm_stub.go1
-rw-r--r--src/runtime/testdata/testprogcgo/eintr.go1
-rw-r--r--src/runtime/time.go11
-rw-r--r--src/runtime/time_fake.go1
-rw-r--r--src/runtime/trace/annotation.go16
-rw-r--r--src/runtime/trace/trace.go65
-rw-r--r--src/runtime/type.go10
-rw-r--r--src/runtime/vdso_linux.go1
-rw-r--r--src/runtime/vlrt.go11
-rw-r--r--src/sort/example_multi_test.go2
-rw-r--r--src/sort/export_test.go4
-rw-r--r--src/sort/gen_sort_variants.go391
-rw-r--r--src/sort/search.go41
-rw-r--r--src/sort/search_test.go106
-rw-r--r--src/sort/slice.go5
-rw-r--r--src/sort/sort.go37
-rw-r--r--src/sort/sort_test.go70
-rw-r--r--src/sort/zsortfunc.go371
-rw-r--r--src/sort/zsortinterface.go371
-rw-r--r--src/strconv/atof.go2
-rw-r--r--src/strconv/doc.go5
-rw-r--r--src/strconv/eisel_lemire.go4
-rw-r--r--src/strconv/ftoaryu.go10
-rw-r--r--src/strconv/quote.go8
-rw-r--r--src/strings/builder.go1
-rw-r--r--src/strings/replace.go16
-rw-r--r--src/strings/strings.go16
-rw-r--r--src/sync/atomic/atomic_test.go3
-rw-r--r--src/sync/cond.go12
-rw-r--r--src/sync/once.go7
-rw-r--r--src/sync/pool_test.go1
-rw-r--r--src/syscall/dir_plan9.go1
-rw-r--r--src/syscall/exec_bsd.go1
-rw-r--r--src/syscall/exec_freebsd.go1
-rw-r--r--src/syscall/exec_libc.go1
-rw-r--r--src/syscall/exec_libc2.go1
-rw-r--r--src/syscall/exec_linux.go1
-rw-r--r--src/syscall/exec_plan9.go4
-rw-r--r--src/syscall/exec_windows.go10
-rw-r--r--src/syscall/js/js.go20
-rw-r--r--src/syscall/syscall.go1
-rw-r--r--src/syscall/syscall_js.go1
-rw-r--r--src/syscall/syscall_linux.go6
-rw-r--r--src/syscall/syscall_unix.go1
-rw-r--r--src/testing/fuzz.go2
-rw-r--r--src/testing/quick/quick.go18
-rw-r--r--src/testing/testing.go272
-rw-r--r--src/text/template/funcs.go2
-rw-r--r--src/text/template/helper.go1
-rw-r--r--src/text/template/option.go1
-rw-r--r--src/text/template/parse/lex.go24
-rw-r--r--src/text/template/parse/lex_test.go16
-rw-r--r--src/text/template/parse/parse.go30
-rw-r--r--src/time/format.go8
-rw-r--r--src/time/sleep.go14
-rw-r--r--src/time/tick.go2
-rw-r--r--src/time/time.go9
-rw-r--r--src/time/tzdata/tzdata.go1
-rw-r--r--src/time/zoneinfo.go8
-rw-r--r--src/unicode/graphic.go2
-rw-r--r--src/unicode/letter.go3
-rw-r--r--src/unsafe/unsafe.go9
-rw-r--r--test/codegen/bmi.go46
-rw-r--r--test/codegen/memcombine.go102
-rw-r--r--test/codegen/switch.go50
-rw-r--r--test/fixedbugs/issue52127.go62
-rw-r--r--test/fixedbugs/issue52278.go12
-rw-r--r--test/inline.go45
-rw-r--r--test/nosplit.go45
-rw-r--r--test/run.go51
-rw-r--r--test/shift3.go41
-rw-r--r--test/typeparam/issue51700.go26
-rw-r--r--test/typeparam/issue52124.go6
-rw-r--r--test/typeparam/issue52228.go30
801 files changed, 20623 insertions, 7086 deletions
diff --git a/AUTHORS b/AUTHORS
index 96704bd564f..e2c8150ab08 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -1435,6 +1435,7 @@ Wei Guangjing <vcc.163@gmail.com>
Weichao Tang <tevic.tt@gmail.com>
Weixie Cui <cuiweixie@gmail.com> <523516579@qq.com>
Wembley G. Leach, Jr <wembley.gl@gmail.com>
+Wen Yang <yangwen.yw@gmail.com>
Will Faught <will.faught@gmail.com>
Will Storey <will@summercat.com>
Willem van der Schyff <willemvds@gmail.com>
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index f79c4132b89..ea8b1b964e1 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -2746,6 +2746,7 @@ Weichao Tang <tevic.tt@gmail.com>
Weilu Jia <optix2000@gmail.com>
Weixie Cui <cuiweixie@gmail.com> <523516579@qq.com>
Wembley G. Leach, Jr <wembley.gl@gmail.com>
+Wen Yang <yangwen.yw@gmail.com>
Wenlei (Frank) He <wlhe@google.com>
Wenzel Lowe <lowewenzel@gmail.com>
Wil Selwood <wselwood@gmail.com>
diff --git a/api/next/35044.txt b/api/next/35044.txt
new file mode 100644
index 00000000000..0ed6f2e4d04
--- /dev/null
+++ b/api/next/35044.txt
@@ -0,0 +1 @@
+pkg crypto/x509, method (*CertPool) Clone() *CertPool #35044 \ No newline at end of file
diff --git a/api/next/42710.txt b/api/next/42710.txt
new file mode 100644
index 00000000000..7879758d16c
--- /dev/null
+++ b/api/next/42710.txt
@@ -0,0 +1,2 @@
+pkg hash/maphash, func Bytes(Seed, []uint8) uint64 #42710
+pkg hash/maphash, func String(Seed, string) uint64 #42710
diff --git a/api/next/50340.txt b/api/next/50340.txt
new file mode 100644
index 00000000000..211392cd252
--- /dev/null
+++ b/api/next/50340.txt
@@ -0,0 +1 @@
+pkg sort, func Find(int, func(int) int) (int, bool) #50340
diff --git a/api/next/50674.txt b/api/next/50674.txt
new file mode 100644
index 00000000000..6b5bca3a9db
--- /dev/null
+++ b/api/next/50674.txt
@@ -0,0 +1,9 @@
+pkg crypto/x509, func ParseRevocationList([]uint8) (*RevocationList, error) #50674
+pkg crypto/x509, method (*RevocationList) CheckSignatureFrom(*Certificate) error #50674
+pkg crypto/x509, type RevocationList struct, AuthorityKeyId []uint8 #50674
+pkg crypto/x509, type RevocationList struct, Extensions []pkix.Extension #50674
+pkg crypto/x509, type RevocationList struct, Issuer pkix.Name #50674
+pkg crypto/x509, type RevocationList struct, Raw []uint8 #50674
+pkg crypto/x509, type RevocationList struct, RawIssuer []uint8 #50674
+pkg crypto/x509, type RevocationList struct, RawTBSRevocationList []uint8 #50674
+pkg crypto/x509, type RevocationList struct, Signature []uint8 #50674
diff --git a/api/next/51082.txt b/api/next/51082.txt
new file mode 100644
index 00000000000..b05997f985c
--- /dev/null
+++ b/api/next/51082.txt
@@ -0,0 +1,61 @@
+pkg go/doc, method (*Package) HTML(string) []uint8 #51082
+pkg go/doc, method (*Package) Markdown(string) []uint8 #51082
+pkg go/doc, method (*Package) Parser() *comment.Parser #51082
+pkg go/doc, method (*Package) Printer() *comment.Printer #51082
+pkg go/doc, method (*Package) Synopsis(string) string #51082
+pkg go/doc, method (*Package) Text(string) []uint8 #51082
+pkg go/doc/comment, func DefaultLookupPackage(string) (string, bool) #51082
+pkg go/doc/comment, method (*DocLink) DefaultURL(string) string #51082
+pkg go/doc/comment, method (*Heading) DefaultID() string #51082
+pkg go/doc/comment, method (*List) BlankBefore() bool #51082
+pkg go/doc/comment, method (*List) BlankBetween() bool #51082
+pkg go/doc/comment, method (*Parser) Parse(string) *Doc #51082
+pkg go/doc/comment, method (*Printer) Comment(*Doc) []uint8 #51082
+pkg go/doc/comment, method (*Printer) HTML(*Doc) []uint8 #51082
+pkg go/doc/comment, method (*Printer) Markdown(*Doc) []uint8 #51082
+pkg go/doc/comment, method (*Printer) Text(*Doc) []uint8 #51082
+pkg go/doc/comment, type Block interface, unexported methods #51082
+pkg go/doc/comment, type Code struct #51082
+pkg go/doc/comment, type Code struct, Text string #51082
+pkg go/doc/comment, type Doc struct #51082
+pkg go/doc/comment, type Doc struct, Content []Block #51082
+pkg go/doc/comment, type Doc struct, Links []*LinkDef #51082
+pkg go/doc/comment, type DocLink struct #51082
+pkg go/doc/comment, type DocLink struct, ImportPath string #51082
+pkg go/doc/comment, type DocLink struct, Name string #51082
+pkg go/doc/comment, type DocLink struct, Recv string #51082
+pkg go/doc/comment, type DocLink struct, Text []Text #51082
+pkg go/doc/comment, type Heading struct #51082
+pkg go/doc/comment, type Heading struct, Text []Text #51082
+pkg go/doc/comment, type Italic string #51082
+pkg go/doc/comment, type Link struct #51082
+pkg go/doc/comment, type Link struct, Auto bool #51082
+pkg go/doc/comment, type Link struct, Text []Text #51082
+pkg go/doc/comment, type Link struct, URL string #51082
+pkg go/doc/comment, type LinkDef struct #51082
+pkg go/doc/comment, type LinkDef struct, Text string #51082
+pkg go/doc/comment, type LinkDef struct, URL string #51082
+pkg go/doc/comment, type LinkDef struct, Used bool #51082
+pkg go/doc/comment, type List struct #51082
+pkg go/doc/comment, type List struct, ForceBlankBefore bool #51082
+pkg go/doc/comment, type List struct, ForceBlankBetween bool #51082
+pkg go/doc/comment, type List struct, Items []*ListItem #51082
+pkg go/doc/comment, type ListItem struct #51082
+pkg go/doc/comment, type ListItem struct, Content []Block #51082
+pkg go/doc/comment, type ListItem struct, Number string #51082
+pkg go/doc/comment, type Paragraph struct #51082
+pkg go/doc/comment, type Paragraph struct, Text []Text #51082
+pkg go/doc/comment, type Parser struct #51082
+pkg go/doc/comment, type Parser struct, LookupPackage func(string) (string, bool) #51082
+pkg go/doc/comment, type Parser struct, LookupSym func(string, string) bool #51082
+pkg go/doc/comment, type Parser struct, Words map[string]string #51082
+pkg go/doc/comment, type Plain string #51082
+pkg go/doc/comment, type Printer struct #51082
+pkg go/doc/comment, type Printer struct, DocLinkBaseURL string #51082
+pkg go/doc/comment, type Printer struct, DocLinkURL func(*DocLink) string #51082
+pkg go/doc/comment, type Printer struct, HeadingID func(*Heading) string #51082
+pkg go/doc/comment, type Printer struct, HeadingLevel int #51082
+pkg go/doc/comment, type Printer struct, TextCodePrefix string #51082
+pkg go/doc/comment, type Printer struct, TextPrefix string #51082
+pkg go/doc/comment, type Printer struct, TextWidth int #51082
+pkg go/doc/comment, type Text interface, unexported methods #51082
diff --git a/api/next/51644.txt b/api/next/51644.txt
new file mode 100644
index 00000000000..d93dbbf1846
--- /dev/null
+++ b/api/next/51644.txt
@@ -0,0 +1,2 @@
+pkg encoding/binary, func AppendUvarint([]uint8, uint64) []uint8 #51644
+pkg encoding/binary, func AppendVarint([]uint8, int64) []uint8 #51644
diff --git a/doc/go1.19.html b/doc/go1.19.html
index 857d8ed8cee..a813d59cb82 100644
--- a/doc/go1.19.html
+++ b/doc/go1.19.html
@@ -32,7 +32,22 @@ Do not send CLs removing the interior tags from such phrases.
</p>
<h3 id="go-command">Go command</h3>
<p>
- TODO: complete this section, or delete if not needed
+ TODO: complete this section.
+</p>
+
+<!-- https://go.dev/issue/51461 -->
+<p>
+ The <code>-trimpath</code> flag, if set, is now included in the build settings
+ stamped into Go binaries by <code>go</code> <code>build</code>, and can be
+ examined using
+ <a href="https://pkg.go.dev/cmd/go#hdr-Print_Go_version"><code>go</code> <code>version</code> <code>-m</code></a>
+ or <a href="https://pkg.go.dev/runtime/debug#ReadBuildInfo"><code>debug.ReadBuildInfo</code></a>.
+</p>
+<p>
+ <code>go</code> <code>generate</code> now sets the <code>GOROOT</code>
+ environment variable explicitly in the generator's environment, so that
+ generators can locate the correct <code>GOROOT</code> even if built
+ with <code>-trimpath</code>.
</p>
<h4 id="go-unix">New <code>unix</code> build constraint</h4>
@@ -76,6 +91,19 @@ Do not send CLs removing the interior tags from such phrases.
<p>
TODO: complete this section
</p>
+
+<dl id="image/draw"><dt><a href="/pkg/image/draw/">image/draw</a></dt>
+ <dd>
+ <p><!-- CL 396795 -->
+ <code>Draw</code> with the <code>Src</code> operator preserves
+ non-premultiplied-alpha colors when destination and source images are
+ both <code>*image.NRGBA</code> (or both <code>*image.NRGBA64</code>).
+ This reverts a behavior change accidentally introduced by a Go 1.18
+ library optimization, to match the behavior in Go 1.17 and earlier.
+ </p>
+ </dd>
+</dl><!-- image/draw -->
+
<dl id="net"><dt><a href="/pkg/net/">net</a></dt>
<dd>
<p><!-- CL 386016 -->
@@ -93,9 +121,9 @@ Do not send CLs removing the interior tags from such phrases.
<p><!-- CL 396877 -->
When a net package function or method returns an "I/O timeout"
error, the error will now satisfy <code>errors.Is(err,
- context.Canceled)</code>. When a net package function returns
- an "operation was canceled" error, the error will now satisfy
- <code>errors.Is(err, context.DeadlineExceeded)</code>.
+ context.DeadlineExceeded)</code>. When a net package function
+ returns an "operation was canceled" error, the error will now
+ satisfy <code>errors.Is(err, context.Canceled)</code>.
These changes are intended to make it easier for code to test
for cases in which a context cancelation or timeout causes a net
package function or method to return an error, while preserving
@@ -104,6 +132,17 @@ Do not send CLs removing the interior tags from such phrases.
</dd>
</dl><!-- net -->
+<dl id="runtime"><dt><a href="/pkg/runtime/">runtime</a></dt>
+ <dd>
+ <p><!-- https://go.dev/issue/51461 -->
+ The <code>GOROOT</code> function now returns the empty string
+ (instead of <code>"go"</code>) when the binary was built with
+ the <code>-trimpath</code> flag set and the <code>GOROOT</code>
+ variable is not set in the process environment.
+ </p>
+ </dd>
+</dl><!-- runtime -->
+
<dl id="strconv"><dt><a href="/pkg/strconv/">strconv</a></dt>
<dd>
<p><!-- CL 397255 -->
diff --git a/misc/cgo/gmp/gmp.go b/misc/cgo/gmp/gmp.go
index 971a10aaac6..0835fdc8dea 100644
--- a/misc/cgo/gmp/gmp.go
+++ b/misc/cgo/gmp/gmp.go
@@ -333,10 +333,9 @@ func (z *Int) Abs(x *Int) *Int {
// CmpInt compares x and y. The result is
//
-// -1 if x < y
-// 0 if x == y
-// +1 if x > y
-//
+// -1 if x < y
+// 0 if x == y
+// +1 if x > y
func CmpInt(x, y *Int) int {
x.doinit()
y.doinit()
diff --git a/misc/cgo/testcshared/cshared_test.go b/misc/cgo/testcshared/cshared_test.go
index c9e9e5fe63e..e4898778be8 100644
--- a/misc/cgo/testcshared/cshared_test.go
+++ b/misc/cgo/testcshared/cshared_test.go
@@ -5,6 +5,7 @@
package cshared_test
import (
+ "bufio"
"bytes"
"debug/elf"
"debug/pe"
@@ -838,3 +839,51 @@ func TestGo2C2Go(t *testing.T) {
run(t, goenv, "go", "build", "-o", bin, "./go2c2go/m2")
runExe(t, runenv, bin)
}
+
+func TestIssue36233(t *testing.T) {
+ t.Parallel()
+
+ // Test that the export header uses GoComplex64 and GoComplex128
+ // for complex types.
+
+ tmpdir, err := os.MkdirTemp("", "cshared-TestIssue36233")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer os.RemoveAll(tmpdir)
+
+ const exportHeader = "issue36233.h"
+
+ run(t, nil, "go", "tool", "cgo", "-exportheader", exportHeader, "-objdir", tmpdir, "./issue36233/issue36233.go")
+ data, err := os.ReadFile(exportHeader)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ funcs := []struct{ name, signature string }{
+ {"exportComplex64", "GoComplex64 exportComplex64(GoComplex64 v)"},
+ {"exportComplex128", "GoComplex128 exportComplex128(GoComplex128 v)"},
+ {"exportComplexfloat", "GoComplex64 exportComplexfloat(GoComplex64 v)"},
+ {"exportComplexdouble", "GoComplex128 exportComplexdouble(GoComplex128 v)"},
+ }
+
+ scanner := bufio.NewScanner(bytes.NewReader(data))
+ var found int
+ for scanner.Scan() {
+ b := scanner.Bytes()
+ for _, fn := range funcs {
+ if bytes.Contains(b, []byte(fn.name)) {
+ found++
+ if !bytes.Contains(b, []byte(fn.signature)) {
+ t.Errorf("function signature mismatch; got %q, want %q", b, fn.signature)
+ }
+ }
+ }
+ }
+ if err = scanner.Err(); err != nil {
+ t.Errorf("scanner encountered error: %v", err)
+ }
+ if found != len(funcs) {
+ t.Error("missing functions")
+ }
+}
diff --git a/misc/cgo/testcshared/testdata/issue36233/issue36233.go b/misc/cgo/testcshared/testdata/issue36233/issue36233.go
new file mode 100644
index 00000000000..d0d1e5d50ae
--- /dev/null
+++ b/misc/cgo/testcshared/testdata/issue36233/issue36233.go
@@ -0,0 +1,29 @@
+// Copyright 2022 The Go Authors. 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
+
+// #include <complex.h>
+import "C"
+
+//export exportComplex64
+func exportComplex64(v complex64) complex64 {
+ return v
+}
+
+//export exportComplex128
+func exportComplex128(v complex128) complex128 {
+ return v
+}
+
+//export exportComplexfloat
+func exportComplexfloat(v C.complexfloat) C.complexfloat {
+ return v
+}
+
+//export exportComplexdouble
+func exportComplexdouble(v C.complexdouble) C.complexdouble {
+ return v
+}
+
+func main() {}
diff --git a/misc/ios/go_ios_exec.go b/misc/ios/go_ios_exec.go
index 34a734cda78..c275dd339cc 100644
--- a/misc/ios/go_ios_exec.go
+++ b/misc/ios/go_ios_exec.go
@@ -13,9 +13,11 @@
// binary.
//
// This script requires that three environment variables be set:
-// GOIOS_DEV_ID: The codesigning developer id or certificate identifier
-// GOIOS_APP_ID: The provisioning app id prefix. Must support wildcard app ids.
-// GOIOS_TEAM_ID: The team id that owns the app id prefix.
+//
+// GOIOS_DEV_ID: The codesigning developer id or certificate identifier
+// GOIOS_APP_ID: The provisioning app id prefix. Must support wildcard app ids.
+// GOIOS_TEAM_ID: The team id that owns the app id prefix.
+//
// $GOROOT/misc/ios contains a script, detect.go, that attempts to autodetect these.
package main
diff --git a/src/archive/tar/common.go b/src/archive/tar/common.go
index c99b5c19207..f6d701d925c 100644
--- a/src/archive/tar/common.go
+++ b/src/archive/tar/common.go
@@ -221,9 +221,11 @@ func (s sparseEntry) endOffset() int64 { return s.Offset + s.Length }
// that the file has no data in it, which is rather odd.
//
// As an example, if the underlying raw file contains the 10-byte data:
+//
// var compactFile = "abcdefgh"
//
// And the sparse map has the following entries:
+//
// var spd sparseDatas = []sparseEntry{
// {Offset: 2, Length: 5}, // Data fragment for 2..6
// {Offset: 18, Length: 3}, // Data fragment for 18..20
@@ -235,6 +237,7 @@ func (s sparseEntry) endOffset() int64 { return s.Offset + s.Length }
// }
//
// Then the content of the resulting sparse file with a Header.Size of 25 is:
+//
// var sparseFile = "\x00"*2 + "abcde" + "\x00"*11 + "fgh" + "\x00"*4
type (
sparseDatas []sparseEntry
@@ -293,9 +296,9 @@ func alignSparseEntries(src []sparseEntry, size int64) []sparseEntry {
// The input must have been already validated.
//
// This function mutates src and returns a normalized map where:
-// * adjacent fragments are coalesced together
-// * only the last fragment may be empty
-// * the endOffset of the last fragment is the total size
+// - adjacent fragments are coalesced together
+// - only the last fragment may be empty
+// - the endOffset of the last fragment is the total size
func invertSparseEntries(src []sparseEntry, size int64) []sparseEntry {
dst := src[:0]
var pre sparseEntry
diff --git a/src/archive/tar/reader.go b/src/archive/tar/reader.go
index 4b11909bc95..f1b35c34f6f 100644
--- a/src/archive/tar/reader.go
+++ b/src/archive/tar/reader.go
@@ -336,9 +336,9 @@ func parsePAX(r io.Reader) (map[string]string, error) {
// header in case further processing is required.
//
// The err will be set to io.EOF only when one of the following occurs:
-// * Exactly 0 bytes are read and EOF is hit.
-// * Exactly 1 block of zeros is read and EOF is hit.
-// * At least 2 blocks of zeros are read.
+// - Exactly 0 bytes are read and EOF is hit.
+// - Exactly 1 block of zeros is read and EOF is hit.
+// - At least 2 blocks of zeros are read.
func (tr *Reader) readHeader() (*Header, *block, error) {
// Two blocks of zero bytes marks the end of the archive.
if _, err := io.ReadFull(tr.r, tr.blk[:]); err != nil {
diff --git a/src/archive/tar/strconv.go b/src/archive/tar/strconv.go
index 275db6f0263..ac3196370e6 100644
--- a/src/archive/tar/strconv.go
+++ b/src/archive/tar/strconv.go
@@ -306,6 +306,7 @@ func formatPAXRecord(k, v string) (string, error) {
// validPAXRecord reports whether the key-value pair is valid where each
// record is formatted as:
+//
// "%d %s=%s\n" % (size, key, value)
//
// Keys and values should be UTF-8, but the number of bad writers out there
diff --git a/src/archive/zip/reader.go b/src/archive/zip/reader.go
index 92fd6f6a925..b4f6a8d7149 100644
--- a/src/archive/zip/reader.go
+++ b/src/archive/zip/reader.go
@@ -229,6 +229,9 @@ func (r *checksumReader) Read(b []byte) (n int, err error) {
n, err = r.rc.Read(b)
r.hash.Write(b[:n])
r.nread += uint64(n)
+ if r.nread > r.f.UncompressedSize64 {
+ return 0, ErrFormat
+ }
if err == nil {
return
}
diff --git a/src/archive/zip/reader_test.go b/src/archive/zip/reader_test.go
index 9bc23642c0e..fd0a171304c 100644
--- a/src/archive/zip/reader_test.go
+++ b/src/archive/zip/reader_test.go
@@ -1407,3 +1407,30 @@ func TestCVE202141772(t *testing.T) {
t.Errorf("Inconsistent name in info entry: %v", name)
}
}
+
+func TestUnderSize(t *testing.T) {
+ z, err := OpenReader("testdata/readme.zip")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer z.Close()
+
+ for _, f := range z.File {
+ f.UncompressedSize64 = 1
+ }
+
+ for _, f := range z.File {
+ t.Run(f.Name, func(t *testing.T) {
+ rd, err := f.Open()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer rd.Close()
+
+ _, err = io.Copy(io.Discard, rd)
+ if err != ErrFormat {
+ t.Fatalf("Error mismatch\n\tGot: %v\n\tWant: %v", err, ErrFormat)
+ }
+ })
+ }
+}
diff --git a/src/builtin/builtin.go b/src/builtin/builtin.go
index 8997902f8f9..e3e4df9de68 100644
--- a/src/builtin/builtin.go
+++ b/src/builtin/builtin.go
@@ -137,9 +137,12 @@ type ComplexType complex64
// new elements. If it does not, a new underlying array will be allocated.
// Append returns the updated slice. It is therefore necessary to store the
// result of append, often in the variable holding the slice itself:
+//
// slice = append(slice, elem1, elem2)
// slice = append(slice, anotherSlice...)
+//
// As a special case, it is legal to append a string to a byte slice, like this:
+//
// slice = append([]byte("hello "), "world"...)
func append(slice []Type, elems ...Type) []Type
@@ -156,24 +159,28 @@ func copy(dst, src []Type) int
func delete(m map[Type]Type1, key Type)
// The len built-in function returns the length of v, according to its type:
+//
// Array: the number of elements in v.
// Pointer to array: the number of elements in *v (even if v is nil).
// Slice, or map: the number of elements in v; if v is nil, len(v) is zero.
// String: the number of bytes in v.
// Channel: the number of elements queued (unread) in the channel buffer;
// if v is nil, len(v) is zero.
+//
// For some arguments, such as a string literal or a simple array expression, the
// result can be a constant. See the Go language specification's "Length and
// capacity" section for details.
func len(v Type) int
// The cap built-in function returns the capacity of v, according to its type:
+//
// Array: the number of elements in v (same as len(v)).
// Pointer to array: the number of elements in *v (same as len(v)).
// Slice: the maximum length the slice can reach when resliced;
// if v is nil, cap(v) is zero.
// Channel: the channel buffer capacity, in units of elements;
// if v is nil, cap(v) is zero.
+//
// For some arguments, such as a simple array expression, the result can be a
// constant. See the Go language specification's "Length and capacity" section for
// details.
@@ -184,6 +191,7 @@ func cap(v Type) int
// value. Unlike new, make's return type is the same as the type of its
// argument, not a pointer to it. The specification of the result depends on
// the type:
+//
// Slice: The size specifies the length. The capacity of the slice is
// equal to its length. A second integer argument may be provided to
// specify a different capacity; it must be no smaller than the
@@ -225,7 +233,9 @@ func imag(c ComplexType) FloatType
// the last sent value is received. After the last value has been received
// from a closed channel c, any receive from c will succeed without
// blocking, returning the zero value for the channel element. The form
+//
// x, ok := <-c
+//
// will also set ok to false for a closed channel.
func close(c chan<- Type)
diff --git a/src/bytes/bytes.go b/src/bytes/bytes.go
index e3dab4d0356..659a82bcc8c 100644
--- a/src/bytes/bytes.go
+++ b/src/bytes/bytes.go
@@ -30,7 +30,7 @@ func Compare(a, b []byte) int {
// explode splits s into a slice of UTF-8 sequences, one per Unicode code point (still slices of bytes),
// up to a maximum of n byte slices. Invalid UTF-8 sequences are chopped into individual bytes.
func explode(s []byte, n int) [][]byte {
- if n <= 0 {
+ if n <= 0 || n > len(s) {
n = len(s)
}
a := make([][]byte, n)
@@ -348,6 +348,9 @@ func genSplit(s, sep []byte, sepSave, n int) [][]byte {
if n < 0 {
n = Count(s, sep) + 1
}
+ if n > len(s)+1 {
+ n = len(s) + 1
+ }
a := make([][]byte, n)
n--
@@ -369,9 +372,10 @@ func genSplit(s, sep []byte, sepSave, n int) [][]byte {
// the subslices between those separators.
// If sep is empty, SplitN splits after each UTF-8 sequence.
// The count determines the number of subslices to return:
-// n > 0: at most n subslices; the last subslice will be the unsplit remainder.
-// n == 0: the result is nil (zero subslices)
-// n < 0: all subslices
+//
+// n > 0: at most n subslices; the last subslice will be the unsplit remainder.
+// n == 0: the result is nil (zero subslices)
+// n < 0: all subslices
//
// To split around the first instance of a separator, see Cut.
func SplitN(s, sep []byte, n int) [][]byte { return genSplit(s, sep, 0, n) }
@@ -380,9 +384,10 @@ func SplitN(s, sep []byte, n int) [][]byte { return genSplit(s, sep, 0, n) }
// returns a slice of those subslices.
// If sep is empty, SplitAfterN splits after each UTF-8 sequence.
// The count determines the number of subslices to return:
-// n > 0: at most n subslices; the last subslice will be the unsplit remainder.
-// n == 0: the result is nil (zero subslices)
-// n < 0: all subslices
+//
+// n > 0: at most n subslices; the last subslice will be the unsplit remainder.
+// n == 0: the result is nil (zero subslices)
+// n < 0: all subslices
func SplitAfterN(s, sep []byte, n int) [][]byte {
return genSplit(s, sep, len(sep), n)
}
@@ -1139,7 +1144,7 @@ func ReplaceAll(s, old, new []byte) []byte {
}
// EqualFold reports whether s and t, interpreted as UTF-8 strings,
-// are equal under Unicode case-folding, which is a more general
+// are equal under simple Unicode case-folding, which is a more general
// form of case-insensitivity.
func EqualFold(s, t []byte) bool {
for len(s) != 0 && len(t) != 0 {
diff --git a/src/bytes/bytes_test.go b/src/bytes/bytes_test.go
index 2e6ab315404..b702efb2393 100644
--- a/src/bytes/bytes_test.go
+++ b/src/bytes/bytes_test.go
@@ -8,6 +8,7 @@ import (
. "bytes"
"fmt"
"internal/testenv"
+ "math"
"math/rand"
"reflect"
"strings"
@@ -723,6 +724,7 @@ var splittests = []SplitTest{
{"1 2", " ", 3, []string{"1", "2"}},
{"123", "", 2, []string{"1", "23"}},
{"123", "", 17, []string{"1", "2", "3"}},
+ {"bT", "T", math.MaxInt / 4, []string{"b", ""}},
}
func TestSplit(t *testing.T) {
diff --git a/src/cmd/addr2line/main.go b/src/cmd/addr2line/main.go
index 018802940b2..6e005a8fac9 100644
--- a/src/cmd/addr2line/main.go
+++ b/src/cmd/addr2line/main.go
@@ -6,6 +6,7 @@
// just enough to support pprof.
//
// Usage:
+//
// go tool addr2line binary
//
// Addr2line reads hexadecimal addresses, one per line and with optional 0x prefix,
diff --git a/src/cmd/asm/doc.go b/src/cmd/asm/doc.go
index 4a0c785aad4..098f0639090 100644
--- a/src/cmd/asm/doc.go
+++ b/src/cmd/asm/doc.go
@@ -3,11 +3,11 @@
// license that can be found in the LICENSE file.
/*
-Asm, typically invoked as ``go tool asm'', assembles the source file into an object
+Asm, typically invoked as “go tool asm”, assembles the source file into an object
file named for the basename of the argument source file with a .o suffix. The
object file can then be combined with other objects into a package archive.
-Command Line
+# Command Line
Usage:
diff --git a/src/cmd/asm/internal/asm/parse.go b/src/cmd/asm/internal/asm/parse.go
index 59aedbf0cc5..acd03e13994 100644
--- a/src/cmd/asm/internal/asm/parse.go
+++ b/src/cmd/asm/internal/asm/parse.go
@@ -162,7 +162,7 @@ func (p *Parser) nextToken() lex.ScanToken {
// line consumes a single assembly line from p.lex of the form
//
-// {label:} WORD[.cond] [ arg {, arg} ] (';' | '\n')
+// {label:} WORD[.cond] [ arg {, arg} ] (';' | '\n')
//
// It adds any labels to p.pendingLabels and returns the word, cond,
// operand list, and true. If there is an error or EOF, it returns
@@ -891,7 +891,7 @@ func (p *Parser) symRefAttrs(name string, issueError bool) (bool, obj.ABI) {
// constrained form of the operand syntax that's always SB-based,
// non-static, and has at most a simple integer offset:
//
-// [$|*]sym[<abi>][+Int](SB)
+// [$|*]sym[<abi>][+Int](SB)
func (p *Parser) funcAddress() (string, obj.ABI, bool) {
switch p.peek() {
case '$', '*':
@@ -1041,9 +1041,13 @@ func (p *Parser) registerIndirect(a *obj.Addr, prefix rune) {
//
// For 386/AMD64 register list specifies 4VNNIW-style multi-source operand.
// For range of 4 elements, Intel manual uses "+3" notation, for example:
+//
// VP4DPWSSDS zmm1{k1}{z}, zmm2+3, m128
+//
// Given asm line:
+//
// VP4DPWSSDS Z5, [Z10-Z13], (AX)
+//
// zmm2 is Z10, and Z13 is the only valid value for it (Z10+3).
// Only simple ranges are accepted, like [Z0-Z3].
//
diff --git a/src/cmd/asm/internal/lex/tokenizer.go b/src/cmd/asm/internal/lex/tokenizer.go
index 861a2d421d5..4db88e20c37 100644
--- a/src/cmd/asm/internal/lex/tokenizer.go
+++ b/src/cmd/asm/internal/lex/tokenizer.go
@@ -109,7 +109,7 @@ func (t *Tokenizer) Next() ScanToken {
}
text := s.TokenText()
t.line += strings.Count(text, "\n")
- // TODO: Use constraint.IsGoBuild once it exists.
+ // TODO: Use constraint.IsGoBuild once #44505 fixed.
if strings.HasPrefix(text, "//go:build") {
t.tok = BuildComment
break
diff --git a/src/cmd/buildid/doc.go b/src/cmd/buildid/doc.go
index d1ec155c976..a554d798c06 100644
--- a/src/cmd/buildid/doc.go
+++ b/src/cmd/buildid/doc.go
@@ -6,6 +6,7 @@
Buildid displays or updates the build ID stored in a Go package or binary.
Usage:
+
go tool buildid [-w] file
By default, buildid prints the build ID found in the named file.
diff --git a/src/cmd/cgo/doc.go b/src/cmd/cgo/doc.go
index a6787f64050..4c62c5d70e4 100644
--- a/src/cmd/cgo/doc.go
+++ b/src/cmd/cgo/doc.go
@@ -3,10 +3,9 @@
// license that can be found in the LICENSE file.
/*
-
Cgo enables the creation of Go packages that call C code.
-Using cgo with the go command
+# Using cgo with the go command
To use cgo write normal Go code that imports a pseudo-package "C".
The Go code can then refer to types such as C.size_t, variables such
@@ -91,11 +90,11 @@ file. This allows pre-compiled static libraries to be included in the package
directory and linked properly.
For example if package foo is in the directory /go/src/foo:
- // #cgo LDFLAGS: -L${SRCDIR}/libs -lfoo
+ // #cgo LDFLAGS: -L${SRCDIR}/libs -lfoo
Will be expanded to:
- // #cgo LDFLAGS: -L/go/src/foo/libs -lfoo
+ // #cgo LDFLAGS: -L/go/src/foo/libs -lfoo
When the Go tool sees that one or more Go files use the special import
"C", it will look for other non-Go files in the directory and compile
@@ -139,7 +138,7 @@ or you can set the CC environment variable any time you run the go tool.
The CXX_FOR_TARGET, CXX_FOR_${GOOS}_${GOARCH}, and CXX
environment variables work in a similar way for C++ code.
-Go references to C
+# Go references to C
Within the Go file, C's struct field names that are keywords in Go
can be accessed by prefixing them with an underscore: if x points at a C
@@ -291,7 +290,7 @@ the helper function crashes the program, like when Go itself runs out
of memory. Because C.malloc cannot fail, it has no two-result form
that returns errno.
-C references to Go
+# C references to Go
Go functions can be exported for use by C code in the following way:
@@ -327,7 +326,7 @@ definitions and declarations, then the two output files will produce
duplicate symbols and the linker will fail. To avoid this, definitions
must be placed in preambles in other files, or in C source files.
-Passing pointers
+# Passing pointers
Go is a garbage collected language, and the garbage collector needs to
know the location of every pointer to Go memory. Because of this,
@@ -398,7 +397,7 @@ passing uninitialized C memory to Go code if the Go code is going to
store pointer values in it. Zero out the memory in C before passing it
to Go.
-Special cases
+# Special cases
A few special C types which would normally be represented by a pointer
type in Go are instead represented by a uintptr. Those include:
@@ -449,9 +448,10 @@ to auto-update code from Go 1.14 and earlier:
go tool fix -r eglconf <pkg>
-Using cgo directly
+# Using cgo directly
Usage:
+
go tool cgo [cgo options] [-- compiler options] gofiles...
Cgo transforms the specified input Go source files into several output
diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go
index 9877182fc4b..a52163fd656 100644
--- a/src/cmd/cgo/gcc.go
+++ b/src/cmd/cgo/gcc.go
@@ -114,11 +114,11 @@ func (p *Package) addToFlag(flag string, args []string) {
//
// For example, the following string:
//
-// `a b:"c d" 'e''f' "g\""`
+// `a b:"c d" 'e''f' "g\""`
//
// Would be parsed as:
//
-// []string{"a", "b:c d", "ef", `g"`}
+// []string{"a", "b:c d", "ef", `g"`}
func splitQuoted(s string) (r []string, err error) {
var args []string
arg := make([]rune, len(s))
@@ -1137,13 +1137,19 @@ func (p *Package) mangle(f *File, arg *ast.Expr, addPosition bool) (ast.Expr, bo
// checkIndex checks whether arg has the form &a[i], possibly inside
// type conversions. If so, then in the general case it writes
-// _cgoIndexNN := a
-// _cgoNN := &cgoIndexNN[i] // with type conversions, if any
+//
+// _cgoIndexNN := a
+// _cgoNN := &cgoIndexNN[i] // with type conversions, if any
+//
// to sb, and writes
-// _cgoCheckPointer(_cgoNN, _cgoIndexNN)
+//
+// _cgoCheckPointer(_cgoNN, _cgoIndexNN)
+//
// to sbCheck, and returns true. If a is a simple variable or field reference,
// it writes
-// _cgoIndexNN := &a
+//
+// _cgoIndexNN := &a
+//
// and dereferences the uses of _cgoIndexNN. Taking the address avoids
// making a copy of an array.
//
@@ -1191,10 +1197,14 @@ func (p *Package) checkIndex(sb, sbCheck *bytes.Buffer, arg ast.Expr, i int) boo
// checkAddr checks whether arg has the form &x, possibly inside type
// conversions. If so, it writes
-// _cgoBaseNN := &x
-// _cgoNN := _cgoBaseNN // with type conversions, if any
+//
+// _cgoBaseNN := &x
+// _cgoNN := _cgoBaseNN // with type conversions, if any
+//
// to sb, and writes
-// _cgoCheckPointer(_cgoBaseNN, true)
+//
+// _cgoCheckPointer(_cgoBaseNN, true)
+//
// to sbCheck, and returns true. This tells _cgoCheckPointer to check
// just the contents of the pointer being passed, not any other part
// of the memory allocation. This is run after checkIndex, which looks
diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go
index 8ead173e64d..adbb761e388 100644
--- a/src/cmd/cgo/out.go
+++ b/src/cmd/cgo/out.go
@@ -1399,6 +1399,19 @@ func (p *Package) cgoType(e ast.Expr) *Type {
case *ast.ChanType:
return &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("GoChan")}
case *ast.Ident:
+ goTypesFixup := func(r *Type) *Type {
+ if r.Size == 0 { // int or uint
+ rr := new(Type)
+ *rr = *r
+ rr.Size = p.IntSize
+ rr.Align = p.IntSize
+ r = rr
+ }
+ if r.Align > p.PtrSize {
+ r.Align = p.PtrSize
+ }
+ return r
+ }
// Look up the type in the top level declarations.
// TODO: Handle types defined within a function.
for _, d := range p.Decl {
@@ -1417,6 +1430,17 @@ func (p *Package) cgoType(e ast.Expr) *Type {
}
}
if def := typedef[t.Name]; def != nil {
+ if defgo, ok := def.Go.(*ast.Ident); ok {
+ switch defgo.Name {
+ case "complex64", "complex128":
+ // MSVC does not support the _Complex keyword
+ // nor the complex macro.
+ // Use GoComplex64 and GoComplex128 instead,
+ // which are typedef-ed to a compatible type.
+ // See go.dev/issues/36233.
+ return goTypesFixup(goTypes[defgo.Name])
+ }
+ }
return def
}
if t.Name == "uintptr" {
@@ -1430,17 +1454,7 @@ func (p *Package) cgoType(e ast.Expr) *Type {
return &Type{Size: 2 * p.PtrSize, Align: p.PtrSize, C: c("GoInterface")}
}
if r, ok := goTypes[t.Name]; ok {
- if r.Size == 0 { // int or uint
- rr := new(Type)
- *rr = *r
- rr.Size = p.IntSize
- rr.Align = p.IntSize
- r = rr
- }
- if r.Align > p.PtrSize {
- r.Align = p.PtrSize
- }
- return r
+ return goTypesFixup(r)
}
error_(e.Pos(), "unrecognized Go type %s", t.Name)
return &Type{Size: 4, Align: 4, C: c("int")}
@@ -1895,8 +1909,14 @@ typedef GoUintGOINTBITS GoUint;
typedef size_t GoUintptr;
typedef float GoFloat32;
typedef double GoFloat64;
+#ifdef _MSC_VER
+#include <complex.h>
+typedef _Fcomplex GoComplex64;
+typedef _Dcomplex GoComplex128;
+#else
typedef float _Complex GoComplex64;
typedef double _Complex GoComplex128;
+#endif
/*
static assertion to make sure the file is being used on architecture
diff --git a/src/cmd/compile/internal/abi/abiutils.go b/src/cmd/compile/internal/abi/abiutils.go
index 07ece87c411..aa5063f741d 100644
--- a/src/cmd/compile/internal/abi/abiutils.go
+++ b/src/cmd/compile/internal/abi/abiutils.go
@@ -258,7 +258,7 @@ type RegAmounts struct {
// by the ABI rules for parameter passing and result returning.
type ABIConfig struct {
// Do we need anything more than this?
- offsetForLocals int64 // e.g., obj.(*Link).FixedFrameSize() -- extra linkage information on some architectures.
+ offsetForLocals int64 // e.g., obj.(*Link).Arch.FixedFrameSize -- extra linkage information on some architectures.
regAmounts RegAmounts
regsForTypeCache map[*types.Type]int
}
diff --git a/src/cmd/compile/internal/amd64/ssa.go b/src/cmd/compile/internal/amd64/ssa.go
index d34fdc611b6..c9667bd04ae 100644
--- a/src/cmd/compile/internal/amd64/ssa.go
+++ b/src/cmd/compile/internal/amd64/ssa.go
@@ -111,7 +111,9 @@ func moveByType(t *types.Type) obj.As {
}
// opregreg emits instructions for
-// dest := dest(To) op src(From)
+//
+// dest := dest(To) op src(From)
+//
// and also returns the created obj.Prog so it
// may be further adjusted (offset, scale, etc).
func opregreg(s *ssagen.State, op obj.As, dest, src int16) *obj.Prog {
@@ -280,8 +282,15 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.To.Reg = v.Reg()
p.SetFrom3Reg(v.Args[1].Reg())
+ case ssa.OpAMD64SARXL, ssa.OpAMD64SARXQ,
+ ssa.OpAMD64SHLXL, ssa.OpAMD64SHLXQ,
+ ssa.OpAMD64SHRXL, ssa.OpAMD64SHRXQ:
+ p := opregreg(s, v.Op.Asm(), v.Reg(), v.Args[1].Reg())
+ p.SetFrom3Reg(v.Args[0].Reg())
+
case ssa.OpAMD64SHLXLload, ssa.OpAMD64SHLXQload,
- ssa.OpAMD64SHRXLload, ssa.OpAMD64SHRXQload:
+ ssa.OpAMD64SHRXLload, ssa.OpAMD64SHRXQload,
+ ssa.OpAMD64SARXLload, ssa.OpAMD64SARXQload:
p := opregreg(s, v.Op.Asm(), v.Reg(), v.Args[1].Reg())
m := obj.Addr{Type: obj.TYPE_MEM, Reg: v.Args[0].Reg()}
ssagen.AddAux(&m, v)
@@ -289,8 +298,10 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
case ssa.OpAMD64SHLXLloadidx1, ssa.OpAMD64SHLXLloadidx4, ssa.OpAMD64SHLXLloadidx8,
ssa.OpAMD64SHRXLloadidx1, ssa.OpAMD64SHRXLloadidx4, ssa.OpAMD64SHRXLloadidx8,
+ ssa.OpAMD64SARXLloadidx1, ssa.OpAMD64SARXLloadidx4, ssa.OpAMD64SARXLloadidx8,
ssa.OpAMD64SHLXQloadidx1, ssa.OpAMD64SHLXQloadidx8,
- ssa.OpAMD64SHRXQloadidx1, ssa.OpAMD64SHRXQloadidx8:
+ ssa.OpAMD64SHRXQloadidx1, ssa.OpAMD64SHRXQloadidx8,
+ ssa.OpAMD64SARXQloadidx1, ssa.OpAMD64SARXQloadidx8:
p := opregreg(s, v.Op.Asm(), v.Reg(), v.Args[2].Reg())
m := obj.Addr{Type: obj.TYPE_MEM}
memIdx(&m, v)
@@ -786,7 +797,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpAMD64MOVBloadidx1, ssa.OpAMD64MOVWloadidx1, ssa.OpAMD64MOVLloadidx1, ssa.OpAMD64MOVQloadidx1, ssa.OpAMD64MOVSSloadidx1, ssa.OpAMD64MOVSDloadidx1,
- ssa.OpAMD64MOVQloadidx8, ssa.OpAMD64MOVSDloadidx8, ssa.OpAMD64MOVLloadidx8, ssa.OpAMD64MOVLloadidx4, ssa.OpAMD64MOVSSloadidx4, ssa.OpAMD64MOVWloadidx2:
+ ssa.OpAMD64MOVQloadidx8, ssa.OpAMD64MOVSDloadidx8, ssa.OpAMD64MOVLloadidx8, ssa.OpAMD64MOVLloadidx4, ssa.OpAMD64MOVSSloadidx4, ssa.OpAMD64MOVWloadidx2,
+ ssa.OpAMD64MOVBELloadidx1, ssa.OpAMD64MOVBELloadidx4, ssa.OpAMD64MOVBELloadidx8, ssa.OpAMD64MOVBEQloadidx1, ssa.OpAMD64MOVBEQloadidx8:
p := s.Prog(v.Op.Asm())
memIdx(&p.From, v)
ssagen.AddAux(&p.From, v)
@@ -808,7 +820,8 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
ssa.OpAMD64SUBLmodifyidx1, ssa.OpAMD64SUBLmodifyidx4, ssa.OpAMD64SUBLmodifyidx8, ssa.OpAMD64SUBQmodifyidx1, ssa.OpAMD64SUBQmodifyidx8,
ssa.OpAMD64ANDLmodifyidx1, ssa.OpAMD64ANDLmodifyidx4, ssa.OpAMD64ANDLmodifyidx8, ssa.OpAMD64ANDQmodifyidx1, ssa.OpAMD64ANDQmodifyidx8,
ssa.OpAMD64ORLmodifyidx1, ssa.OpAMD64ORLmodifyidx4, ssa.OpAMD64ORLmodifyidx8, ssa.OpAMD64ORQmodifyidx1, ssa.OpAMD64ORQmodifyidx8,
- ssa.OpAMD64XORLmodifyidx1, ssa.OpAMD64XORLmodifyidx4, ssa.OpAMD64XORLmodifyidx8, ssa.OpAMD64XORQmodifyidx1, ssa.OpAMD64XORQmodifyidx8:
+ ssa.OpAMD64XORLmodifyidx1, ssa.OpAMD64XORLmodifyidx4, ssa.OpAMD64XORLmodifyidx8, ssa.OpAMD64XORQmodifyidx1, ssa.OpAMD64XORQmodifyidx8,
+ ssa.OpAMD64MOVBEWstoreidx1, ssa.OpAMD64MOVBEWstoreidx2, ssa.OpAMD64MOVBELstoreidx1, ssa.OpAMD64MOVBELstoreidx4, ssa.OpAMD64MOVBELstoreidx8, ssa.OpAMD64MOVBEQstoreidx1, ssa.OpAMD64MOVBEQstoreidx8:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[2].Reg()
@@ -1087,7 +1100,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
}
p := s.Prog(mov)
p.From.Type = obj.TYPE_ADDR
- p.From.Offset = -base.Ctxt.FixedFrameSize() // 0 on amd64, just to be consistent with other architectures
+ p.From.Offset = -base.Ctxt.Arch.FixedFrameSize // 0 on amd64, just to be consistent with other architectures
p.From.Name = obj.NAME_PARAM
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
@@ -1387,6 +1400,16 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
}
}
+ case ssa.BlockAMD64JUMPTABLE:
+ // JMP *(TABLE)(INDEX*8)
+ p := s.Prog(obj.AJMP)
+ p.To.Type = obj.TYPE_MEM
+ p.To.Reg = b.Controls[1].Reg()
+ p.To.Index = b.Controls[0].Reg()
+ p.To.Scale = 8
+ // Save jump tables for later resolution of the target blocks.
+ s.JumpTables = append(s.JumpTables, b)
+
default:
b.Fatalf("branch not implemented: %s", b.LongString())
}
diff --git a/src/cmd/compile/internal/amd64/versions_test.go b/src/cmd/compile/internal/amd64/versions_test.go
index e28aefbd3c6..1ef06f7e581 100644
--- a/src/cmd/compile/internal/amd64/versions_test.go
+++ b/src/cmd/compile/internal/amd64/versions_test.go
@@ -241,6 +241,7 @@ var featureToOpcodes = map[string][]string{
// native objdump doesn't include [QL] on linux.
"popcnt": {"popcntq", "popcntl", "popcnt"},
"bmi1": {"andnq", "andnl", "andn", "blsiq", "blsil", "blsi", "blsmskq", "blsmskl", "blsmsk", "blsrq", "blsrl", "blsr", "tzcntq", "tzcntl", "tzcnt"},
+ "bmi2": {"sarxq", "sarxl", "sarx", "shlxq", "shlxl", "shlx", "shrxq", "shrxl", "shrx"},
"sse41": {"roundsd"},
"fma": {"vfmadd231sd"},
"movbe": {"movbeqq", "movbeq", "movbell", "movbel", "movbe"},
diff --git a/src/cmd/compile/internal/arm/ssa.go b/src/cmd/compile/internal/arm/ssa.go
index 063fb65b33a..a53f51bd137 100644
--- a/src/cmd/compile/internal/arm/ssa.go
+++ b/src/cmd/compile/internal/arm/ssa.go
@@ -854,7 +854,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
// caller's SP is FixedFrameSize below the address of the first arg
p := s.Prog(arm.AMOVW)
p.From.Type = obj.TYPE_ADDR
- p.From.Offset = -base.Ctxt.FixedFrameSize()
+ p.From.Offset = -base.Ctxt.Arch.FixedFrameSize
p.From.Name = obj.NAME_PARAM
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
diff --git a/src/cmd/compile/internal/arm64/ssa.go b/src/cmd/compile/internal/arm64/ssa.go
index 48eb2190b25..3b6e6f67230 100644
--- a/src/cmd/compile/internal/arm64/ssa.go
+++ b/src/cmd/compile/internal/arm64/ssa.go
@@ -171,7 +171,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
for _, a := range v.Block.Func.RegArgs {
// Pass the spill/unspill information along to the assembler, offset by size of
// the saved LR slot.
- addr := ssagen.SpillSlotAddr(a, arm64.REGSP, base.Ctxt.FixedFrameSize())
+ addr := ssagen.SpillSlotAddr(a, arm64.REGSP, base.Ctxt.Arch.FixedFrameSize)
s.FuncInfo().AddSpill(
obj.RegSpill{Reg: a.Reg, Addr: addr, Unspill: loadByType(a.Type), Spill: storeByType(a.Type)})
}
@@ -1128,7 +1128,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
// caller's SP is FixedFrameSize below the address of the first arg
p := s.Prog(arm64.AMOVD)
p.From.Type = obj.TYPE_ADDR
- p.From.Offset = -base.Ctxt.FixedFrameSize()
+ p.From.Offset = -base.Ctxt.Arch.FixedFrameSize
p.From.Name = obj.NAME_PARAM
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
diff --git a/src/cmd/compile/internal/deadcode/deadcode.go b/src/cmd/compile/internal/deadcode/deadcode.go
index c37a5a6990c..decd2611836 100644
--- a/src/cmd/compile/internal/deadcode/deadcode.go
+++ b/src/cmd/compile/internal/deadcode/deadcode.go
@@ -6,6 +6,7 @@ package deadcode
import (
"go/constant"
+ "go/token"
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
@@ -86,6 +87,85 @@ func stmts(nn *ir.Nodes) {
}
}
}
+ if n.Op() == ir.OSWITCH {
+ n := n.(*ir.SwitchStmt)
+ // Use a closure wrapper here so we can use "return" to abort the analysis.
+ func() {
+ if n.Tag != nil && n.Tag.Op() == ir.OTYPESW {
+ return // no special type-switch case yet.
+ }
+ var x constant.Value // value we're switching on
+ if n.Tag != nil {
+ if ir.ConstType(n.Tag) == constant.Unknown {
+ return
+ }
+ x = n.Tag.Val()
+ } else {
+ x = constant.MakeBool(true) // switch { ... } => switch true { ... }
+ }
+ var def *ir.CaseClause
+ for _, cas := range n.Cases {
+ if len(cas.List) == 0 { // default case
+ def = cas
+ continue
+ }
+ for _, c := range cas.List {
+ if ir.ConstType(c) == constant.Unknown {
+ return // can't statically tell if it matches or not - give up.
+ }
+ if constant.Compare(x, token.EQL, c.Val()) {
+ for _, n := range cas.Body {
+ if n.Op() == ir.OFALL {
+ return // fallthrough makes it complicated - abort.
+ }
+ }
+ // This switch entry is the one that always triggers.
+ for _, cas2 := range n.Cases {
+ for _, c2 := range cas2.List {
+ if cas2 != cas || c2 != c {
+ ir.Visit(c2, markHiddenClosureDead)
+ }
+ }
+ if cas2 != cas {
+ ir.VisitList(cas2.Body, markHiddenClosureDead)
+ }
+ }
+
+ cas.List[0] = c
+ cas.List = cas.List[:1]
+ n.Cases[0] = cas
+ n.Cases = n.Cases[:1]
+ return
+ }
+ }
+ }
+ if def != nil {
+ for _, n := range def.Body {
+ if n.Op() == ir.OFALL {
+ return // fallthrough makes it complicated - abort.
+ }
+ }
+ for _, cas := range n.Cases {
+ if cas != def {
+ ir.VisitList(cas.List, markHiddenClosureDead)
+ ir.VisitList(cas.Body, markHiddenClosureDead)
+ }
+ }
+ n.Cases[0] = def
+ n.Cases = n.Cases[:1]
+ return
+ }
+
+ // TODO: handle case bodies ending with panic/return as we do in the IF case above.
+
+ // entire switch is a nop - no case ever triggers
+ for _, cas := range n.Cases {
+ ir.VisitList(cas.List, markHiddenClosureDead)
+ ir.VisitList(cas.Body, markHiddenClosureDead)
+ }
+ n.Cases = n.Cases[:0]
+ }()
+ }
if len(n.Init()) != 0 {
stmts(n.(ir.InitNode).PtrInit())
diff --git a/src/cmd/compile/internal/dwarfgen/dwarf.go b/src/cmd/compile/internal/dwarfgen/dwarf.go
index e249a52e57a..f84368ece34 100644
--- a/src/cmd/compile/internal/dwarfgen/dwarf.go
+++ b/src/cmd/compile/internal/dwarfgen/dwarf.go
@@ -339,7 +339,7 @@ func createSimpleVar(fnsym *obj.LSym, n *ir.Name) *dwarf.Var {
localAutoOffset := func() int64 {
offs = n.FrameOffset()
- if base.Ctxt.FixedFrameSize() == 0 {
+ if base.Ctxt.Arch.FixedFrameSize == 0 {
offs -= int64(types.PtrSize)
}
if buildcfg.FramePointerEnabled {
@@ -357,7 +357,7 @@ func createSimpleVar(fnsym *obj.LSym, n *ir.Name) *dwarf.Var {
if n.IsOutputParamInRegisters() {
offs = localAutoOffset()
} else {
- offs = n.FrameOffset() + base.Ctxt.FixedFrameSize()
+ offs = n.FrameOffset() + base.Ctxt.Arch.FixedFrameSize
}
default:
@@ -547,7 +547,7 @@ func RecordFlags(flags ...string) {
fmt.Fprintf(&cmd, " -%s=%v", f.Name, getter.Get())
}
- // Adds flag to producer string singalling whether regabi is turned on or
+ // Adds flag to producer string signaling whether regabi is turned on or
// off.
// Once regabi is turned on across the board and the relative GOEXPERIMENT
// knobs no longer exist this code should be removed.
diff --git a/src/cmd/compile/internal/escape/desugar.go b/src/cmd/compile/internal/escape/desugar.go
index 8b3cc25cf9a..6c21981acad 100644
--- a/src/cmd/compile/internal/escape/desugar.go
+++ b/src/cmd/compile/internal/escape/desugar.go
@@ -24,9 +24,9 @@ func fixRecoverCall(call *ir.CallExpr) {
pos := call.Pos()
- // FP is equal to caller's SP plus FixedFrameSize().
+ // FP is equal to caller's SP plus FixedFrameSize.
var fp ir.Node = ir.NewCallExpr(pos, ir.OGETCALLERSP, nil, nil)
- if off := base.Ctxt.FixedFrameSize(); off != 0 {
+ if off := base.Ctxt.Arch.FixedFrameSize; off != 0 {
fp = ir.NewBinaryExpr(fp.Pos(), ir.OADD, fp, ir.NewInt(off))
}
// TODO(mdempsky): Replace *int32 with unsafe.Pointer, without upsetting checkptr.
diff --git a/src/cmd/compile/internal/escape/escape.go b/src/cmd/compile/internal/escape/escape.go
index 4713ecddcae..4408a531eca 100644
--- a/src/cmd/compile/internal/escape/escape.go
+++ b/src/cmd/compile/internal/escape/escape.go
@@ -210,6 +210,9 @@ func (b *batch) walkFunc(fn *ir.Func) {
switch n.Op() {
case ir.OLABEL:
n := n.(*ir.LabelStmt)
+ if n.Label.IsBlank() {
+ break
+ }
if e.labels == nil {
e.labels = make(map[*types.Sym]labelState)
}
diff --git a/src/cmd/compile/internal/escape/stmt.go b/src/cmd/compile/internal/escape/stmt.go
index 0afb5d64ef6..4e8dd904ffb 100644
--- a/src/cmd/compile/internal/escape/stmt.go
+++ b/src/cmd/compile/internal/escape/stmt.go
@@ -50,6 +50,9 @@ func (e *escape) stmt(n ir.Node) {
case ir.OLABEL:
n := n.(*ir.LabelStmt)
+ if n.Label.IsBlank() {
+ break
+ }
switch e.labels[n.Label] {
case nonlooping:
if base.Flag.LowerM > 2 {
diff --git a/src/cmd/compile/internal/gc/obj.go b/src/cmd/compile/internal/gc/obj.go
index 74e4c0a8900..fe8b6e9d458 100644
--- a/src/cmd/compile/internal/gc/obj.go
+++ b/src/cmd/compile/internal/gc/obj.go
@@ -271,6 +271,9 @@ func addGCLocals() {
objw.Global(x, int32(len(x.P)), obj.RODATA|obj.DUPOK)
x.Set(obj.AttrStatic, true)
}
+ for _, jt := range fn.JumpTables {
+ objw.Global(jt.Sym, int32(len(jt.Targets)*base.Ctxt.Arch.PtrSize), obj.RODATA)
+ }
}
}
diff --git a/src/cmd/compile/internal/inline/inl.go b/src/cmd/compile/internal/inline/inl.go
index be01914d080..8c2ea49c8f4 100644
--- a/src/cmd/compile/internal/inline/inl.go
+++ b/src/cmd/compile/internal/inline/inl.go
@@ -522,7 +522,8 @@ func InlineCalls(fn *ir.Func) {
// but then you may as well do it here. so this is cleaner and
// shorter and less complicated.
// The result of inlnode MUST be assigned back to n, e.g.
-// n.Left = inlnode(n.Left)
+//
+// n.Left = inlnode(n.Left)
func inlnode(n ir.Node, maxCost int32, inlMap map[*ir.Func]bool, edit func(ir.Node) ir.Node) ir.Node {
if n == nil {
return n
@@ -657,7 +658,8 @@ var NewInline = func(call *ir.CallExpr, fn *ir.Func, inlIndex int) *ir.InlinedCa
// inlined function body, and (List, Rlist) contain the (input, output)
// parameters.
// The result of mkinlcall MUST be assigned back to n, e.g.
-// n.Left = mkinlcall(n.Left, fn, isddd)
+//
+// n.Left = mkinlcall(n.Left, fn, isddd)
func mkinlcall(n *ir.CallExpr, fn *ir.Func, maxCost int32, inlMap map[*ir.Func]bool, edit func(ir.Node) ir.Node) ir.Node {
if fn.Inl == nil {
if logopt.Enabled() {
diff --git a/src/cmd/compile/internal/ir/const.go b/src/cmd/compile/internal/ir/const.go
index eaa4d5b6b15..f0b66957f12 100644
--- a/src/cmd/compile/internal/ir/const.go
+++ b/src/cmd/compile/internal/ir/const.go
@@ -26,7 +26,7 @@ func NewString(s string) Node {
}
const (
- // Maximum size in bits for big.Ints before signalling
+ // Maximum size in bits for big.Ints before signaling
// overflow and also mantissa precision for big.Floats.
ConstPrec = 512
)
diff --git a/src/cmd/compile/internal/ir/expr.go b/src/cmd/compile/internal/ir/expr.go
index b5c0983d6a3..4f1f582fa1f 100644
--- a/src/cmd/compile/internal/ir/expr.go
+++ b/src/cmd/compile/internal/ir/expr.go
@@ -951,11 +951,11 @@ var IsIntrinsicCall = func(*CallExpr) bool { return false }
// instead of computing both. SameSafeExpr assumes that l and r are
// used in the same statement or expression. In order for it to be
// safe to reuse l or r, they must:
-// * be the same expression
-// * not have side-effects (no function calls, no channel ops);
-// however, panics are ok
-// * not cause inappropriate aliasing; e.g. two string to []byte
-// conversions, must result in two distinct slices
+// - be the same expression
+// - not have side-effects (no function calls, no channel ops);
+// however, panics are ok
+// - not cause inappropriate aliasing; e.g. two string to []byte
+// conversions, must result in two distinct slices
//
// The handling of OINDEXMAP is subtle. OINDEXMAP can occur both
// as an lvalue (map assignment) and an rvalue (map access). This is
diff --git a/src/cmd/compile/internal/ir/node.go b/src/cmd/compile/internal/ir/node.go
index 5e5868abb2a..9ccb8e3c30e 100644
--- a/src/cmd/compile/internal/ir/node.go
+++ b/src/cmd/compile/internal/ir/node.go
@@ -310,6 +310,7 @@ const (
ORESULT // result of a function call; Xoffset is stack offset
OINLMARK // start of an inlined body, with file/line of caller. Xoffset is an index into the inline tree.
OLINKSYMOFFSET // offset within a name
+ OJUMPTABLE // A jump table structure for implementing dense expression switches
// opcodes for generics
ODYNAMICDOTTYPE // x = i.(T) where T is a type parameter (or derived from a type parameter)
@@ -551,7 +552,8 @@ func SetPos(n Node) src.XPos {
}
// The result of InitExpr MUST be assigned back to n, e.g.
-// n.X = InitExpr(init, n.X)
+//
+// n.X = InitExpr(init, n.X)
func InitExpr(init []Node, expr Node) Node {
if len(init) == 0 {
return expr
diff --git a/src/cmd/compile/internal/ir/node_gen.go b/src/cmd/compile/internal/ir/node_gen.go
index 22ff885d68e..8d6fc8c6078 100644
--- a/src/cmd/compile/internal/ir/node_gen.go
+++ b/src/cmd/compile/internal/ir/node_gen.go
@@ -712,6 +712,28 @@ func (n *InstExpr) editChildren(edit func(Node) Node) {
editNodes(n.Targs, edit)
}
+func (n *JumpTableStmt) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
+func (n *JumpTableStmt) copy() Node {
+ c := *n
+ c.init = copyNodes(c.init)
+ return &c
+}
+func (n *JumpTableStmt) doChildren(do func(Node) bool) bool {
+ if doNodes(n.init, do) {
+ return true
+ }
+ if n.Idx != nil && do(n.Idx) {
+ return true
+ }
+ return false
+}
+func (n *JumpTableStmt) editChildren(edit func(Node) Node) {
+ editNodes(n.init, edit)
+ if n.Idx != nil {
+ n.Idx = edit(n.Idx).(Node)
+ }
+}
+
func (n *KeyExpr) Format(s fmt.State, verb rune) { fmtNode(n, s, verb) }
func (n *KeyExpr) copy() Node {
c := *n
diff --git a/src/cmd/compile/internal/ir/op_string.go b/src/cmd/compile/internal/ir/op_string.go
index 14eb84083a8..8927f18ceab 100644
--- a/src/cmd/compile/internal/ir/op_string.go
+++ b/src/cmd/compile/internal/ir/op_string.go
@@ -154,19 +154,20 @@ func _() {
_ = x[ORESULT-143]
_ = x[OINLMARK-144]
_ = x[OLINKSYMOFFSET-145]
- _ = x[ODYNAMICDOTTYPE-146]
- _ = x[ODYNAMICDOTTYPE2-147]
- _ = x[ODYNAMICTYPE-148]
- _ = x[OTAILCALL-149]
- _ = x[OGETG-150]
- _ = x[OGETCALLERPC-151]
- _ = x[OGETCALLERSP-152]
- _ = x[OEND-153]
+ _ = x[OJUMPTABLE-146]
+ _ = x[ODYNAMICDOTTYPE-147]
+ _ = x[ODYNAMICDOTTYPE2-148]
+ _ = x[ODYNAMICTYPE-149]
+ _ = x[OTAILCALL-150]
+ _ = x[OGETG-151]
+ _ = x[OGETCALLERPC-152]
+ _ = x[OGETCALLERSP-153]
+ _ = x[OEND-154]
}
-const _Op_name = "XXXNAMENONAMETYPELITERALNILADDSUBORXORADDSTRADDRANDANDAPPENDBYTES2STRBYTES2STRTMPRUNES2STRSTR2BYTESSTR2BYTESTMPSTR2RUNESSLICE2ARRPTRASAS2AS2DOTTYPEAS2FUNCAS2MAPRAS2RECVASOPCALLCALLFUNCCALLMETHCALLINTERCAPCLOSECLOSURECOMPLITMAPLITSTRUCTLITARRAYLITSLICELITPTRLITCONVCONVIFACECONVIDATACONVNOPCOPYDCLDCLFUNCDCLCONSTDCLTYPEDELETEDOTDOTPTRDOTMETHDOTINTERXDOTDOTTYPEDOTTYPE2EQNELTLEGEGTDEREFINDEXINDEXMAPKEYSTRUCTKEYLENMAKEMAKECHANMAKEMAPMAKESLICEMAKESLICECOPYMULDIVMODLSHRSHANDANDNOTNEWNOTBITNOTPLUSNEGORORPANICPRINTPRINTNPARENSENDSLICESLICEARRSLICESTRSLICE3SLICE3ARRSLICEHEADERRECOVERRECOVERFPRECVRUNESTRSELRECV2REALIMAGCOMPLEXALIGNOFOFFSETOFSIZEOFUNSAFEADDUNSAFESLICEMETHEXPRMETHVALUEBLOCKBREAKCASECONTINUEDEFERFALLFORFORUNTILGOTOIFLABELGORANGERETURNSELECTSWITCHTYPESWFUNCINSTTFUNCINLCALLEFACEITABIDATASPTRCFUNCCHECKNILVARDEFVARKILLVARLIVERESULTINLMARKLINKSYMOFFSETDYNAMICDOTTYPEDYNAMICDOTTYPE2DYNAMICTYPETAILCALLGETGGETCALLERPCGETCALLERSPEND"
+const _Op_name = "XXXNAMENONAMETYPELITERALNILADDSUBORXORADDSTRADDRANDANDAPPENDBYTES2STRBYTES2STRTMPRUNES2STRSTR2BYTESSTR2BYTESTMPSTR2RUNESSLICE2ARRPTRASAS2AS2DOTTYPEAS2FUNCAS2MAPRAS2RECVASOPCALLCALLFUNCCALLMETHCALLINTERCAPCLOSECLOSURECOMPLITMAPLITSTRUCTLITARRAYLITSLICELITPTRLITCONVCONVIFACECONVIDATACONVNOPCOPYDCLDCLFUNCDCLCONSTDCLTYPEDELETEDOTDOTPTRDOTMETHDOTINTERXDOTDOTTYPEDOTTYPE2EQNELTLEGEGTDEREFINDEXINDEXMAPKEYSTRUCTKEYLENMAKEMAKECHANMAKEMAPMAKESLICEMAKESLICECOPYMULDIVMODLSHRSHANDANDNOTNEWNOTBITNOTPLUSNEGORORPANICPRINTPRINTNPARENSENDSLICESLICEARRSLICESTRSLICE3SLICE3ARRSLICEHEADERRECOVERRECOVERFPRECVRUNESTRSELRECV2REALIMAGCOMPLEXALIGNOFOFFSETOFSIZEOFUNSAFEADDUNSAFESLICEMETHEXPRMETHVALUEBLOCKBREAKCASECONTINUEDEFERFALLFORFORUNTILGOTOIFLABELGORANGERETURNSELECTSWITCHTYPESWFUNCINSTTFUNCINLCALLEFACEITABIDATASPTRCFUNCCHECKNILVARDEFVARKILLVARLIVERESULTINLMARKLINKSYMOFFSETJUMPTABLEDYNAMICDOTTYPEDYNAMICDOTTYPE2DYNAMICTYPETAILCALLGETGGETCALLERPCGETCALLERSPEND"
-var _Op_index = [...]uint16{0, 3, 7, 13, 17, 24, 27, 30, 33, 35, 38, 44, 48, 54, 60, 69, 81, 90, 99, 111, 120, 132, 134, 137, 147, 154, 161, 168, 172, 176, 184, 192, 201, 204, 209, 216, 223, 229, 238, 246, 254, 260, 264, 273, 282, 289, 293, 296, 303, 311, 318, 324, 327, 333, 340, 348, 352, 359, 367, 369, 371, 373, 375, 377, 379, 384, 389, 397, 400, 409, 412, 416, 424, 431, 440, 453, 456, 459, 462, 465, 468, 471, 477, 480, 483, 489, 493, 496, 500, 505, 510, 516, 521, 525, 530, 538, 546, 552, 561, 572, 579, 588, 592, 599, 607, 611, 615, 622, 629, 637, 643, 652, 663, 671, 680, 685, 690, 694, 702, 707, 711, 714, 722, 726, 728, 733, 735, 740, 746, 752, 758, 764, 772, 777, 784, 789, 793, 798, 802, 807, 815, 821, 828, 835, 841, 848, 861, 875, 890, 901, 909, 913, 924, 935, 938}
+var _Op_index = [...]uint16{0, 3, 7, 13, 17, 24, 27, 30, 33, 35, 38, 44, 48, 54, 60, 69, 81, 90, 99, 111, 120, 132, 134, 137, 147, 154, 161, 168, 172, 176, 184, 192, 201, 204, 209, 216, 223, 229, 238, 246, 254, 260, 264, 273, 282, 289, 293, 296, 303, 311, 318, 324, 327, 333, 340, 348, 352, 359, 367, 369, 371, 373, 375, 377, 379, 384, 389, 397, 400, 409, 412, 416, 424, 431, 440, 453, 456, 459, 462, 465, 468, 471, 477, 480, 483, 489, 493, 496, 500, 505, 510, 516, 521, 525, 530, 538, 546, 552, 561, 572, 579, 588, 592, 599, 607, 611, 615, 622, 629, 637, 643, 652, 663, 671, 680, 685, 690, 694, 702, 707, 711, 714, 722, 726, 728, 733, 735, 740, 746, 752, 758, 764, 772, 777, 784, 789, 793, 798, 802, 807, 815, 821, 828, 835, 841, 848, 861, 870, 884, 899, 910, 918, 922, 933, 944, 947}
func (i Op) String() string {
if i >= Op(len(_Op_index)-1) {
diff --git a/src/cmd/compile/internal/ir/stmt.go b/src/cmd/compile/internal/ir/stmt.go
index 80bd205436c..0e76f174408 100644
--- a/src/cmd/compile/internal/ir/stmt.go
+++ b/src/cmd/compile/internal/ir/stmt.go
@@ -8,6 +8,7 @@ import (
"cmd/compile/internal/base"
"cmd/compile/internal/types"
"cmd/internal/src"
+ "go/constant"
)
// A Decl is a declaration of a const, type, or var. (A declared func is a Func.)
@@ -262,6 +263,37 @@ func NewIfStmt(pos src.XPos, cond Node, body, els []Node) *IfStmt {
return n
}
+// A JumpTableStmt is used to implement switches. Its semantics are:
+// tmp := jt.Idx
+// if tmp == Cases[0] goto Targets[0]
+// if tmp == Cases[1] goto Targets[1]
+// ...
+// if tmp == Cases[n] goto Targets[n]
+// Note that a JumpTableStmt is more like a multiway-goto than
+// a multiway-if. In particular, the case bodies are just
+// labels to jump to, not not full Nodes lists.
+type JumpTableStmt struct {
+ miniStmt
+
+ // Value used to index the jump table.
+ // We support only integer types that
+ // are at most the size of a uintptr.
+ Idx Node
+
+ // If Idx is equal to Cases[i], jump to Targets[i].
+ // Cases entries must be distinct and in increasing order.
+ // The length of Cases and Targets must be equal.
+ Cases []constant.Value
+ Targets []*types.Sym
+}
+
+func NewJumpTableStmt(pos src.XPos, idx Node) *JumpTableStmt {
+ n := &JumpTableStmt{Idx: idx}
+ n.pos = pos
+ n.op = OJUMPTABLE
+ return n
+}
+
// An InlineMarkStmt is a marker placed just before an inlined body.
type InlineMarkStmt struct {
miniStmt
diff --git a/src/cmd/compile/internal/liveness/plive.go b/src/cmd/compile/internal/liveness/plive.go
index 3202e506c8f..bd0a6fa1a33 100644
--- a/src/cmd/compile/internal/liveness/plive.go
+++ b/src/cmd/compile/internal/liveness/plive.go
@@ -244,8 +244,10 @@ func (lv *liveness) initcache() {
// liveness effects on a variable.
//
// The possible flags are:
+//
// uevar - used by the instruction
// varkill - killed by the instruction (set)
+//
// A kill happens after the use (for an instruction that updates a value, for example).
type liveEffect int
@@ -1460,14 +1462,14 @@ func (lv *liveness) emitStackObjects() *obj.LSym {
// isfat reports whether a variable of type t needs multiple assignments to initialize.
// For example:
//
-// type T struct { x, y int }
-// x := T{x: 0, y: 1}
+// type T struct { x, y int }
+// x := T{x: 0, y: 1}
//
// Then we need:
//
-// var t T
-// t.x = 0
-// t.y = 1
+// var t T
+// t.x = 0
+// t.y = 1
//
// to fully initialize t.
func isfat(t *types.Type) bool {
diff --git a/src/cmd/compile/internal/mips/ggen.go b/src/cmd/compile/internal/mips/ggen.go
index 1a5125207dd..a18440e7b3c 100644
--- a/src/cmd/compile/internal/mips/ggen.go
+++ b/src/cmd/compile/internal/mips/ggen.go
@@ -20,7 +20,7 @@ func zerorange(pp *objw.Progs, p *obj.Prog, off, cnt int64, _ *uint32) *obj.Prog
}
if cnt < int64(4*types.PtrSize) {
for i := int64(0); i < cnt; i += int64(types.PtrSize) {
- p = pp.Append(p, mips.AMOVW, obj.TYPE_REG, mips.REGZERO, 0, obj.TYPE_MEM, mips.REGSP, base.Ctxt.FixedFrameSize()+off+i)
+ p = pp.Append(p, mips.AMOVW, obj.TYPE_REG, mips.REGZERO, 0, obj.TYPE_MEM, mips.REGSP, base.Ctxt.Arch.FixedFrameSize+off+i)
}
} else {
//fmt.Printf("zerorange frame:%v, lo: %v, hi:%v \n", frame ,lo, hi)
@@ -30,7 +30,7 @@ func zerorange(pp *objw.Progs, p *obj.Prog, off, cnt int64, _ *uint32) *obj.Prog
// MOVW R0, (Widthptr)r1
// ADD $Widthptr, r1
// BNE r1, r2, loop
- p = pp.Append(p, mips.AADD, obj.TYPE_CONST, 0, base.Ctxt.FixedFrameSize()+off-4, obj.TYPE_REG, mips.REGRT1, 0)
+ p = pp.Append(p, mips.AADD, obj.TYPE_CONST, 0, base.Ctxt.Arch.FixedFrameSize+off-4, obj.TYPE_REG, mips.REGRT1, 0)
p.Reg = mips.REGSP
p = pp.Append(p, mips.AADD, obj.TYPE_CONST, 0, cnt, obj.TYPE_REG, mips.REGRT2, 0)
p.Reg = mips.REGRT1
diff --git a/src/cmd/compile/internal/mips/ssa.go b/src/cmd/compile/internal/mips/ssa.go
index 6326f966bf2..0411756c8d7 100644
--- a/src/cmd/compile/internal/mips/ssa.go
+++ b/src/cmd/compile/internal/mips/ssa.go
@@ -792,7 +792,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
// caller's SP is FixedFrameSize below the address of the first arg
p := s.Prog(mips.AMOVW)
p.From.Type = obj.TYPE_ADDR
- p.From.Offset = -base.Ctxt.FixedFrameSize()
+ p.From.Offset = -base.Ctxt.Arch.FixedFrameSize
p.From.Name = obj.NAME_PARAM
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
diff --git a/src/cmd/compile/internal/mips64/ssa.go b/src/cmd/compile/internal/mips64/ssa.go
index 6e12c6cb942..f3e372c3bcb 100644
--- a/src/cmd/compile/internal/mips64/ssa.go
+++ b/src/cmd/compile/internal/mips64/ssa.go
@@ -762,7 +762,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
// caller's SP is FixedFrameSize below the address of the first arg
p := s.Prog(mips.AMOVV)
p.From.Type = obj.TYPE_ADDR
- p.From.Offset = -base.Ctxt.FixedFrameSize()
+ p.From.Offset = -base.Ctxt.Arch.FixedFrameSize
p.From.Name = obj.NAME_PARAM
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
diff --git a/src/cmd/compile/internal/noder/expr.go b/src/cmd/compile/internal/noder/expr.go
index 566abda963e..e37e4cd661d 100644
--- a/src/cmd/compile/internal/noder/expr.go
+++ b/src/cmd/compile/internal/noder/expr.go
@@ -6,6 +6,7 @@ package noder
import (
"fmt"
+ "go/constant"
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
@@ -62,6 +63,14 @@ func (g *irgen) expr(expr syntax.Expr) ir.Node {
case types2.UntypedNil:
// ok; can appear in type switch case clauses
// TODO(mdempsky): Handle as part of type switches instead?
+ case types2.UntypedInt, types2.UntypedFloat, types2.UntypedComplex:
+ // Untyped rhs of non-constant shift, e.g. x << 1.0.
+ // If we have a constant value, it must be an int >= 0.
+ if tv.Value != nil {
+ s := constant.ToInt(tv.Value)
+ assert(s.Kind() == constant.Int && constant.Sign(s) >= 0)
+ }
+ typ = types2.Typ[types2.Uint]
case types2.UntypedBool:
typ = types2.Typ[types2.Bool] // expression in "if" or "for" condition
case types2.UntypedString:
diff --git a/src/cmd/compile/internal/noder/noder.go b/src/cmd/compile/internal/noder/noder.go
index 77ca642183b..cc5610acda9 100644
--- a/src/cmd/compile/internal/noder/noder.go
+++ b/src/cmd/compile/internal/noder/noder.go
@@ -33,29 +33,35 @@ func LoadPackage(filenames []string) {
sem := make(chan struct{}, runtime.GOMAXPROCS(0)+10)
noders := make([]*noder, len(filenames))
- for i, filename := range filenames {
+ for i := range noders {
p := noder{
err: make(chan syntax.Error),
}
noders[i] = &p
+ }
- filename := filename
- go func() {
+ // Move the entire syntax processing logic into a separate goroutine to avoid blocking on the "sem".
+ go func() {
+ for i, filename := range filenames {
+ filename := filename
+ p := noders[i]
sem <- struct{}{}
- defer func() { <-sem }()
- defer close(p.err)
- fbase := syntax.NewFileBase(filename)
-
- f, err := os.Open(filename)
- if err != nil {
- p.error(syntax.Error{Msg: err.Error()})
- return
- }
- defer f.Close()
+ go func() {
+ defer func() { <-sem }()
+ defer close(p.err)
+ fbase := syntax.NewFileBase(filename)
+
+ f, err := os.Open(filename)
+ if err != nil {
+ p.error(syntax.Error{Msg: err.Error()})
+ return
+ }
+ defer f.Close()
- p.file, _ = syntax.Parse(fbase, f, p.error, p.pragma, mode) // errors are tracked via p.error
- }()
- }
+ p.file, _ = syntax.Parse(fbase, f, p.error, p.pragma, mode) // errors are tracked via p.error
+ }()
+ }
+ }()
var lines uint
for _, p := range noders {
diff --git a/src/cmd/compile/internal/noder/stencil.go b/src/cmd/compile/internal/noder/stencil.go
index eeac8d8de7d..41435a7afe8 100644
--- a/src/cmd/compile/internal/noder/stencil.go
+++ b/src/cmd/compile/internal/noder/stencil.go
@@ -741,6 +741,7 @@ func (g *genInst) genericSubst(newsym *types.Sym, nameNode *ir.Name, tparams []*
// Pos of the instantiated function is same as the generic function
newf := ir.NewFunc(gf.Pos())
newf.Pragma = gf.Pragma // copy over pragmas from generic function to stenciled implementation.
+ newf.Endlineno = gf.Endlineno
newf.Nname = ir.NewNameAt(gf.Pos(), newsym)
newf.Nname.Func = newf
newf.Nname.Defn = newf
diff --git a/src/cmd/compile/internal/noder/unified.go b/src/cmd/compile/internal/noder/unified.go
index 2c1f2362ad9..e7a4001cec2 100644
--- a/src/cmd/compile/internal/noder/unified.go
+++ b/src/cmd/compile/internal/noder/unified.go
@@ -33,38 +33,38 @@ var localPkgReader *pkgReader
//
// The pipeline contains 2 steps:
//
-// 1) Generate package export data "stub".
+// 1. Generate package export data "stub".
//
-// 2) Generate package IR from package export data.
+// 2. Generate package IR from package export data.
//
// The package data "stub" at step (1) contains everything from the local package,
// but nothing that have been imported. When we're actually writing out export data
// to the output files (see writeNewExport function), we run the "linker", which does
// a few things:
//
-// + Updates compiler extensions data (e.g., inlining cost, escape analysis results).
+// - Updates compiler extensions data (e.g., inlining cost, escape analysis results).
//
-// + Handles re-exporting any transitive dependencies.
+// - Handles re-exporting any transitive dependencies.
//
-// + Prunes out any unnecessary details (e.g., non-inlineable functions, because any
-// downstream importers only care about inlinable functions).
+// - Prunes out any unnecessary details (e.g., non-inlineable functions, because any
+// downstream importers only care about inlinable functions).
//
// The source files are typechecked twice, once before writing export data
// using types2 checker, once after read export data using gc/typecheck.
// This duplication of work will go away once we always use types2 checker,
// we can remove the gc/typecheck pass. The reason it is still here:
//
-// + It reduces engineering costs in maintaining a fork of typecheck
-// (e.g., no need to backport fixes like CL 327651).
+// - It reduces engineering costs in maintaining a fork of typecheck
+// (e.g., no need to backport fixes like CL 327651).
//
-// + It makes it easier to pass toolstash -cmp.
+// - It makes it easier to pass toolstash -cmp.
//
-// + Historically, we would always re-run the typechecker after import, even though
-// we know the imported data is valid. It's not ideal, but also not causing any
-// problem either.
+// - Historically, we would always re-run the typechecker after import, even though
+// we know the imported data is valid. It's not ideal, but also not causing any
+// problem either.
//
-// + There's still transformation that being done during gc/typecheck, like rewriting
-// multi-valued function call, or transform ir.OINDEX -> ir.OINDEXMAP.
+// - There's still transformation that being done during gc/typecheck, like rewriting
+// multi-valued function call, or transform ir.OINDEX -> ir.OINDEXMAP.
//
// Using syntax+types2 tree, which already has a complete representation of generics,
// the unified IR has the full typed AST for doing introspection during step (1).
diff --git a/src/cmd/compile/internal/pkginit/init.go b/src/cmd/compile/internal/pkginit/init.go
index 40f14082600..32e95bedc23 100644
--- a/src/cmd/compile/internal/pkginit/init.go
+++ b/src/cmd/compile/internal/pkginit/init.go
@@ -65,9 +65,9 @@ func MakeInit() {
// Task makes and returns an initialization record for the package.
// See runtime/proc.go:initTask for its layout.
// The 3 tasks for initialization are:
-// 1) Initialize all of the packages the current package depends on.
-// 2) Initialize all the variables that have initializers.
-// 3) Run any init functions.
+// 1. Initialize all of the packages the current package depends on.
+// 2. Initialize all the variables that have initializers.
+// 3. Run any init functions.
func Task() *ir.Name {
var deps []*obj.LSym // initTask records for packages the current package depends on
var fns []*obj.LSym // functions to call for package initialization
diff --git a/src/cmd/compile/internal/ppc64/ggen.go b/src/cmd/compile/internal/ppc64/ggen.go
index 7877be3336b..4c935cfc71f 100644
--- a/src/cmd/compile/internal/ppc64/ggen.go
+++ b/src/cmd/compile/internal/ppc64/ggen.go
@@ -19,17 +19,17 @@ func zerorange(pp *objw.Progs, p *obj.Prog, off, cnt int64, _ *uint32) *obj.Prog
}
if cnt < int64(4*types.PtrSize) {
for i := int64(0); i < cnt; i += int64(types.PtrSize) {
- p = pp.Append(p, ppc64.AMOVD, obj.TYPE_REG, ppc64.REGZERO, 0, obj.TYPE_MEM, ppc64.REGSP, base.Ctxt.FixedFrameSize()+off+i)
+ p = pp.Append(p, ppc64.AMOVD, obj.TYPE_REG, ppc64.REGZERO, 0, obj.TYPE_MEM, ppc64.REGSP, base.Ctxt.Arch.FixedFrameSize+off+i)
}
} else if cnt <= int64(128*types.PtrSize) {
- p = pp.Append(p, ppc64.AADD, obj.TYPE_CONST, 0, base.Ctxt.FixedFrameSize()+off-8, obj.TYPE_REG, ppc64.REGRT1, 0)
+ p = pp.Append(p, ppc64.AADD, obj.TYPE_CONST, 0, base.Ctxt.Arch.FixedFrameSize+off-8, obj.TYPE_REG, ppc64.REGRT1, 0)
p.Reg = ppc64.REGSP
p = pp.Append(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_MEM, 0, 0)
p.To.Name = obj.NAME_EXTERN
p.To.Sym = ir.Syms.Duffzero
p.To.Offset = 4 * (128 - cnt/int64(types.PtrSize))
} else {
- p = pp.Append(p, ppc64.AMOVD, obj.TYPE_CONST, 0, base.Ctxt.FixedFrameSize()+off-8, obj.TYPE_REG, ppc64.REGTMP, 0)
+ p = pp.Append(p, ppc64.AMOVD, obj.TYPE_CONST, 0, base.Ctxt.Arch.FixedFrameSize+off-8, obj.TYPE_REG, ppc64.REGTMP, 0)
p = pp.Append(p, ppc64.AADD, obj.TYPE_REG, ppc64.REGTMP, 0, obj.TYPE_REG, ppc64.REGRT1, 0)
p.Reg = ppc64.REGSP
p = pp.Append(p, ppc64.AMOVD, obj.TYPE_CONST, 0, cnt, obj.TYPE_REG, ppc64.REGTMP, 0)
diff --git a/src/cmd/compile/internal/ppc64/ssa.go b/src/cmd/compile/internal/ppc64/ssa.go
index da74cacd95f..8689bd8b27e 100644
--- a/src/cmd/compile/internal/ppc64/ssa.go
+++ b/src/cmd/compile/internal/ppc64/ssa.go
@@ -476,7 +476,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
// caller's SP is FixedFrameSize below the address of the first arg
p := s.Prog(ppc64.AMOVD)
p.From.Type = obj.TYPE_ADDR
- p.From.Offset = -base.Ctxt.FixedFrameSize()
+ p.From.Offset = -base.Ctxt.Arch.FixedFrameSize
p.From.Name = obj.NAME_PARAM
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
@@ -509,7 +509,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
for _, a := range v.Block.Func.RegArgs {
// Pass the spill/unspill information along to the assembler, offset by size of
// the saved LR slot.
- addr := ssagen.SpillSlotAddr(a, ppc64.REGSP, base.Ctxt.FixedFrameSize())
+ addr := ssagen.SpillSlotAddr(a, ppc64.REGSP, base.Ctxt.Arch.FixedFrameSize)
s.FuncInfo().AddSpill(
obj.RegSpill{Reg: a.Reg, Addr: addr, Unspill: loadByType(a.Type), Spill: storeByType(a.Type)})
}
diff --git a/src/cmd/compile/internal/reflectdata/alg.go b/src/cmd/compile/internal/reflectdata/alg.go
index d000618bd64..9fe90da0fe5 100644
--- a/src/cmd/compile/internal/reflectdata/alg.go
+++ b/src/cmd/compile/internal/reflectdata/alg.go
@@ -412,22 +412,25 @@ func geneq(t *types.Type) *obj.LSym {
//
// if eq(p[0], q[0]) && eq(p[1], q[1]) && ... {
// } else {
- // return
+ // goto neq
// }
//
// And so on.
//
// Otherwise it generates:
//
- // for i := 0; i < nelem; i++ {
- // if eq(p[i], q[i]) {
+ // iterateTo := nelem/unroll*unroll
+ // for i := 0; i < iterateTo; i += unroll {
+ // if eq(p[i+0], q[i+0]) && eq(p[i+1], q[i+1]) && ... && eq(p[i+unroll-1], q[i+unroll-1]) {
// } else {
// goto neq
// }
// }
+ // if eq(p[iterateTo+0], q[iterateTo+0]) && eq(p[iterateTo+1], q[iterateTo+1]) && ... {
+ // } else {
+ // goto neq
+ // }
//
- // TODO(josharian): consider doing some loop unrolling
- // for larger nelem as well, processing a few elements at a time in a loop.
checkAll := func(unroll int64, last bool, eq func(pi, qi ir.Node) ir.Node) {
// checkIdx generates a node to check for equality at index i.
checkIdx := func(i ir.Node) ir.Node {
@@ -442,46 +445,69 @@ func geneq(t *types.Type) *obj.LSym {
return eq(pi, qi)
}
- if nelem <= unroll {
- if last {
- // Do last comparison in a different manner.
- nelem--
- }
- // Generate a series of checks.
- for i := int64(0); i < nelem; i++ {
- // if check {} else { goto neq }
- nif := ir.NewIfStmt(base.Pos, checkIdx(ir.NewInt(i)), nil, nil)
- nif.Else.Append(ir.NewBranchStmt(base.Pos, ir.OGOTO, neq))
- fn.Body.Append(nif)
- }
- if last {
- fn.Body.Append(ir.NewAssignStmt(base.Pos, nr, checkIdx(ir.NewInt(nelem))))
- }
- } else {
- // Generate a for loop.
- // for i := 0; i < nelem; i++
+ iterations := nelem / unroll
+ iterateTo := iterations * unroll
+ // If a loop is iterated only once, there shouldn't be any loop at all.
+ if iterations == 1 {
+ iterateTo = 0
+ }
+
+ if iterateTo > 0 {
+ // Generate an unrolled for loop.
+ // for i := 0; i < nelem/unroll*unroll; i += unroll
i := typecheck.Temp(types.Types[types.TINT])
init := ir.NewAssignStmt(base.Pos, i, ir.NewInt(0))
- cond := ir.NewBinaryExpr(base.Pos, ir.OLT, i, ir.NewInt(nelem))
- post := ir.NewAssignStmt(base.Pos, i, ir.NewBinaryExpr(base.Pos, ir.OADD, i, ir.NewInt(1)))
- loop := ir.NewForStmt(base.Pos, nil, cond, post, nil)
+ cond := ir.NewBinaryExpr(base.Pos, ir.OLT, i, ir.NewInt(iterateTo))
+ loop := ir.NewForStmt(base.Pos, nil, cond, nil, nil)
loop.PtrInit().Append(init)
- // if eq(pi, qi) {} else { goto neq }
- nif := ir.NewIfStmt(base.Pos, checkIdx(i), nil, nil)
- nif.Else.Append(ir.NewBranchStmt(base.Pos, ir.OGOTO, neq))
- loop.Body.Append(nif)
+
+ // if eq(p[i+0], q[i+0]) && eq(p[i+1], q[i+1]) && ... && eq(p[i+unroll-1], q[i+unroll-1]) {
+ // } else {
+ // goto neq
+ // }
+ for j := int64(0); j < unroll; j++ {
+ // if check {} else { goto neq }
+ nif := ir.NewIfStmt(base.Pos, checkIdx(i), nil, nil)
+ nif.Else.Append(ir.NewBranchStmt(base.Pos, ir.OGOTO, neq))
+ loop.Body.Append(nif)
+ post := ir.NewAssignStmt(base.Pos, i, ir.NewBinaryExpr(base.Pos, ir.OADD, i, ir.NewInt(1)))
+ loop.Body.Append(post)
+ }
+
fn.Body.Append(loop)
- if last {
- fn.Body.Append(ir.NewAssignStmt(base.Pos, nr, ir.NewBool(true)))
+
+ if nelem == iterateTo {
+ if last {
+ fn.Body.Append(ir.NewAssignStmt(base.Pos, nr, ir.NewBool(true)))
+ }
+ return
}
}
+
+ // Generate remaining checks, if nelem is not a multiple of unroll.
+ if last {
+ // Do last comparison in a different manner.
+ nelem--
+ }
+ // if eq(p[iterateTo+0], q[iterateTo+0]) && eq(p[iterateTo+1], q[iterateTo+1]) && ... {
+ // } else {
+ // goto neq
+ // }
+ for j := iterateTo; j < nelem; j++ {
+ // if check {} else { goto neq }
+ nif := ir.NewIfStmt(base.Pos, checkIdx(ir.NewInt(j)), nil, nil)
+ nif.Else.Append(ir.NewBranchStmt(base.Pos, ir.OGOTO, neq))
+ fn.Body.Append(nif)
+ }
+ if last {
+ fn.Body.Append(ir.NewAssignStmt(base.Pos, nr, checkIdx(ir.NewInt(nelem))))
+ }
}
switch t.Elem().Kind() {
case types.TSTRING:
// Do two loops. First, check that all the lengths match (cheap).
// Second, check that all the contents match (expensive).
- // TODO: when the array size is small, unroll the length match checks.
checkAll(3, false, func(pi, qi ir.Node) ir.Node {
// Compare lengths.
eqlen, _ := EqString(pi, qi)
@@ -655,7 +681,8 @@ func anyCall(fn *ir.Func) bool {
}
// eqfield returns the node
-// p.field == q.field
+//
+// p.field == q.field
func eqfield(p ir.Node, q ir.Node, field *types.Sym) ir.Node {
nx := ir.NewSelectorExpr(base.Pos, ir.OXDOT, p, field)
ny := ir.NewSelectorExpr(base.Pos, ir.OXDOT, q, field)
@@ -664,9 +691,13 @@ func eqfield(p ir.Node, q ir.Node, field *types.Sym) ir.Node {
}
// EqString returns the nodes
-// len(s) == len(t)
+//
+// len(s) == len(t)
+//
// and
-// memequal(s.ptr, t.ptr, len(s))
+//
+// memequal(s.ptr, t.ptr, len(s))
+//
// which can be used to construct string equality comparison.
// eqlen must be evaluated before eqmem, and shortcircuiting is required.
func EqString(s, t ir.Node) (eqlen *ir.BinaryExpr, eqmem *ir.CallExpr) {
@@ -688,9 +719,13 @@ func EqString(s, t ir.Node) (eqlen *ir.BinaryExpr, eqmem *ir.CallExpr) {
}
// EqInterface returns the nodes
-// s.tab == t.tab (or s.typ == t.typ, as appropriate)
+//
+// s.tab == t.tab (or s.typ == t.typ, as appropriate)
+//
// and
-// ifaceeq(s.tab, s.data, t.data) (or efaceeq(s.typ, s.data, t.data), as appropriate)
+//
+// ifaceeq(s.tab, s.data, t.data) (or efaceeq(s.typ, s.data, t.data), as appropriate)
+//
// which can be used to construct interface equality comparison.
// eqtab must be evaluated before eqdata, and shortcircuiting is required.
func EqInterface(s, t ir.Node) (eqtab *ir.BinaryExpr, eqdata *ir.CallExpr) {
@@ -724,7 +759,8 @@ func EqInterface(s, t ir.Node) (eqtab *ir.BinaryExpr, eqdata *ir.CallExpr) {
}
// eqmem returns the node
-// memequal(&p.field, &q.field [, size])
+//
+// memequal(&p.field, &q.field [, size])
func eqmem(p ir.Node, q ir.Node, field *types.Sym, size int64) ir.Node {
nx := typecheck.Expr(typecheck.NodAddr(ir.NewSelectorExpr(base.Pos, ir.OXDOT, p, field)))
ny := typecheck.Expr(typecheck.NodAddr(ir.NewSelectorExpr(base.Pos, ir.OXDOT, q, field)))
diff --git a/src/cmd/compile/internal/reflectdata/alg_test.go b/src/cmd/compile/internal/reflectdata/alg_test.go
new file mode 100644
index 00000000000..1e57b913fda
--- /dev/null
+++ b/src/cmd/compile/internal/reflectdata/alg_test.go
@@ -0,0 +1,76 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package reflectdata_test
+
+import "testing"
+
+func BenchmarkEqArrayOfStrings5(b *testing.B) {
+ var a [5]string
+ var c [5]string
+
+ for i := 0; i < 5; i++ {
+ a[i] = "aaaa"
+ c[i] = "cccc"
+ }
+
+ for j := 0; j < b.N; j++ {
+ _ = a == c
+ }
+}
+
+func BenchmarkEqArrayOfStrings64(b *testing.B) {
+ var a [64]string
+ var c [64]string
+
+ for i := 0; i < 64; i++ {
+ a[i] = "aaaa"
+ c[i] = "cccc"
+ }
+
+ for j := 0; j < b.N; j++ {
+ _ = a == c
+ }
+}
+
+func BenchmarkEqArrayOfStrings1024(b *testing.B) {
+ var a [1024]string
+ var c [1024]string
+
+ for i := 0; i < 1024; i++ {
+ a[i] = "aaaa"
+ c[i] = "cccc"
+ }
+
+ for j := 0; j < b.N; j++ {
+ _ = a == c
+ }
+}
+
+func BenchmarkEqArrayOfFloats5(b *testing.B) {
+ var a [5]float32
+ var c [5]float32
+
+ for i := 0; i < b.N; i++ {
+ _ = a == c
+ }
+}
+
+func BenchmarkEqArrayOfFloats64(b *testing.B) {
+ var a [64]float32
+ var c [64]float32
+
+ for i := 0; i < b.N; i++ {
+ _ = a == c
+ }
+}
+
+func BenchmarkEqArrayOfFloats1024(b *testing.B) {
+ var a [1024]float32
+ var c [1024]float32
+
+ for i := 0; i < b.N; i++ {
+ _ = a == c
+ }
+}
diff --git a/src/cmd/compile/internal/reflectdata/reflect.go b/src/cmd/compile/internal/reflectdata/reflect.go
index 11371016501..affc6799abb 100644
--- a/src/cmd/compile/internal/reflectdata/reflect.go
+++ b/src/cmd/compile/internal/reflectdata/reflect.go
@@ -667,10 +667,10 @@ var kinds = []int{
// tflag is documented in reflect/type.go.
//
// tflag values must be kept in sync with copies in:
-// - cmd/compile/internal/reflectdata/reflect.go
-// - cmd/link/internal/ld/decodesym.go
-// - reflect/type.go
-// - runtime/type.go
+// - cmd/compile/internal/reflectdata/reflect.go
+// - cmd/link/internal/ld/decodesym.go
+// - reflect/type.go
+// - runtime/type.go
const (
tflagUncommon = 1 << 0
tflagExtraStar = 1 << 1
@@ -1355,21 +1355,21 @@ func writeITab(lsym *obj.LSym, typ, iface *types.Type, allowNonImplement bool) {
// type itab struct {
// inter *interfacetype
// _type *_type
- // hash uint32
+ // hash uint32 // copy of _type.hash. Used for type switches.
// _ [4]byte
- // fun [1]uintptr // variable sized
+ // fun [1]uintptr // variable sized. fun[0]==0 means _type does not implement inter.
// }
o := objw.SymPtr(lsym, 0, writeType(iface), 0)
o = objw.SymPtr(lsym, o, writeType(typ), 0)
o = objw.Uint32(lsym, o, types.TypeHash(typ)) // copy of type hash
o += 4 // skip unused field
+ if !completeItab {
+ // If typ doesn't implement iface, make method entries be zero.
+ o = objw.Uintptr(lsym, o, 0)
+ entries = entries[:0]
+ }
for _, fn := range entries {
- if !completeItab {
- // If typ doesn't implement iface, make method entries be zero.
- o = objw.Uintptr(lsym, o, 0)
- } else {
- o = objw.SymPtrWeak(lsym, o, fn, 0) // method pointer for each method
- }
+ o = objw.SymPtrWeak(lsym, o, fn, 0) // method pointer for each method
}
// Nothing writes static itabs, so they are read only.
objw.Global(lsym, int32(o), int16(obj.DUPOK|obj.RODATA))
@@ -1821,13 +1821,17 @@ func NeedEmit(typ *types.Type) bool {
// Also wraps methods on instantiated generic types for use in itab entries.
// For an instantiated generic type G[int], we generate wrappers like:
// G[int] pointer shaped:
+//
// func (x G[int]) f(arg) {
// .inst.G[int].f(dictionary, x, arg)
-// }
+// }
+//
// G[int] not pointer shaped:
+//
// func (x *G[int]) f(arg) {
// .inst.G[int].f(dictionary, *x, arg)
-// }
+// }
+//
// These wrappers are always fully stenciled.
func methodWrapper(rcvr *types.Type, method *types.Field, forItab bool) *obj.LSym {
orig := rcvr
diff --git a/src/cmd/compile/internal/riscv64/ggen.go b/src/cmd/compile/internal/riscv64/ggen.go
index 0f37f65fcf3..44488e43276 100644
--- a/src/cmd/compile/internal/riscv64/ggen.go
+++ b/src/cmd/compile/internal/riscv64/ggen.go
@@ -19,7 +19,7 @@ func zeroRange(pp *objw.Progs, p *obj.Prog, off, cnt int64, _ *uint32) *obj.Prog
}
// Adjust the frame to account for LR.
- off += base.Ctxt.FixedFrameSize()
+ off += base.Ctxt.Arch.FixedFrameSize
if cnt < int64(4*types.PtrSize) {
for i := int64(0); i < cnt; i += int64(types.PtrSize) {
diff --git a/src/cmd/compile/internal/riscv64/ssa.go b/src/cmd/compile/internal/riscv64/ssa.go
index b6e6dc1a03d..5f74fd876cf 100644
--- a/src/cmd/compile/internal/riscv64/ssa.go
+++ b/src/cmd/compile/internal/riscv64/ssa.go
@@ -237,7 +237,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
for _, a := range v.Block.Func.RegArgs {
// Pass the spill/unspill information along to the assembler, offset by size of
// the saved LR slot.
- addr := ssagen.SpillSlotAddr(a, riscv.REG_SP, base.Ctxt.FixedFrameSize())
+ addr := ssagen.SpillSlotAddr(a, riscv.REG_SP, base.Ctxt.Arch.FixedFrameSize)
s.FuncInfo().AddSpill(
obj.RegSpill{Reg: a.Reg, Addr: addr, Unspill: loadByType(a.Type), Spill: storeByType(a.Type)})
}
@@ -669,7 +669,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
// caller's SP is FixedFrameSize below the address of the first arg
p := s.Prog(riscv.AMOV)
p.From.Type = obj.TYPE_ADDR
- p.From.Offset = -base.Ctxt.FixedFrameSize()
+ p.From.Offset = -base.Ctxt.Arch.FixedFrameSize
p.From.Name = obj.NAME_PARAM
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
diff --git a/src/cmd/compile/internal/s390x/ggen.go b/src/cmd/compile/internal/s390x/ggen.go
index 488a080c468..70e40312248 100644
--- a/src/cmd/compile/internal/s390x/ggen.go
+++ b/src/cmd/compile/internal/s390x/ggen.go
@@ -24,7 +24,7 @@ func zerorange(pp *objw.Progs, p *obj.Prog, off, cnt int64, _ *uint32) *obj.Prog
}
// Adjust the frame to account for LR.
- off += base.Ctxt.FixedFrameSize()
+ off += base.Ctxt.Arch.FixedFrameSize
reg := int16(s390x.REGSP)
// If the off cannot fit in a 12-bit unsigned displacement then we
diff --git a/src/cmd/compile/internal/s390x/ssa.go b/src/cmd/compile/internal/s390x/ssa.go
index deb6c790069..7d9b31de4c2 100644
--- a/src/cmd/compile/internal/s390x/ssa.go
+++ b/src/cmd/compile/internal/s390x/ssa.go
@@ -132,7 +132,9 @@ func moveByType(t *types.Type) obj.As {
}
// opregreg emits instructions for
-// dest := dest(To) op src(From)
+//
+// dest := dest(To) op src(From)
+//
// and also returns the created obj.Prog so it
// may be further adjusted (offset, scale, etc).
func opregreg(s *ssagen.State, op obj.As, dest, src int16) *obj.Prog {
@@ -145,7 +147,9 @@ func opregreg(s *ssagen.State, op obj.As, dest, src int16) *obj.Prog {
}
// opregregimm emits instructions for
+//
// dest := src(From) op off
+//
// and also returns the created obj.Prog so it
// may be further adjusted (offset, scale, etc).
func opregregimm(s *ssagen.State, op obj.As, dest, src int16, off int64) *obj.Prog {
@@ -546,7 +550,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
// caller's SP is FixedFrameSize below the address of the first arg
p := s.Prog(s390x.AMOVD)
p.From.Type = obj.TYPE_ADDR
- p.From.Offset = -base.Ctxt.FixedFrameSize()
+ p.From.Offset = -base.Ctxt.Arch.FixedFrameSize
p.From.Name = obj.NAME_PARAM
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
diff --git a/src/cmd/compile/internal/ssa/addressingmodes.go b/src/cmd/compile/internal/ssa/addressingmodes.go
index 28fa86cd643..469ba0d4947 100644
--- a/src/cmd/compile/internal/ssa/addressingmodes.go
+++ b/src/cmd/compile/internal/ssa/addressingmodes.go
@@ -131,10 +131,14 @@ var needSplit = map[Op]bool{
}
// For each entry k, v in this map, if we have a value x with:
-// x.Op == k[0]
-// x.Args[0].Op == k[1]
+//
+// x.Op == k[0]
+// x.Args[0].Op == k[1]
+//
// then we can set x.Op to v and set x.Args like this:
-// x.Args[0].Args + x.Args[1:]
+//
+// x.Args[0].Args + x.Args[1:]
+//
// Additionally, the Aux/AuxInt from x.Args[0] is merged into x.
var combine = map[[2]Op]Op{
// amd64
@@ -340,11 +344,18 @@ var combine = map[[2]Op]Op{
[2]Op{OpAMD64DIVSDload, OpAMD64LEAQ1}: OpAMD64DIVSDloadidx1,
[2]Op{OpAMD64DIVSDload, OpAMD64LEAQ8}: OpAMD64DIVSDloadidx8,
+ [2]Op{OpAMD64SARXLload, OpAMD64ADDQ}: OpAMD64SARXLloadidx1,
+ [2]Op{OpAMD64SARXQload, OpAMD64ADDQ}: OpAMD64SARXQloadidx1,
[2]Op{OpAMD64SHLXLload, OpAMD64ADDQ}: OpAMD64SHLXLloadidx1,
[2]Op{OpAMD64SHLXQload, OpAMD64ADDQ}: OpAMD64SHLXQloadidx1,
[2]Op{OpAMD64SHRXLload, OpAMD64ADDQ}: OpAMD64SHRXLloadidx1,
[2]Op{OpAMD64SHRXQload, OpAMD64ADDQ}: OpAMD64SHRXQloadidx1,
+ [2]Op{OpAMD64SARXLload, OpAMD64LEAQ1}: OpAMD64SARXLloadidx1,
+ [2]Op{OpAMD64SARXLload, OpAMD64LEAQ4}: OpAMD64SARXLloadidx4,
+ [2]Op{OpAMD64SARXLload, OpAMD64LEAQ8}: OpAMD64SARXLloadidx8,
+ [2]Op{OpAMD64SARXQload, OpAMD64LEAQ1}: OpAMD64SARXQloadidx1,
+ [2]Op{OpAMD64SARXQload, OpAMD64LEAQ8}: OpAMD64SARXQloadidx8,
[2]Op{OpAMD64SHLXLload, OpAMD64LEAQ1}: OpAMD64SHLXLloadidx1,
[2]Op{OpAMD64SHLXLload, OpAMD64LEAQ4}: OpAMD64SHLXLloadidx4,
[2]Op{OpAMD64SHLXLload, OpAMD64LEAQ8}: OpAMD64SHLXLloadidx8,
@@ -356,6 +367,26 @@ var combine = map[[2]Op]Op{
[2]Op{OpAMD64SHRXQload, OpAMD64LEAQ1}: OpAMD64SHRXQloadidx1,
[2]Op{OpAMD64SHRXQload, OpAMD64LEAQ8}: OpAMD64SHRXQloadidx8,
+ // amd64/v3
+ [2]Op{OpAMD64MOVBELload, OpAMD64ADDQ}: OpAMD64MOVBELloadidx1,
+ [2]Op{OpAMD64MOVBEQload, OpAMD64ADDQ}: OpAMD64MOVBEQloadidx1,
+ [2]Op{OpAMD64MOVBELload, OpAMD64LEAQ1}: OpAMD64MOVBELloadidx1,
+ [2]Op{OpAMD64MOVBELload, OpAMD64LEAQ4}: OpAMD64MOVBELloadidx4,
+ [2]Op{OpAMD64MOVBELload, OpAMD64LEAQ8}: OpAMD64MOVBELloadidx8,
+ [2]Op{OpAMD64MOVBEQload, OpAMD64LEAQ1}: OpAMD64MOVBEQloadidx1,
+ [2]Op{OpAMD64MOVBEQload, OpAMD64LEAQ8}: OpAMD64MOVBEQloadidx8,
+
+ [2]Op{OpAMD64MOVBEWstore, OpAMD64ADDQ}: OpAMD64MOVBEWstoreidx1,
+ [2]Op{OpAMD64MOVBELstore, OpAMD64ADDQ}: OpAMD64MOVBELstoreidx1,
+ [2]Op{OpAMD64MOVBEQstore, OpAMD64ADDQ}: OpAMD64MOVBEQstoreidx1,
+ [2]Op{OpAMD64MOVBEWstore, OpAMD64LEAQ1}: OpAMD64MOVBEWstoreidx1,
+ [2]Op{OpAMD64MOVBEWstore, OpAMD64LEAQ2}: OpAMD64MOVBEWstoreidx2,
+ [2]Op{OpAMD64MOVBELstore, OpAMD64LEAQ1}: OpAMD64MOVBELstoreidx1,
+ [2]Op{OpAMD64MOVBELstore, OpAMD64LEAQ4}: OpAMD64MOVBELstoreidx4,
+ [2]Op{OpAMD64MOVBELstore, OpAMD64LEAQ8}: OpAMD64MOVBELstoreidx8,
+ [2]Op{OpAMD64MOVBEQstore, OpAMD64LEAQ1}: OpAMD64MOVBEQstoreidx1,
+ [2]Op{OpAMD64MOVBEQstore, OpAMD64LEAQ8}: OpAMD64MOVBEQstoreidx8,
+
// 386
[2]Op{Op386MOVBload, Op386ADDL}: Op386MOVBloadidx1,
[2]Op{Op386MOVWload, Op386ADDL}: Op386MOVWloadidx1,
diff --git a/src/cmd/compile/internal/ssa/block.go b/src/cmd/compile/internal/ssa/block.go
index 4d21ade3e3b..db7df3f3384 100644
--- a/src/cmd/compile/internal/ssa/block.go
+++ b/src/cmd/compile/internal/ssa/block.go
@@ -71,19 +71,25 @@ type Block struct {
// Edge represents a CFG edge.
// Example edges for b branching to either c or d.
// (c and d have other predecessors.)
-// b.Succs = [{c,3}, {d,1}]
-// c.Preds = [?, ?, ?, {b,0}]
-// d.Preds = [?, {b,1}, ?]
+//
+// b.Succs = [{c,3}, {d,1}]
+// c.Preds = [?, ?, ?, {b,0}]
+// d.Preds = [?, {b,1}, ?]
+//
// These indexes allow us to edit the CFG in constant time.
// In addition, it informs phi ops in degenerate cases like:
-// b:
-// if k then c else c
-// c:
-// v = Phi(x, y)
+//
+// b:
+// if k then c else c
+// c:
+// v = Phi(x, y)
+//
// Then the indexes tell you whether x is chosen from
// the if or else branch from b.
-// b.Succs = [{c,0},{c,1}]
-// c.Preds = [{b,0},{b,1}]
+//
+// b.Succs = [{c,0},{c,1}]
+// c.Preds = [{b,0},{b,1}]
+//
// means x is chosen if k is true.
type Edge struct {
// block edge goes to (in a Succs list) or from (in a Preds list)
@@ -106,12 +112,13 @@ func (e Edge) String() string {
}
// BlockKind is the kind of SSA block.
-// kind controls successors
-// ------------------------------------------
-// Exit [return mem] []
-// Plain [] [next]
-// If [boolean Value] [then, else]
-// Defer [mem] [nopanic, panic] (control opcode should be OpStaticCall to runtime.deferproc)
+//
+// kind controls successors
+// ------------------------------------------
+// Exit [return mem] []
+// Plain [] [next]
+// If [boolean Value] [then, else]
+// Defer [mem] [nopanic, panic] (control opcode should be OpStaticCall to runtime.deferproc)
type BlockKind int8
// short form print
@@ -330,10 +337,12 @@ func (b *Block) swapSuccessors() {
//
// b.removePred(i)
// for _, v := range b.Values {
-// if v.Op != OpPhi {
-// continue
-// }
-// b.removeArg(v, i)
+//
+// if v.Op != OpPhi {
+// continue
+// }
+// b.removeArg(v, i)
+//
// }
func (b *Block) removePhiArg(phi *Value, i int) {
n := len(b.Preds)
diff --git a/src/cmd/compile/internal/ssa/branchelim.go b/src/cmd/compile/internal/ssa/branchelim.go
index 59773ef31b9..7a08654f4e1 100644
--- a/src/cmd/compile/internal/ssa/branchelim.go
+++ b/src/cmd/compile/internal/ssa/branchelim.go
@@ -11,11 +11,11 @@ import "cmd/internal/src"
//
// Search for basic blocks that look like
//
-// bb0 bb0
-// | \ / \
-// | bb1 or bb1 bb2 <- trivial if/else blocks
-// | / \ /
-// bb2 bb3
+// bb0 bb0
+// | \ / \
+// | bb1 or bb1 bb2 <- trivial if/else blocks
+// | / \ /
+// bb2 bb3
//
// where the intermediate blocks are mostly empty (with no side-effects);
// rewrite Phis in the postdominator as CondSelects.
diff --git a/src/cmd/compile/internal/ssa/check.go b/src/cmd/compile/internal/ssa/check.go
index 28edfd2237d..df677e674a0 100644
--- a/src/cmd/compile/internal/ssa/check.go
+++ b/src/cmd/compile/internal/ssa/check.go
@@ -100,6 +100,10 @@ func checkFunc(f *Func) {
if b.NumControls() != 0 {
f.Fatalf("plain/dead block %s has a control value", b)
}
+ case BlockJumpTable:
+ if b.NumControls() != 1 {
+ f.Fatalf("jumpTable block %s has no control value", b)
+ }
}
if len(b.Succs) != 2 && b.Likely != BranchUnknown {
f.Fatalf("likeliness prediction %d for block %s with %d successors", b.Likely, b, len(b.Succs))
diff --git a/src/cmd/compile/internal/ssa/compile.go b/src/cmd/compile/internal/ssa/compile.go
index f95140eaf9e..5e898ab96f4 100644
--- a/src/cmd/compile/internal/ssa/compile.go
+++ b/src/cmd/compile/internal/ssa/compile.go
@@ -250,8 +250,8 @@ var GenssaDump map[string]bool = make(map[string]bool) // names of functions to
// version is used as a regular expression to match the phase name(s).
//
// Special cases that have turned out to be useful:
-// - ssa/check/on enables checking after each phase
-// - ssa/all/time enables time reporting for all phases
+// - ssa/check/on enables checking after each phase
+// - ssa/all/time enables time reporting for all phases
//
// See gc/lex.go for dissection of the option string.
// Example uses:
diff --git a/src/cmd/compile/internal/ssa/config.go b/src/cmd/compile/internal/ssa/config.go
index b9c98bdba90..931ef454fca 100644
--- a/src/cmd/compile/internal/ssa/config.go
+++ b/src/cmd/compile/internal/ssa/config.go
@@ -168,6 +168,9 @@ type Frontend interface {
// MyImportPath provides the import name (roughly, the package) for the function being compiled.
MyImportPath() string
+
+ // LSym returns the linker symbol of the function being compiled.
+ LSym() string
}
// NewConfig returns a new configuration object for the given architecture.
@@ -297,8 +300,8 @@ func NewConfig(arch string, types Types, ctxt *obj.Link, optimize, softfloat boo
c.registers = registersRISCV64[:]
c.gpRegMask = gpRegMaskRISCV64
c.fpRegMask = fpRegMaskRISCV64
- // c.intParamRegs = paramIntRegRISCV64
- // c.floatParamRegs = paramFloatRegRISCV64
+ c.intParamRegs = paramIntRegRISCV64
+ c.floatParamRegs = paramFloatRegRISCV64
c.FPReg = framepointerRegRISCV64
c.hasGReg = true
case "wasm":
@@ -329,8 +332,8 @@ func NewConfig(arch string, types Types, ctxt *obj.Link, optimize, softfloat boo
c.floatParamRegs = nil // no FP registers in softfloat mode
}
- c.ABI0 = abi.NewABIConfig(0, 0, ctxt.FixedFrameSize())
- c.ABI1 = abi.NewABIConfig(len(c.intParamRegs), len(c.floatParamRegs), ctxt.FixedFrameSize())
+ c.ABI0 = abi.NewABIConfig(0, 0, ctxt.Arch.FixedFrameSize)
+ c.ABI1 = abi.NewABIConfig(len(c.intParamRegs), len(c.floatParamRegs), ctxt.Arch.FixedFrameSize)
// On Plan 9, floating point operations are not allowed in note handler.
if buildcfg.GOOS == "plan9" {
diff --git a/src/cmd/compile/internal/ssa/cse.go b/src/cmd/compile/internal/ssa/cse.go
index ade5e0648e7..f4b799394c0 100644
--- a/src/cmd/compile/internal/ssa/cse.go
+++ b/src/cmd/compile/internal/ssa/cse.go
@@ -235,14 +235,15 @@ type eqclass []*Value
// partitionValues partitions the values into equivalence classes
// based on having all the following features match:
-// - opcode
-// - type
-// - auxint
-// - aux
-// - nargs
-// - block # if a phi op
-// - first two arg's opcodes and auxint
-// - NOT first two arg's aux; that can break CSE.
+// - opcode
+// - type
+// - auxint
+// - aux
+// - nargs
+// - block # if a phi op
+// - first two arg's opcodes and auxint
+// - NOT first two arg's aux; that can break CSE.
+//
// partitionValues returns a list of equivalence classes, each
// being a sorted by ID list of *Values. The eqclass slices are
// backed by the same storage as the input slice.
diff --git a/src/cmd/compile/internal/ssa/debug.go b/src/cmd/compile/internal/ssa/debug.go
index 08dc5c468ec..2c18d352048 100644
--- a/src/cmd/compile/internal/ssa/debug.go
+++ b/src/cmd/compile/internal/ssa/debug.go
@@ -402,28 +402,28 @@ func (sc *slotCanonicalizer) canonSlot(idx SlKeyIdx) LocalSlot {
// OpArg{Int,Float}Reg values, inserting additional values in
// cases where they are missing. Example:
//
-// func foo(s string, used int, notused int) int {
-// return len(s) + used
-// }
+// func foo(s string, used int, notused int) int {
+// return len(s) + used
+// }
//
// In the function above, the incoming parameter "used" is fully live,
// "notused" is not live, and "s" is partially live (only the length
// field of the string is used). At the point where debug value
// analysis runs, we might expect to see an entry block with:
//
-// b1:
-// v4 = ArgIntReg <uintptr> {s+8} [0] : BX
-// v5 = ArgIntReg <int> {used} [0] : CX
+// b1:
+// v4 = ArgIntReg <uintptr> {s+8} [0] : BX
+// v5 = ArgIntReg <int> {used} [0] : CX
//
// While this is an accurate picture of the live incoming params,
// we also want to have debug locations for non-live params (or
// their non-live pieces), e.g. something like
//
-// b1:
-// v9 = ArgIntReg <*uint8> {s+0} [0] : AX
-// v4 = ArgIntReg <uintptr> {s+8} [0] : BX
-// v5 = ArgIntReg <int> {used} [0] : CX
-// v10 = ArgIntReg <int> {unused} [0] : DI
+// b1:
+// v9 = ArgIntReg <*uint8> {s+0} [0] : AX
+// v4 = ArgIntReg <uintptr> {s+8} [0] : BX
+// v5 = ArgIntReg <int> {used} [0] : CX
+// v10 = ArgIntReg <int> {unused} [0] : DI
//
// This function examines the live OpArg{Int,Float}Reg values and
// synthesizes new (dead) values for the non-live params or the
@@ -1489,14 +1489,14 @@ func setupLocList(ctxt *obj.Link, f *Func, list []byte, st, en ID) ([]byte, int)
// that spills a register arg. It returns the ID of that instruction
// Example:
//
-// b1:
-// v3 = ArgIntReg <int> {p1+0} [0] : AX
-// ... more arg regs ..
-// v4 = ArgFloatReg <float32> {f1+0} [0] : X0
-// v52 = MOVQstore <mem> {p1} v2 v3 v1
-// ... more stores ...
-// v68 = MOVSSstore <mem> {f4} v2 v67 v66
-// v38 = MOVQstoreconst <mem> {blob} [val=0,off=0] v2 v32
+// b1:
+// v3 = ArgIntReg <int> {p1+0} [0] : AX
+// ... more arg regs ..
+// v4 = ArgFloatReg <float32> {f1+0} [0] : X0
+// v52 = MOVQstore <mem> {p1} v2 v3 v1
+// ... more stores ...
+// v68 = MOVSSstore <mem> {f4} v2 v67 v66
+// v38 = MOVQstoreconst <mem> {blob} [val=0,off=0] v2 v32
//
// Important: locatePrologEnd is expected to work properly only with
// optimization turned off (e.g. "-N"). If optimization is enabled
diff --git a/src/cmd/compile/internal/ssa/debug_test.go b/src/cmd/compile/internal/ssa/debug_test.go
index 2fc12557c05..c807863ea65 100644
--- a/src/cmd/compile/internal/ssa/debug_test.go
+++ b/src/cmd/compile/internal/ssa/debug_test.go
@@ -84,7 +84,7 @@ var optimizedLibs = (!strings.Contains(gogcflags, "-N") && !strings.Contains(gog
// "O" is an explicit indication that we expect it to be optimized out.
// For example:
//
-// if len(os.Args) > 1 { //gdb-dbg=(hist/A,cannedInput/A) //dlv-dbg=(hist/A,cannedInput/A)
+// if len(os.Args) > 1 { //gdb-dbg=(hist/A,cannedInput/A) //dlv-dbg=(hist/A,cannedInput/A)
//
// TODO: not implemented for Delve yet, but this is the plan
//
diff --git a/src/cmd/compile/internal/ssa/expand_calls.go b/src/cmd/compile/internal/ssa/expand_calls.go
index a3cea855f2f..90ea2d50403 100644
--- a/src/cmd/compile/internal/ssa/expand_calls.go
+++ b/src/cmd/compile/internal/ssa/expand_calls.go
@@ -656,15 +656,16 @@ outer:
// It decomposes a Load or an Arg into smaller parts and returns the new mem.
// If the type does not match one of the expected aggregate types, it returns nil instead.
// Parameters:
-// pos -- the location of any generated code.
-// b -- the block into which any generated code should normally be placed
-// source -- the value, possibly an aggregate, to be stored.
-// mem -- the mem flowing into this decomposition (loads depend on it, stores updated it)
-// t -- the type of the value to be stored
-// storeOffset -- if the value is stored in memory, it is stored at base (see storeRc) + storeOffset
-// loadRegOffset -- regarding source as a value in registers, the register offset in ABI1. Meaningful only if source is OpArg.
-// storeRc -- storeRC; if the value is stored in registers, this specifies the registers.
-// StoreRc also identifies whether the target is registers or memory, and has the base for the store operation.
+//
+// pos -- the location of any generated code.
+// b -- the block into which any generated code should normally be placed
+// source -- the value, possibly an aggregate, to be stored.
+// mem -- the mem flowing into this decomposition (loads depend on it, stores updated it)
+// t -- the type of the value to be stored
+// storeOffset -- if the value is stored in memory, it is stored at base (see storeRc) + storeOffset
+// loadRegOffset -- regarding source as a value in registers, the register offset in ABI1. Meaningful only if source is OpArg.
+// storeRc -- storeRC; if the value is stored in registers, this specifies the registers.
+// StoreRc also identifies whether the target is registers or memory, and has the base for the store operation.
func (x *expandState) decomposeArg(pos src.XPos, b *Block, source, mem *Value, t *types.Type, storeOffset int64, loadRegOffset Abi1RO, storeRc registerCursor) *Value {
pa := x.prAssignForArg(source)
@@ -777,15 +778,16 @@ func (x *expandState) splitSlotsIntoNames(locs []*LocalSlot, suffix string, off
// It decomposes a Load into smaller parts and returns the new mem.
// If the type does not match one of the expected aggregate types, it returns nil instead.
// Parameters:
-// pos -- the location of any generated code.
-// b -- the block into which any generated code should normally be placed
-// source -- the value, possibly an aggregate, to be stored.
-// mem -- the mem flowing into this decomposition (loads depend on it, stores updated it)
-// t -- the type of the value to be stored
-// storeOffset -- if the value is stored in memory, it is stored at base (see storeRc) + offset
-// loadRegOffset -- regarding source as a value in registers, the register offset in ABI1. Meaningful only if source is OpArg.
-// storeRc -- storeRC; if the value is stored in registers, this specifies the registers.
-// StoreRc also identifies whether the target is registers or memory, and has the base for the store operation.
+//
+// pos -- the location of any generated code.
+// b -- the block into which any generated code should normally be placed
+// source -- the value, possibly an aggregate, to be stored.
+// mem -- the mem flowing into this decomposition (loads depend on it, stores updated it)
+// t -- the type of the value to be stored
+// storeOffset -- if the value is stored in memory, it is stored at base (see storeRc) + offset
+// loadRegOffset -- regarding source as a value in registers, the register offset in ABI1. Meaningful only if source is OpArg.
+// storeRc -- storeRC; if the value is stored in registers, this specifies the registers.
+// StoreRc also identifies whether the target is registers or memory, and has the base for the store operation.
//
// TODO -- this needs cleanup; it just works for SSA-able aggregates, and won't fully generalize to register-args aggregates.
func (x *expandState) decomposeLoad(pos src.XPos, b *Block, source, mem *Value, t *types.Type, storeOffset int64, loadRegOffset Abi1RO, storeRc registerCursor) *Value {
@@ -1106,7 +1108,7 @@ func (x *expandState) rewriteArgs(v *Value, firstArg int) {
a0 := a.Args[0]
if a0.Op == OpLocalAddr {
n := a0.Aux.(*ir.Name)
- if n.Class == ir.PPARAM && n.FrameOffset()+x.f.Config.ctxt.FixedFrameSize() == aOffset {
+ if n.Class == ir.PPARAM && n.FrameOffset()+x.f.Config.ctxt.Arch.FixedFrameSize == aOffset {
continue
}
}
@@ -1127,7 +1129,7 @@ func (x *expandState) rewriteArgs(v *Value, firstArg int) {
// It's common for a tail call passing the same arguments (e.g. method wrapper),
// so this would be a self copy. Detect this and optimize it out.
n := a.Aux.(*ir.Name)
- if n.Class == ir.PPARAM && n.FrameOffset()+x.f.Config.ctxt.FixedFrameSize() == aOffset {
+ if n.Class == ir.PPARAM && n.FrameOffset()+x.f.Config.ctxt.Arch.FixedFrameSize == aOffset {
continue
}
}
diff --git a/src/cmd/compile/internal/ssa/export_test.go b/src/cmd/compile/internal/ssa/export_test.go
index c4e87ec7d0f..87d1b41419c 100644
--- a/src/cmd/compile/internal/ssa/export_test.go
+++ b/src/cmd/compile/internal/ssa/export_test.go
@@ -102,6 +102,9 @@ func (d TestFrontend) Debug_checknil() bool { retu
func (d TestFrontend) MyImportPath() string {
return "my/import/path"
}
+func (d TestFrontend) LSym() string {
+ return "my/import/path.function"
+}
var testTypes Types
diff --git a/src/cmd/compile/internal/ssa/func.go b/src/cmd/compile/internal/ssa/func.go
index 0b5392f0f03..35a93826634 100644
--- a/src/cmd/compile/internal/ssa/func.go
+++ b/src/cmd/compile/internal/ssa/func.go
@@ -820,17 +820,22 @@ func (f *Func) invalidateCFG() {
}
// DebugHashMatch reports whether environment variable evname
-// 1) is empty (this is a special more-quickly implemented case of 3)
-// 2) is "y" or "Y"
-// 3) is a suffix of the sha1 hash of name
-// 4) is a suffix of the environment variable
+// 1. is empty (this is a special more-quickly implemented case of 3)
+// 2. is "y" or "Y"
+// 3. is a suffix of the sha1 hash of name
+// 4. is a suffix of the environment variable
// fmt.Sprintf("%s%d", evname, n)
// provided that all such variables are nonempty for 0 <= i <= n
+//
// Otherwise it returns false.
// When true is returned the message
-// "%s triggered %s\n", evname, name
+//
+// "%s triggered %s\n", evname, name
+//
// is printed on the file named in environment variable
-// GSHS_LOGFILE
+//
+// GSHS_LOGFILE
+//
// or standard out if that is empty or there is an error
// opening the file.
func (f *Func) DebugHashMatch(evname string) bool {
diff --git a/src/cmd/compile/internal/ssa/fuse.go b/src/cmd/compile/internal/ssa/fuse.go
index fec2ba87737..2b176dfa7b2 100644
--- a/src/cmd/compile/internal/ssa/fuse.go
+++ b/src/cmd/compile/internal/ssa/fuse.go
@@ -55,19 +55,21 @@ func fuse(f *Func, typ fuseType) {
// fuseBlockIf handles the following cases where s0 and s1 are empty blocks.
//
-// b b b b
-// \ / \ / | \ / \ / | | |
-// s0 s1 | s1 s0 | | |
-// \ / | / \ | | |
-// ss ss ss ss
+// b b b b
+// \ / \ / | \ / \ / | | |
+// s0 s1 | s1 s0 | | |
+// \ / | / \ | | |
+// ss ss ss ss
//
// If all Phi ops in ss have identical variables for slots corresponding to
// s0, s1 and b then the branch can be dropped.
// This optimization often comes up in switch statements with multiple
// expressions in a case clause:
-// switch n {
-// case 1,2,3: return 4
-// }
+//
+// switch n {
+// case 1,2,3: return 4
+// }
+//
// TODO: If ss doesn't contain any OpPhis, are s0 and s1 dead code anyway.
func fuseBlockIf(b *Block) bool {
if b.Kind != BlockIf {
diff --git a/src/cmd/compile/internal/ssa/fuse_branchredirect.go b/src/cmd/compile/internal/ssa/fuse_branchredirect.go
index 27449db55af..59570968a27 100644
--- a/src/cmd/compile/internal/ssa/fuse_branchredirect.go
+++ b/src/cmd/compile/internal/ssa/fuse_branchredirect.go
@@ -8,21 +8,24 @@ package ssa
// of an If block can be derived from its predecessor If block, in
// some such cases, we can redirect the predecessor If block to the
// corresponding successor block directly. For example:
-// p:
-// v11 = Less64 <bool> v10 v8
-// If v11 goto b else u
-// b: <- p ...
-// v17 = Leq64 <bool> v10 v8
-// If v17 goto s else o
+//
+// p:
+// v11 = Less64 <bool> v10 v8
+// If v11 goto b else u
+// b: <- p ...
+// v17 = Leq64 <bool> v10 v8
+// If v17 goto s else o
+//
// We can redirect p to s directly.
//
// The implementation here borrows the framework of the prove pass.
-// 1, Traverse all blocks of function f to find If blocks.
-// 2, For any If block b, traverse all its predecessors to find If blocks.
-// 3, For any If block predecessor p, update relationship p->b.
-// 4, Traverse all successors of b.
-// 5, For any successor s of b, try to update relationship b->s, if a
-// contradiction is found then redirect p to another successor of b.
+//
+// 1, Traverse all blocks of function f to find If blocks.
+// 2, For any If block b, traverse all its predecessors to find If blocks.
+// 3, For any If block predecessor p, update relationship p->b.
+// 4, Traverse all successors of b.
+// 5, For any successor s of b, try to update relationship b->s, if a
+// contradiction is found then redirect p to another successor of b.
func fuseBranchRedirect(f *Func) bool {
ft := newFactsTable(f)
ft.checkpoint()
diff --git a/src/cmd/compile/internal/ssa/fuse_comparisons.go b/src/cmd/compile/internal/ssa/fuse_comparisons.go
index d843fc3fda1..f5fb84b0d73 100644
--- a/src/cmd/compile/internal/ssa/fuse_comparisons.go
+++ b/src/cmd/compile/internal/ssa/fuse_comparisons.go
@@ -9,22 +9,22 @@ package ssa
//
// Look for branch structure like:
//
-// p
-// |\
-// | b
-// |/ \
-// s0 s1
+// p
+// |\
+// | b
+// |/ \
+// s0 s1
//
// In our example, p has control '1 <= x', b has control 'x < 5',
// and s0 and s1 are the if and else results of the comparison.
//
// This will be optimized into:
//
-// p
-// \
-// b
-// / \
-// s0 s1
+// p
+// \
+// b
+// / \
+// s0 s1
//
// where b has the combined control value 'unsigned(x-1) < 4'.
// Later passes will then fuse p and b.
diff --git a/src/cmd/compile/internal/ssa/gen/AMD64.rules b/src/cmd/compile/internal/ssa/gen/AMD64.rules
index d50bdf2a175..81fdebaf49d 100644
--- a/src/cmd/compile/internal/ssa/gen/AMD64.rules
+++ b/src/cmd/compile/internal/ssa/gen/AMD64.rules
@@ -206,6 +206,11 @@
(Rsh16x(64|32|16|8) x y) && shiftIsBounded(v) => (SARW x y)
(Rsh8x(64|32|16|8) x y) && shiftIsBounded(v) => (SARB x y)
+// Prefer SARX/SHLX/SHRX instruction because it has less register restriction on the shift input.
+(SAR(Q|L) x y) && buildcfg.GOAMD64 >= 3 => (SARX(Q|L) x y)
+(SHL(Q|L) x y) && buildcfg.GOAMD64 >= 3 => (SHLX(Q|L) x y)
+(SHR(Q|L) x y) && buildcfg.GOAMD64 >= 3 => (SHRX(Q|L) x y)
+
// Lowering integer comparisons
(Less(64|32|16|8) x y) => (SETL (CMP(Q|L|W|B) x y))
(Less(64|32|16|8)U x y) => (SETB (CMP(Q|L|W|B) x y))
@@ -512,6 +517,8 @@
(If cond yes no) => (NE (TESTB cond cond) yes no)
+(JumpTable idx) => (JUMPTABLE {makeJumpTableSym(b)} idx (LEAQ <typ.Uintptr> {makeJumpTableSym(b)} (SB)))
+
// Atomic loads. Other than preserving their ordering with respect to other loads, nothing special here.
(AtomicLoad8 ptr mem) => (MOVBatomicload ptr mem)
(AtomicLoad32 ptr mem) => (MOVLatomicload ptr mem)
@@ -590,6 +597,8 @@
// mutandis, for UGE and SETAE, and CC and SETCC.
((NE|EQ) (TESTL (SHLL (MOVLconst [1]) x) y)) => ((ULT|UGE) (BTL x y))
((NE|EQ) (TESTQ (SHLQ (MOVQconst [1]) x) y)) => ((ULT|UGE) (BTQ x y))
+((NE|EQ) (TESTL (SHLXL (MOVLconst [1]) x) y)) => ((ULT|UGE) (BTL x y))
+((NE|EQ) (TESTQ (SHLXQ (MOVQconst [1]) x) y)) => ((ULT|UGE) (BTQ x y))
((NE|EQ) (TESTLconst [c] x)) && isUint32PowerOfTwo(int64(c))
=> ((ULT|UGE) (BTLconst [int8(log32(c))] x))
((NE|EQ) (TESTQconst [c] x)) && isUint64PowerOfTwo(int64(c))
@@ -598,6 +607,8 @@
=> ((ULT|UGE) (BTQconst [int8(log64(c))] x))
(SET(NE|EQ) (TESTL (SHLL (MOVLconst [1]) x) y)) => (SET(B|AE) (BTL x y))
(SET(NE|EQ) (TESTQ (SHLQ (MOVQconst [1]) x) y)) => (SET(B|AE) (BTQ x y))
+(SET(NE|EQ) (TESTL (SHLXL (MOVLconst [1]) x) y)) => (SET(B|AE) (BTL x y))
+(SET(NE|EQ) (TESTQ (SHLXQ (MOVQconst [1]) x) y)) => (SET(B|AE) (BTQ x y))
(SET(NE|EQ) (TESTLconst [c] x)) && isUint32PowerOfTwo(int64(c))
=> (SET(B|AE) (BTLconst [int8(log32(c))] x))
(SET(NE|EQ) (TESTQconst [c] x)) && isUint64PowerOfTwo(int64(c))
@@ -609,6 +620,10 @@
=> (SET(B|AE)store [off] {sym} ptr (BTL x y) mem)
(SET(NE|EQ)store [off] {sym} ptr (TESTQ (SHLQ (MOVQconst [1]) x) y) mem)
=> (SET(B|AE)store [off] {sym} ptr (BTQ x y) mem)
+(SET(NE|EQ)store [off] {sym} ptr (TESTL (SHLXL (MOVLconst [1]) x) y) mem)
+ => (SET(B|AE)store [off] {sym} ptr (BTL x y) mem)
+(SET(NE|EQ)store [off] {sym} ptr (TESTQ (SHLXQ (MOVQconst [1]) x) y) mem)
+ => (SET(B|AE)store [off] {sym} ptr (BTQ x y) mem)
(SET(NE|EQ)store [off] {sym} ptr (TESTLconst [c] x) mem) && isUint32PowerOfTwo(int64(c))
=> (SET(B|AE)store [off] {sym} ptr (BTLconst [int8(log32(c))] x) mem)
(SET(NE|EQ)store [off] {sym} ptr (TESTQconst [c] x) mem) && isUint64PowerOfTwo(int64(c))
@@ -621,9 +636,10 @@
(BT(Q|L)const [c] (SHRQconst [d] x)) && (c+d)<64 => (BTQconst [c+d] x)
(BT(Q|L)const [c] (SHLQconst [d] x)) && c>d => (BT(Q|L)const [c-d] x)
(BT(Q|L)const [0] s:(SHRQ x y)) => (BTQ y x)
+(BT(Q|L)const [0] s:(SHRXQ x y)) => (BTQ y x)
(BTLconst [c] (SHRLconst [d] x)) && (c+d)<32 => (BTLconst [c+d] x)
(BTLconst [c] (SHLLconst [d] x)) && c>d => (BTLconst [c-d] x)
-(BTLconst [0] s:(SHRL x y)) => (BTL y x)
+(BTLconst [0] s:(SHR(L|XL) x y)) => (BTL y x)
// Rewrite a & 1 != 1 into a & 1 == 0.
// Among other things, this lets us turn (a>>b)&1 != 1 into a bit test.
@@ -635,6 +651,8 @@
// Recognize bit setting (a |= 1<<b) and toggling (a ^= 1<<b)
(OR(Q|L) (SHL(Q|L) (MOV(Q|L)const [1]) y) x) => (BTS(Q|L) x y)
(XOR(Q|L) (SHL(Q|L) (MOV(Q|L)const [1]) y) x) => (BTC(Q|L) x y)
+(OR(Q|L) (SHLX(Q|L) (MOV(Q|L)const [1]) y) x) => (BTS(Q|L) x y)
+(XOR(Q|L) (SHLX(Q|L) (MOV(Q|L)const [1]) y) x) => (BTC(Q|L) x y)
// Convert ORconst into BTS, if the code gets smaller, with boundary being
// (ORL $40,AX is 3 bytes, ORL $80,AX is 6 bytes).
@@ -650,6 +668,8 @@
// Recognize bit clearing: a &^= 1<<b
(AND(Q|L) (NOT(Q|L) (SHL(Q|L) (MOV(Q|L)const [1]) y)) x) => (BTR(Q|L) x y)
(ANDN(Q|L) x (SHL(Q|L) (MOV(Q|L)const [1]) y)) => (BTR(Q|L) x y)
+(AND(Q|L) (NOT(Q|L) (SHLX(Q|L) (MOV(Q|L)const [1]) y)) x) => (BTR(Q|L) x y)
+(ANDN(Q|L) x (SHLX(Q|L) (MOV(Q|L)const [1]) y)) => (BTR(Q|L) x y)
(ANDQconst [c] x) && isUint64PowerOfTwo(int64(^c)) && uint64(^c) >= 128
=> (BTRQconst [int8(log32(^c))] x)
(ANDLconst [c] x) && isUint32PowerOfTwo(int64(^c)) && uint64(^c) >= 128
@@ -791,6 +811,8 @@
(SHLQ x (MOV(Q|L)const [c])) => (SHLQconst [int8(c&63)] x)
(SHLL x (MOV(Q|L)const [c])) => (SHLLconst [int8(c&31)] x)
+(SHLXQ x (MOV(Q|L)const [c])) => (SHLQconst [int8(c&63)] x)
+(SHLXL x (MOV(Q|L)const [c])) => (SHLLconst [int8(c&31)] x)
(SHRQ x (MOV(Q|L)const [c])) => (SHRQconst [int8(c&63)] x)
(SHRL x (MOV(Q|L)const [c])) => (SHRLconst [int8(c&31)] x)
@@ -798,33 +820,36 @@
(SHRW _ (MOV(Q|L)const [c])) && c&31 >= 16 => (MOVLconst [0])
(SHRB x (MOV(Q|L)const [c])) && c&31 < 8 => (SHRBconst [int8(c&31)] x)
(SHRB _ (MOV(Q|L)const [c])) && c&31 >= 8 => (MOVLconst [0])
+(SHRXQ x (MOV(Q|L)const [c])) => (SHRQconst [int8(c&63)] x)
+(SHRXL x (MOV(Q|L)const [c])) => (SHRLconst [int8(c&31)] x)
(SARQ x (MOV(Q|L)const [c])) => (SARQconst [int8(c&63)] x)
(SARL x (MOV(Q|L)const [c])) => (SARLconst [int8(c&31)] x)
(SARW x (MOV(Q|L)const [c])) => (SARWconst [int8(min(int64(c)&31,15))] x)
(SARB x (MOV(Q|L)const [c])) => (SARBconst [int8(min(int64(c)&31,7))] x)
-
+(SARXQ x (MOV(Q|L)const [c])) => (SARQconst [int8(c&63)] x)
+(SARXL x (MOV(Q|L)const [c])) => (SARLconst [int8(c&31)] x)
// Operations which don't affect the low 6/5 bits of the shift amount are NOPs.
-((SHLQ|SHRQ|SARQ) x (ADDQconst [c] y)) && c & 63 == 0 => ((SHLQ|SHRQ|SARQ) x y)
-((SHLQ|SHRQ|SARQ) x (NEGQ <t> (ADDQconst [c] y))) && c & 63 == 0 => ((SHLQ|SHRQ|SARQ) x (NEGQ <t> y))
-((SHLQ|SHRQ|SARQ) x (ANDQconst [c] y)) && c & 63 == 63 => ((SHLQ|SHRQ|SARQ) x y)
-((SHLQ|SHRQ|SARQ) x (NEGQ <t> (ANDQconst [c] y))) && c & 63 == 63 => ((SHLQ|SHRQ|SARQ) x (NEGQ <t> y))
-
-((SHLL|SHRL|SARL) x (ADDQconst [c] y)) && c & 31 == 0 => ((SHLL|SHRL|SARL) x y)
-((SHLL|SHRL|SARL) x (NEGQ <t> (ADDQconst [c] y))) && c & 31 == 0 => ((SHLL|SHRL|SARL) x (NEGQ <t> y))
-((SHLL|SHRL|SARL) x (ANDQconst [c] y)) && c & 31 == 31 => ((SHLL|SHRL|SARL) x y)
-((SHLL|SHRL|SARL) x (NEGQ <t> (ANDQconst [c] y))) && c & 31 == 31 => ((SHLL|SHRL|SARL) x (NEGQ <t> y))
-
-((SHLQ|SHRQ|SARQ) x (ADDLconst [c] y)) && c & 63 == 0 => ((SHLQ|SHRQ|SARQ) x y)
-((SHLQ|SHRQ|SARQ) x (NEGL <t> (ADDLconst [c] y))) && c & 63 == 0 => ((SHLQ|SHRQ|SARQ) x (NEGL <t> y))
-((SHLQ|SHRQ|SARQ) x (ANDLconst [c] y)) && c & 63 == 63 => ((SHLQ|SHRQ|SARQ) x y)
-((SHLQ|SHRQ|SARQ) x (NEGL <t> (ANDLconst [c] y))) && c & 63 == 63 => ((SHLQ|SHRQ|SARQ) x (NEGL <t> y))
-
-((SHLL|SHRL|SARL) x (ADDLconst [c] y)) && c & 31 == 0 => ((SHLL|SHRL|SARL) x y)
-((SHLL|SHRL|SARL) x (NEGL <t> (ADDLconst [c] y))) && c & 31 == 0 => ((SHLL|SHRL|SARL) x (NEGL <t> y))
-((SHLL|SHRL|SARL) x (ANDLconst [c] y)) && c & 31 == 31 => ((SHLL|SHRL|SARL) x y)
-((SHLL|SHRL|SARL) x (NEGL <t> (ANDLconst [c] y))) && c & 31 == 31 => ((SHLL|SHRL|SARL) x (NEGL <t> y))
+((SHLQ|SHRQ|SARQ|SHLXQ|SHRXQ|SARXQ) x (ADDQconst [c] y)) && c & 63 == 0 => ((SHLQ|SHRQ|SARQ|SHLXQ|SHRXQ|SARXQ) x y)
+((SHLQ|SHRQ|SARQ|SHLXQ|SHRXQ|SARXQ) x (NEGQ <t> (ADDQconst [c] y))) && c & 63 == 0 => ((SHLQ|SHRQ|SARQ|SHLXQ|SHRXQ|SARXQ) x (NEGQ <t> y))
+((SHLQ|SHRQ|SARQ|SHLXQ|SHRXQ|SARXQ) x (ANDQconst [c] y)) && c & 63 == 63 => ((SHLQ|SHRQ|SARQ|SHLXQ|SHRXQ|SARXQ) x y)
+((SHLQ|SHRQ|SARQ|SHLXQ|SHRXQ|SARXQ) x (NEGQ <t> (ANDQconst [c] y))) && c & 63 == 63 => ((SHLQ|SHRQ|SARQ|SHLXQ|SHRXQ|SARXQ) x (NEGQ <t> y))
+
+((SHLL|SHRL|SARL|SHLXL|SHRXL|SARXL) x (ADDQconst [c] y)) && c & 31 == 0 => ((SHLL|SHRL|SARL|SHLXL|SHRXL|SARXL) x y)
+((SHLL|SHRL|SARL|SHLXL|SHRXL|SARXL) x (NEGQ <t> (ADDQconst [c] y))) && c & 31 == 0 => ((SHLL|SHRL|SARL|SHLXL|SHRXL|SARXL) x (NEGQ <t> y))
+((SHLL|SHRL|SARL|SHLXL|SHRXL|SARXL) x (ANDQconst [c] y)) && c & 31 == 31 => ((SHLL|SHRL|SARL|SHLXL|SHRXL|SARXL) x y)
+((SHLL|SHRL|SARL|SHLXL|SHRXL|SARXL) x (NEGQ <t> (ANDQconst [c] y))) && c & 31 == 31 => ((SHLL|SHRL|SARL|SHLXL|SHRXL|SARXL) x (NEGQ <t> y))
+
+((SHLQ|SHRQ|SARQ|SHLXQ|SHRXQ|SARXQ) x (ADDLconst [c] y)) && c & 63 == 0 => ((SHLQ|SHRQ|SARQ|SHLXQ|SHRXQ|SARXQ) x y)
+((SHLQ|SHRQ|SARQ|SHLXQ|SHRXQ|SARXQ) x (NEGL <t> (ADDLconst [c] y))) && c & 63 == 0 => ((SHLQ|SHRQ|SARQ|SHLXQ|SHRXQ|SARXQ) x (NEGL <t> y))
+((SHLQ|SHRQ|SARQ|SHLXQ|SHRXQ|SARXQ) x (ANDLconst [c] y)) && c & 63 == 63 => ((SHLQ|SHRQ|SARQ|SHLXQ|SHRXQ|SARXQ) x y)
+((SHLQ|SHRQ|SARQ|SHLXQ|SHRXQ|SARXQ) x (NEGL <t> (ANDLconst [c] y))) && c & 63 == 63 => ((SHLQ|SHRQ|SARQ|SHLXQ|SHRXQ|SARXQ) x (NEGL <t> y))
+
+((SHLL|SHRL|SARL|SHLXL|SHRXL|SARXL) x (ADDLconst [c] y)) && c & 31 == 0 => ((SHLL|SHRL|SARL|SHLXL|SHRXL|SARXL) x y)
+((SHLL|SHRL|SARL|SHLXL|SHRXL|SARXL) x (NEGL <t> (ADDLconst [c] y))) && c & 31 == 0 => ((SHLL|SHRL|SARL|SHLXL|SHRXL|SARXL) x (NEGL <t> y))
+((SHLL|SHRL|SARL|SHLXL|SHRXL|SARXL) x (ANDLconst [c] y)) && c & 31 == 31 => ((SHLL|SHRL|SARL|SHLXL|SHRXL|SARXL) x y)
+((SHLL|SHRL|SARL|SHLXL|SHRXL|SARXL) x (NEGL <t> (ANDLconst [c] y))) && c & 31 == 31 => ((SHLL|SHRL|SARL|SHLXL|SHRXL|SARXL) x (NEGL <t> y))
// Constant rotate instructions
((ADDQ|ORQ|XORQ) (SHLQconst x [c]) (SHRQconst x [d])) && d==64-c => (ROLQconst x [c])
@@ -856,9 +881,13 @@
// it in order to strip it out.
(ORQ (SHLQ x y) (ANDQ (SHRQ x (NEG(Q|L) y)) (SBBQcarrymask (CMP(Q|L)const (NEG(Q|L) (ADD(Q|L)const (AND(Q|L)const y [63]) [-64])) [64])))) => (ROLQ x y)
(ORQ (SHRQ x y) (ANDQ (SHLQ x (NEG(Q|L) y)) (SBBQcarrymask (CMP(Q|L)const (NEG(Q|L) (ADD(Q|L)const (AND(Q|L)const y [63]) [-64])) [64])))) => (RORQ x y)
+(ORQ (SHLXQ x y) (ANDQ (SHRXQ x (NEG(Q|L) y)) (SBBQcarrymask (CMP(Q|L)const (NEG(Q|L) (ADD(Q|L)const (AND(Q|L)const y [63]) [-64])) [64])))) => (ROLQ x y)
+(ORQ (SHRXQ x y) (ANDQ (SHLXQ x (NEG(Q|L) y)) (SBBQcarrymask (CMP(Q|L)const (NEG(Q|L) (ADD(Q|L)const (AND(Q|L)const y [63]) [-64])) [64])))) => (RORQ x y)
(ORL (SHLL x y) (ANDL (SHRL x (NEG(Q|L) y)) (SBBLcarrymask (CMP(Q|L)const (NEG(Q|L) (ADD(Q|L)const (AND(Q|L)const y [31]) [-32])) [32])))) => (ROLL x y)
(ORL (SHRL x y) (ANDL (SHLL x (NEG(Q|L) y)) (SBBLcarrymask (CMP(Q|L)const (NEG(Q|L) (ADD(Q|L)const (AND(Q|L)const y [31]) [-32])) [32])))) => (RORL x y)
+(ORL (SHLXL x y) (ANDL (SHRXL x (NEG(Q|L) y)) (SBBLcarrymask (CMP(Q|L)const (NEG(Q|L) (ADD(Q|L)const (AND(Q|L)const y [31]) [-32])) [32])))) => (ROLL x y)
+(ORL (SHRXL x y) (ANDL (SHLXL x (NEG(Q|L) y)) (SBBLcarrymask (CMP(Q|L)const (NEG(Q|L) (ADD(Q|L)const (AND(Q|L)const y [31]) [-32])) [32])))) => (RORL x y)
// Help with rotate detection
(CMPQconst (NEGQ (ADDQconst [-16] (ANDQconst [15] _))) [32]) => (FlagLT_ULT)
@@ -873,6 +902,15 @@
(SHLL x (NEG(Q|L) (ADD(Q|L)const (AND(Q|L)const y [15]) [-16]))))
&& v.Type.Size() == 2
=> (RORW x y)
+(ORL (SHLXL x (AND(Q|L)const y [15]))
+ (ANDL (SHRW x (NEG(Q|L) (ADD(Q|L)const (AND(Q|L)const y [15]) [-16])))
+ (SBBLcarrymask (CMP(Q|L)const (NEG(Q|L) (ADD(Q|L)const (AND(Q|L)const y [15]) [-16])) [16]))))
+ && v.Type.Size() == 2
+ => (ROLW x y)
+(ORL (SHRW x (AND(Q|L)const y [15]))
+ (SHLXL x (NEG(Q|L) (ADD(Q|L)const (AND(Q|L)const y [15]) [-16]))))
+ && v.Type.Size() == 2
+ => (RORW x y)
(ORL (SHLL x (AND(Q|L)const y [ 7]))
(ANDL (SHRB x (NEG(Q|L) (ADD(Q|L)const (AND(Q|L)const y [ 7]) [ -8])))
@@ -883,6 +921,15 @@
(SHLL x (NEG(Q|L) (ADD(Q|L)const (AND(Q|L)const y [ 7]) [ -8]))))
&& v.Type.Size() == 1
=> (RORB x y)
+(ORL (SHLXL x (AND(Q|L)const y [ 7]))
+ (ANDL (SHRB x (NEG(Q|L) (ADD(Q|L)const (AND(Q|L)const y [ 7]) [ -8])))
+ (SBBLcarrymask (CMP(Q|L)const (NEG(Q|L) (ADD(Q|L)const (AND(Q|L)const y [ 7]) [ -8])) [ 8]))))
+ && v.Type.Size() == 1
+ => (ROLB x y)
+(ORL (SHRB x (AND(Q|L)const y [ 7]))
+ (SHLXL x (NEG(Q|L) (ADD(Q|L)const (AND(Q|L)const y [ 7]) [ -8]))))
+ && v.Type.Size() == 1
+ => (RORB x y)
// rotate left negative = rotate right
(ROLQ x (NEG(Q|L) y)) => (RORQ x y)
@@ -916,6 +963,7 @@
// Multi-register shifts
(ORQ (SH(R|L)Q lo bits) (SH(L|R)Q hi (NEGQ bits))) => (SH(R|L)DQ lo hi bits)
+(ORQ (SH(R|L)XQ lo bits) (SH(L|R)XQ hi (NEGQ bits))) => (SH(R|L)DQ lo hi bits)
// Note: the word and byte shifts keep the low 5 bits (not the low 4 or 3 bits)
// because the x86 instructions are defined to use all 5 bits of the shift even
@@ -2252,5 +2300,10 @@
&& clobber(x0, x1, sh)
=> @mergePoint(b,x0,x1) (MOVBEQload [i] {s} p1 mem)
-(SHL(Q|L) l:(MOV(Q|L)load [off] {sym} ptr mem) x) && buildcfg.GOAMD64 >= 3 && canMergeLoad(v, l) && clobber(l) => (SHLX(Q|L)load [off] {sym} ptr x mem)
-(SHR(Q|L) l:(MOV(Q|L)load [off] {sym} ptr mem) x) && buildcfg.GOAMD64 >= 3 && canMergeLoad(v, l) && clobber(l) => (SHRX(Q|L)load [off] {sym} ptr x mem)
+(SARX(Q|L) l:(MOV(Q|L)load [off] {sym} ptr mem) x) && canMergeLoad(v, l) && clobber(l) => (SARX(Q|L)load [off] {sym} ptr x mem)
+(SHLX(Q|L) l:(MOV(Q|L)load [off] {sym} ptr mem) x) && canMergeLoad(v, l) && clobber(l) => (SHLX(Q|L)load [off] {sym} ptr x mem)
+(SHRX(Q|L) l:(MOV(Q|L)load [off] {sym} ptr mem) x) && canMergeLoad(v, l) && clobber(l) => (SHRX(Q|L)load [off] {sym} ptr x mem)
+
+((SHL|SHR|SAR)XQload [off] {sym} ptr (MOVQconst [c]) mem) => ((SHL|SHR|SAR)Qconst [int8(c&63)] (MOVQload [off] {sym} ptr mem))
+((SHL|SHR|SAR)XQload [off] {sym} ptr (MOVLconst [c]) mem) => ((SHL|SHR|SAR)Qconst [int8(c&63)] (MOVQload [off] {sym} ptr mem))
+((SHL|SHR|SAR)XLload [off] {sym} ptr (MOVLconst [c]) mem) => ((SHL|SHR|SAR)Lconst [int8(c&31)] (MOVLload [off] {sym} ptr mem))
diff --git a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go
index d760d7d79e3..fc42fa5e28b 100644
--- a/src/cmd/compile/internal/ssa/gen/AMD64Ops.go
+++ b/src/cmd/compile/internal/ssa/gen/AMD64Ops.go
@@ -937,13 +937,41 @@ func init() {
{name: "MOVBELstore", argLength: 3, reg: gpstore, asm: "MOVBEL", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // swap and store 4 bytes in arg1 to arg0+auxint+aux. arg2=mem
{name: "MOVBEQload", argLength: 2, reg: gpload, asm: "MOVBEQ", aux: "SymOff", typ: "UInt64", faultOnNilArg0: true, symEffect: "Read"}, // load and swap 8 bytes from arg0+auxint+aux. arg1=mem
{name: "MOVBEQstore", argLength: 3, reg: gpstore, asm: "MOVBEQ", aux: "SymOff", typ: "Mem", faultOnNilArg0: true, symEffect: "Write"}, // swap and store 8 bytes in arg1 to arg0+auxint+aux. arg2=mem
+ // indexed MOVBE loads
+ {name: "MOVBELloadidx1", argLength: 3, reg: gploadidx, commutative: true, asm: "MOVBEL", scale: 1, aux: "SymOff", typ: "UInt32", symEffect: "Read"}, // load and swap 4 bytes from arg0+arg1+auxint+aux. arg2=mem. Zero extend.
+ {name: "MOVBELloadidx4", argLength: 3, reg: gploadidx, asm: "MOVBEL", scale: 4, aux: "SymOff", typ: "UInt32", symEffect: "Read"}, // load and swap 4 bytes from arg0+4*arg1+auxint+aux. arg2=mem. Zero extend.
+ {name: "MOVBELloadidx8", argLength: 3, reg: gploadidx, asm: "MOVBEL", scale: 8, aux: "SymOff", typ: "UInt32", symEffect: "Read"}, // load and swap 4 bytes from arg0+8*arg1+auxint+aux. arg2=mem. Zero extend.
+ {name: "MOVBEQloadidx1", argLength: 3, reg: gploadidx, commutative: true, asm: "MOVBEQ", scale: 1, aux: "SymOff", typ: "UInt64", symEffect: "Read"}, // load and swap 8 bytes from arg0+arg1+auxint+aux. arg2=mem
+ {name: "MOVBEQloadidx8", argLength: 3, reg: gploadidx, asm: "MOVBEQ", scale: 8, aux: "SymOff", typ: "UInt64", symEffect: "Read"}, // load and swap 8 bytes from arg0+8*arg1+auxint+aux. arg2=mem
+ // indexed MOVBE stores
+ {name: "MOVBEWstoreidx1", argLength: 4, reg: gpstoreidx, commutative: true, asm: "MOVBEW", scale: 1, aux: "SymOff", symEffect: "Write"}, // swap and store 2 bytes in arg2 to arg0+arg1+auxint+aux. arg3=mem
+ {name: "MOVBEWstoreidx2", argLength: 4, reg: gpstoreidx, asm: "MOVBEW", scale: 2, aux: "SymOff", symEffect: "Write"}, // swap and store 2 bytes in arg2 to arg0+2*arg1+auxint+aux. arg3=mem
+ {name: "MOVBELstoreidx1", argLength: 4, reg: gpstoreidx, commutative: true, asm: "MOVBEL", scale: 1, aux: "SymOff", symEffect: "Write"}, // swap and store 4 bytes in arg2 to arg0+arg1+auxint+aux. arg3=mem
+ {name: "MOVBELstoreidx4", argLength: 4, reg: gpstoreidx, asm: "MOVBEL", scale: 4, aux: "SymOff", symEffect: "Write"}, // swap and store 4 bytes in arg2 to arg0+4*arg1+auxint+aux. arg3=mem
+ {name: "MOVBELstoreidx8", argLength: 4, reg: gpstoreidx, asm: "MOVBEL", scale: 8, aux: "SymOff", symEffect: "Write"}, // swap and store 4 bytes in arg2 to arg0+8*arg1+auxint+aux. arg3=mem
+ {name: "MOVBEQstoreidx1", argLength: 4, reg: gpstoreidx, commutative: true, asm: "MOVBEQ", scale: 1, aux: "SymOff", symEffect: "Write"}, // swap and store 8 bytes in arg2 to arg0+arg1+auxint+aux. arg3=mem
+ {name: "MOVBEQstoreidx8", argLength: 4, reg: gpstoreidx, asm: "MOVBEQ", scale: 8, aux: "SymOff", symEffect: "Write"}, // swap and store 8 bytes in arg2 to arg0+8*arg1+auxint+aux. arg3=mem
// CPUID feature: BMI2.
+ {name: "SARXQ", argLength: 2, reg: gp21, asm: "SARXQ"}, // signed arg0 >> arg1, shift amount is mod 64
+ {name: "SARXL", argLength: 2, reg: gp21, asm: "SARXL"}, // signed int32(arg0) >> arg1, shift amount is mod 32
+ {name: "SHLXQ", argLength: 2, reg: gp21, asm: "SHLXQ"}, // arg0 << arg1, shift amount is mod 64
+ {name: "SHLXL", argLength: 2, reg: gp21, asm: "SHLXL"}, // arg0 << arg1, shift amount is mod 32
+ {name: "SHRXQ", argLength: 2, reg: gp21, asm: "SHRXQ"}, // unsigned arg0 >> arg1, shift amount is mod 64
+ {name: "SHRXL", argLength: 2, reg: gp21, asm: "SHRXL"}, // unsigned uint32(arg0) >> arg1, shift amount is mod 32
+
+ {name: "SARXLload", argLength: 3, reg: gp21shxload, asm: "SARXL", aux: "SymOff", typ: "Uint32", faultOnNilArg0: true, symEffect: "Read"}, // signed *(arg0+auxint+aux) >> arg1, arg2=mem, shift amount is mod 32
+ {name: "SARXQload", argLength: 3, reg: gp21shxload, asm: "SARXQ", aux: "SymOff", typ: "Uint64", faultOnNilArg0: true, symEffect: "Read"}, // signed *(arg0+auxint+aux) >> arg1, arg2=mem, shift amount is mod 64
{name: "SHLXLload", argLength: 3, reg: gp21shxload, asm: "SHLXL", aux: "SymOff", typ: "Uint32", faultOnNilArg0: true, symEffect: "Read"}, // *(arg0+auxint+aux) << arg1, arg2=mem, shift amount is mod 32
{name: "SHLXQload", argLength: 3, reg: gp21shxload, asm: "SHLXQ", aux: "SymOff", typ: "Uint64", faultOnNilArg0: true, symEffect: "Read"}, // *(arg0+auxint+aux) << arg1, arg2=mem, shift amount is mod 64
{name: "SHRXLload", argLength: 3, reg: gp21shxload, asm: "SHRXL", aux: "SymOff", typ: "Uint32", faultOnNilArg0: true, symEffect: "Read"}, // unsigned *(arg0+auxint+aux) >> arg1, arg2=mem, shift amount is mod 32
{name: "SHRXQload", argLength: 3, reg: gp21shxload, asm: "SHRXQ", aux: "SymOff", typ: "Uint64", faultOnNilArg0: true, symEffect: "Read"}, // unsigned *(arg0+auxint+aux) >> arg1, arg2=mem, shift amount is mod 64
+ {name: "SARXLloadidx1", argLength: 4, reg: gp21shxloadidx, asm: "SARXL", scale: 1, aux: "SymOff", typ: "Uint32", faultOnNilArg0: true, symEffect: "Read"}, // signed *(arg0+1*arg1+auxint+aux) >> arg2, arg3=mem, shift amount is mod 32
+ {name: "SARXLloadidx4", argLength: 4, reg: gp21shxloadidx, asm: "SARXL", scale: 4, aux: "SymOff", typ: "Uint32", faultOnNilArg0: true, symEffect: "Read"}, // signed *(arg0+4*arg1+auxint+aux) >> arg2, arg3=mem, shift amount is mod 32
+ {name: "SARXLloadidx8", argLength: 4, reg: gp21shxloadidx, asm: "SARXL", scale: 8, aux: "SymOff", typ: "Uint32", faultOnNilArg0: true, symEffect: "Read"}, // signed *(arg0+8*arg1+auxint+aux) >> arg2, arg3=mem, shift amount is mod 32
+ {name: "SARXQloadidx1", argLength: 4, reg: gp21shxloadidx, asm: "SARXQ", scale: 1, aux: "SymOff", typ: "Uint64", faultOnNilArg0: true, symEffect: "Read"}, // signed *(arg0+1*arg1+auxint+aux) >> arg2, arg3=mem, shift amount is mod 64
+ {name: "SARXQloadidx8", argLength: 4, reg: gp21shxloadidx, asm: "SARXQ", scale: 8, aux: "SymOff", typ: "Uint64", faultOnNilArg0: true, symEffect: "Read"}, // signed *(arg0+8*arg1+auxint+aux) >> arg2, arg3=mem, shift amount is mod 64
{name: "SHLXLloadidx1", argLength: 4, reg: gp21shxloadidx, asm: "SHLXL", scale: 1, aux: "SymOff", typ: "Uint32", faultOnNilArg0: true, symEffect: "Read"}, // *(arg0+1*arg1+auxint+aux) << arg2, arg3=mem, shift amount is mod 32
{name: "SHLXLloadidx4", argLength: 4, reg: gp21shxloadidx, asm: "SHLXL", scale: 4, aux: "SymOff", typ: "Uint32", faultOnNilArg0: true, symEffect: "Read"}, // *(arg0+4*arg1+auxint+aux) << arg2, arg3=mem, shift amount is mod 32
{name: "SHLXLloadidx8", argLength: 4, reg: gp21shxloadidx, asm: "SHLXL", scale: 8, aux: "SymOff", typ: "Uint32", faultOnNilArg0: true, symEffect: "Read"}, // *(arg0+8*arg1+auxint+aux) << arg2, arg3=mem, shift amount is mod 32
@@ -973,6 +1001,12 @@ func init() {
{name: "NEF", controls: 1},
{name: "ORD", controls: 1}, // FP, ordered comparison (parity zero)
{name: "NAN", controls: 1}, // FP, unordered comparison (parity one)
+
+ // JUMPTABLE implements jump tables.
+ // Aux is the symbol (an *obj.LSym) for the jump table.
+ // control[0] is the index into the jump table.
+ // control[1] is the address of the jump table (the address of the symbol stored in Aux).
+ {name: "JUMPTABLE", controls: 2, aux: "Sym"},
}
archs = append(archs, arch{
diff --git a/src/cmd/compile/internal/ssa/gen/generic.rules b/src/cmd/compile/internal/ssa/gen/generic.rules
index 6dbe9b47d01..d5cc107fab7 100644
--- a/src/cmd/compile/internal/ssa/gen/generic.rules
+++ b/src/cmd/compile/internal/ssa/gen/generic.rules
@@ -515,15 +515,18 @@
// simplifications
(Or(64|32|16|8) x x) => x
-(Or(64|32|16|8) (Const(64|32|16|8) [0]) x) => x
+(Or(64|32|16|8) (Const(64|32|16|8) [0]) x) => x
(Or(64|32|16|8) (Const(64|32|16|8) [-1]) _) => (Const(64|32|16|8) [-1])
+(Or(64|32|16|8) (Com(64|32|16|8) x) x) => (Const(64|32|16|8) [-1])
(And(64|32|16|8) x x) => x
(And(64|32|16|8) (Const(64|32|16|8) [-1]) x) => x
-(And(64|32|16|8) (Const(64|32|16|8) [0]) _) => (Const(64|32|16|8) [0])
+(And(64|32|16|8) (Const(64|32|16|8) [0]) _) => (Const(64|32|16|8) [0])
+(And(64|32|16|8) (Com(64|32|16|8) x) x) => (Const(64|32|16|8) [0])
(Xor(64|32|16|8) x x) => (Const(64|32|16|8) [0])
(Xor(64|32|16|8) (Const(64|32|16|8) [0]) x) => x
+(Xor(64|32|16|8) (Com(64|32|16|8) x) x) => (Const(64|32|16|8) [-1])
(Add(64|32|16|8) (Const(64|32|16|8) [0]) x) => x
(Sub(64|32|16|8) x x) => (Const(64|32|16|8) [0])
@@ -533,6 +536,13 @@
(Com(64|32|16|8) (Const(64|32|16|8) [c])) => (Const(64|32|16|8) [^c])
(Neg(64|32|16|8) (Sub(64|32|16|8) x y)) => (Sub(64|32|16|8) y x)
+(Add(64|32|16|8) x (Neg(64|32|16|8) y)) => (Sub(64|32|16|8) x y)
+
+(Xor(64|32|16|8) (Const(64|32|16|8) [-1]) x) => (Com(64|32|16|8) x)
+
+(Sub(64|32|16|8) (Neg(64|32|16|8) x) (Com(64|32|16|8) x)) => (Const(64|32|16|8) [1])
+(Sub(64|32|16|8) (Com(64|32|16|8) x) (Neg(64|32|16|8) x)) => (Const(64|32|16|8) [-1])
+(Add(64|32|16|8) (Com(64|32|16|8) x) x) => (Const(64|32|16|8) [-1])
// ^(x-1) == ^x+1 == -x
(Add(64|32|16|8) (Const(64|32|16|8) [1]) (Com(64|32|16|8) x)) => (Neg(64|32|16|8) x)
diff --git a/src/cmd/compile/internal/ssa/gen/genericOps.go b/src/cmd/compile/internal/ssa/gen/genericOps.go
index 4f133b1ff6a..e04b7db6e7c 100644
--- a/src/cmd/compile/internal/ssa/gen/genericOps.go
+++ b/src/cmd/compile/internal/ssa/gen/genericOps.go
@@ -639,12 +639,13 @@ var genericOps = []opData{
// First [] [always, never]
var genericBlocks = []blockData{
- {name: "Plain"}, // a single successor
- {name: "If", controls: 1}, // if Controls[0] goto Succs[0] else goto Succs[1]
- {name: "Defer", controls: 1}, // Succs[0]=defer queued, Succs[1]=defer recovered. Controls[0] is call op (of memory type)
- {name: "Ret", controls: 1}, // no successors, Controls[0] value is memory result
- {name: "RetJmp", controls: 1}, // no successors, Controls[0] value is a tail call
- {name: "Exit", controls: 1}, // no successors, Controls[0] value generates a panic
+ {name: "Plain"}, // a single successor
+ {name: "If", controls: 1}, // if Controls[0] goto Succs[0] else goto Succs[1]
+ {name: "Defer", controls: 1}, // Succs[0]=defer queued, Succs[1]=defer recovered. Controls[0] is call op (of memory type)
+ {name: "Ret", controls: 1}, // no successors, Controls[0] value is memory result
+ {name: "RetJmp", controls: 1}, // no successors, Controls[0] value is a tail call
+ {name: "Exit", controls: 1}, // no successors, Controls[0] value generates a panic
+ {name: "JumpTable", controls: 1}, // multiple successors, the integer Controls[0] selects which one
// transient block state used for dead code removal
{name: "First"}, // 2 successors, always takes the first one (second is dead)
diff --git a/src/cmd/compile/internal/ssa/gen/rulegen.go b/src/cmd/compile/internal/ssa/gen/rulegen.go
index fe8db4ed1f2..0f7e9703726 100644
--- a/src/cmd/compile/internal/ssa/gen/rulegen.go
+++ b/src/cmd/compile/internal/ssa/gen/rulegen.go
@@ -1838,6 +1838,8 @@ func (op opData) auxIntType() string {
// auxType returns the Go type that this block should store in its aux field.
func (b blockData) auxType() string {
switch b.aux {
+ case "Sym":
+ return "Sym"
case "S390XCCMask", "S390XCCMaskInt8", "S390XCCMaskUint8":
return "s390x.CCMask"
case "S390XRotateParams":
diff --git a/src/cmd/compile/internal/ssa/location.go b/src/cmd/compile/internal/ssa/location.go
index d69db404ed3..00aea879363 100644
--- a/src/cmd/compile/internal/ssa/location.go
+++ b/src/cmd/compile/internal/ssa/location.go
@@ -46,19 +46,19 @@ func (r *Register) GCNum() int16 {
// variable that has been decomposed into multiple stack slots.
// As an example, a string could have the following configurations:
//
-// stack layout LocalSlots
+// stack layout LocalSlots
//
-// Optimizations are disabled. s is on the stack and represented in its entirety.
-// [ ------- s string ---- ] { N: s, Type: string, Off: 0 }
+// Optimizations are disabled. s is on the stack and represented in its entirety.
+// [ ------- s string ---- ] { N: s, Type: string, Off: 0 }
//
-// s was not decomposed, but the SSA operates on its parts individually, so
-// there is a LocalSlot for each of its fields that points into the single stack slot.
-// [ ------- s string ---- ] { N: s, Type: *uint8, Off: 0 }, {N: s, Type: int, Off: 8}
+// s was not decomposed, but the SSA operates on its parts individually, so
+// there is a LocalSlot for each of its fields that points into the single stack slot.
+// [ ------- s string ---- ] { N: s, Type: *uint8, Off: 0 }, {N: s, Type: int, Off: 8}
//
-// s was decomposed. Each of its fields is in its own stack slot and has its own LocalSLot.
-// [ ptr *uint8 ] [ len int] { N: ptr, Type: *uint8, Off: 0, SplitOf: parent, SplitOffset: 0},
-// { N: len, Type: int, Off: 0, SplitOf: parent, SplitOffset: 8}
-// parent = &{N: s, Type: string}
+// s was decomposed. Each of its fields is in its own stack slot and has its own LocalSLot.
+// [ ptr *uint8 ] [ len int] { N: ptr, Type: *uint8, Off: 0, SplitOf: parent, SplitOffset: 0},
+// { N: len, Type: int, Off: 0, SplitOf: parent, SplitOffset: 8}
+// parent = &{N: s, Type: string}
type LocalSlot struct {
N *ir.Name // an ONAME *ir.Name representing a stack location.
Type *types.Type // type of slot
diff --git a/src/cmd/compile/internal/ssa/loopbce.go b/src/cmd/compile/internal/ssa/loopbce.go
index 206aab2c5ef..dd63541771d 100644
--- a/src/cmd/compile/internal/ssa/loopbce.go
+++ b/src/cmd/compile/internal/ssa/loopbce.go
@@ -31,9 +31,10 @@ type indVar struct {
// parseIndVar checks whether the SSA value passed as argument is a valid induction
// variable, and, if so, extracts:
-// * the minimum bound
-// * the increment value
-// * the "next" value (SSA value that is Phi'd into the induction variable every loop)
+// - the minimum bound
+// - the increment value
+// - the "next" value (SSA value that is Phi'd into the induction variable every loop)
+//
// Currently, we detect induction variables that match (Phi min nxt),
// with nxt being (Add inc ind).
// If it can't parse the induction variable correctly, it returns (nil, nil, nil).
@@ -66,19 +67,18 @@ func parseIndVar(ind *Value) (min, inc, nxt *Value) {
//
// Look for variables and blocks that satisfy the following
//
-// loop:
-// ind = (Phi min nxt),
-// if ind < max
-// then goto enter_loop
-// else goto exit_loop
-//
-// enter_loop:
-// do something
-// nxt = inc + ind
-// goto loop
+// loop:
+// ind = (Phi min nxt),
+// if ind < max
+// then goto enter_loop
+// else goto exit_loop
//
-// exit_loop:
+// enter_loop:
+// do something
+// nxt = inc + ind
+// goto loop
//
+// exit_loop:
//
// TODO: handle 32 bit operations
func findIndVar(f *Func) []indVar {
diff --git a/src/cmd/compile/internal/ssa/looprotate.go b/src/cmd/compile/internal/ssa/looprotate.go
index 35010a78d8e..2eefda1c8b3 100644
--- a/src/cmd/compile/internal/ssa/looprotate.go
+++ b/src/cmd/compile/internal/ssa/looprotate.go
@@ -8,19 +8,19 @@ package ssa
// to loops with a check-loop-condition-at-end.
// This helps loops avoid extra unnecessary jumps.
//
-// loop:
-// CMPQ ...
-// JGE exit
-// ...
-// JMP loop
-// exit:
+// loop:
+// CMPQ ...
+// JGE exit
+// ...
+// JMP loop
+// exit:
//
-// JMP entry
-// loop:
-// ...
-// entry:
-// CMPQ ...
-// JLT loop
+// JMP entry
+// loop:
+// ...
+// entry:
+// CMPQ ...
+// JLT loop
func loopRotate(f *Func) {
loopnest := f.loopnest()
if loopnest.hasIrreducible {
diff --git a/src/cmd/compile/internal/ssa/magic.go b/src/cmd/compile/internal/ssa/magic.go
index 93f8801bce4..e903d92bb61 100644
--- a/src/cmd/compile/internal/ssa/magic.go
+++ b/src/cmd/compile/internal/ssa/magic.go
@@ -110,7 +110,8 @@ type umagicData struct {
// umagic computes the constants needed to strength reduce unsigned n-bit divides by the constant uint64(c).
// The return values satisfy for all 0 <= x < 2^n
-// floor(x / uint64(c)) = x * (m + 2^n) >> (n+s)
+//
+// floor(x / uint64(c)) = x * (m + 2^n) >> (n+s)
func umagic(n uint, c int64) umagicData {
// Convert from ConstX auxint values to the real uint64 constant they represent.
d := uint64(c) << (64 - n) >> (64 - n)
@@ -183,7 +184,8 @@ type smagicData struct {
// magic computes the constants needed to strength reduce signed n-bit divides by the constant c.
// Must have c>0.
// The return values satisfy for all -2^(n-1) <= x < 2^(n-1)
-// trunc(x / c) = x * m >> (n+s) + (x < 0 ? 1 : 0)
+//
+// trunc(x / c) = x * m >> (n+s) + (x < 0 ? 1 : 0)
func smagic(n uint, c int64) smagicData {
C := new(big.Int).SetInt64(c)
s := C.BitLen() - 1
diff --git a/src/cmd/compile/internal/ssa/op.go b/src/cmd/compile/internal/ssa/op.go
index a1835dcd302..a3e8dcd2f62 100644
--- a/src/cmd/compile/internal/ssa/op.go
+++ b/src/cmd/compile/internal/ssa/op.go
@@ -391,9 +391,9 @@ const (
// A Sym represents a symbolic offset from a base register.
// Currently a Sym can be one of 3 things:
-// - a *gc.Node, for an offset from SP (the stack pointer)
-// - a *obj.LSym, for an offset from SB (the global pointer)
-// - nil, for no offset
+// - a *gc.Node, for an offset from SP (the stack pointer)
+// - a *obj.LSym, for an offset from SB (the global pointer)
+// - nil, for no offset
type Sym interface {
CanBeAnSSASym()
CanBeAnSSAAux()
@@ -479,12 +479,13 @@ const (
)
// boundsAPI determines which register arguments a bounds check call should use. For an [a:b:c] slice, we do:
-// CMPQ c, cap
-// JA fail1
-// CMPQ b, c
-// JA fail2
-// CMPQ a, b
-// JA fail3
+//
+// CMPQ c, cap
+// JA fail1
+// CMPQ b, c
+// JA fail2
+// CMPQ a, b
+// JA fail3
//
// fail1: CALL panicSlice3Acap (c, cap)
// fail2: CALL panicSlice3B (b, c)
diff --git a/src/cmd/compile/internal/ssa/opGen.go b/src/cmd/compile/internal/ssa/opGen.go
index 005a033a409..0357fdb12ab 100644
--- a/src/cmd/compile/internal/ssa/opGen.go
+++ b/src/cmd/compile/internal/ssa/opGen.go
@@ -50,6 +50,7 @@ const (
BlockAMD64NEF
BlockAMD64ORD
BlockAMD64NAN
+ BlockAMD64JUMPTABLE
BlockARMEQ
BlockARMNE
@@ -149,6 +150,7 @@ const (
BlockRet
BlockRetJmp
BlockExit
+ BlockJumpTable
BlockFirst
)
@@ -172,22 +174,23 @@ var blockString = [...]string{
Block386ORD: "ORD",
Block386NAN: "NAN",
- BlockAMD64EQ: "EQ",
- BlockAMD64NE: "NE",
- BlockAMD64LT: "LT",
- BlockAMD64LE: "LE",
- BlockAMD64GT: "GT",
- BlockAMD64GE: "GE",
- BlockAMD64OS: "OS",
- BlockAMD64OC: "OC",
- BlockAMD64ULT: "ULT",
- BlockAMD64ULE: "ULE",
- BlockAMD64UGT: "UGT",
- BlockAMD64UGE: "UGE",
- BlockAMD64EQF: "EQF",
- BlockAMD64NEF: "NEF",
- BlockAMD64ORD: "ORD",
- BlockAMD64NAN: "NAN",
+ BlockAMD64EQ: "EQ",
+ BlockAMD64NE: "NE",
+ BlockAMD64LT: "LT",
+ BlockAMD64LE: "LE",
+ BlockAMD64GT: "GT",
+ BlockAMD64GE: "GE",
+ BlockAMD64OS: "OS",
+ BlockAMD64OC: "OC",
+ BlockAMD64ULT: "ULT",
+ BlockAMD64ULE: "ULE",
+ BlockAMD64UGT: "UGT",
+ BlockAMD64UGE: "UGE",
+ BlockAMD64EQF: "EQF",
+ BlockAMD64NEF: "NEF",
+ BlockAMD64ORD: "ORD",
+ BlockAMD64NAN: "NAN",
+ BlockAMD64JUMPTABLE: "JUMPTABLE",
BlockARMEQ: "EQ",
BlockARMNE: "NE",
@@ -281,13 +284,14 @@ var blockString = [...]string{
BlockS390XCLIJ: "CLIJ",
BlockS390XCLGIJ: "CLGIJ",
- BlockPlain: "Plain",
- BlockIf: "If",
- BlockDefer: "Defer",
- BlockRet: "Ret",
- BlockRetJmp: "RetJmp",
- BlockExit: "Exit",
- BlockFirst: "First",
+ BlockPlain: "Plain",
+ BlockIf: "If",
+ BlockDefer: "Defer",
+ BlockRet: "Ret",
+ BlockRetJmp: "RetJmp",
+ BlockExit: "Exit",
+ BlockJumpTable: "JumpTable",
+ BlockFirst: "First",
}
func (k BlockKind) String() string { return blockString[k] }
@@ -1050,10 +1054,35 @@ const (
OpAMD64MOVBELstore
OpAMD64MOVBEQload
OpAMD64MOVBEQstore
+ OpAMD64MOVBELloadidx1
+ OpAMD64MOVBELloadidx4
+ OpAMD64MOVBELloadidx8
+ OpAMD64MOVBEQloadidx1
+ OpAMD64MOVBEQloadidx8
+ OpAMD64MOVBEWstoreidx1
+ OpAMD64MOVBEWstoreidx2
+ OpAMD64MOVBELstoreidx1
+ OpAMD64MOVBELstoreidx4
+ OpAMD64MOVBELstoreidx8
+ OpAMD64MOVBEQstoreidx1
+ OpAMD64MOVBEQstoreidx8
+ OpAMD64SARXQ
+ OpAMD64SARXL
+ OpAMD64SHLXQ
+ OpAMD64SHLXL
+ OpAMD64SHRXQ
+ OpAMD64SHRXL
+ OpAMD64SARXLload
+ OpAMD64SARXQload
OpAMD64SHLXLload
OpAMD64SHLXQload
OpAMD64SHRXLload
OpAMD64SHRXQload
+ OpAMD64SARXLloadidx1
+ OpAMD64SARXLloadidx4
+ OpAMD64SARXLloadidx8
+ OpAMD64SARXQloadidx1
+ OpAMD64SARXQloadidx8
OpAMD64SHLXLloadidx1
OpAMD64SHLXLloadidx4
OpAMD64SHLXLloadidx8
@@ -13911,6 +13940,319 @@ var opcodeTable = [...]opInfo{
},
},
{
+ name: "MOVBELloadidx1",
+ auxType: auxSymOff,
+ argLen: 3,
+ commutative: true,
+ symEffect: SymRead,
+ asm: x86.AMOVBEL,
+ scale: 1,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
+ },
+ outputs: []outputInfo{
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ },
+ },
+ },
+ {
+ name: "MOVBELloadidx4",
+ auxType: auxSymOff,
+ argLen: 3,
+ symEffect: SymRead,
+ asm: x86.AMOVBEL,
+ scale: 4,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
+ },
+ outputs: []outputInfo{
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ },
+ },
+ },
+ {
+ name: "MOVBELloadidx8",
+ auxType: auxSymOff,
+ argLen: 3,
+ symEffect: SymRead,
+ asm: x86.AMOVBEL,
+ scale: 8,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
+ },
+ outputs: []outputInfo{
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ },
+ },
+ },
+ {
+ name: "MOVBEQloadidx1",
+ auxType: auxSymOff,
+ argLen: 3,
+ commutative: true,
+ symEffect: SymRead,
+ asm: x86.AMOVBEQ,
+ scale: 1,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
+ },
+ outputs: []outputInfo{
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ },
+ },
+ },
+ {
+ name: "MOVBEQloadidx8",
+ auxType: auxSymOff,
+ argLen: 3,
+ symEffect: SymRead,
+ asm: x86.AMOVBEQ,
+ scale: 8,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
+ },
+ outputs: []outputInfo{
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ },
+ },
+ },
+ {
+ name: "MOVBEWstoreidx1",
+ auxType: auxSymOff,
+ argLen: 4,
+ commutative: true,
+ symEffect: SymWrite,
+ asm: x86.AMOVBEW,
+ scale: 1,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
+ },
+ },
+ },
+ {
+ name: "MOVBEWstoreidx2",
+ auxType: auxSymOff,
+ argLen: 4,
+ symEffect: SymWrite,
+ asm: x86.AMOVBEW,
+ scale: 2,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
+ },
+ },
+ },
+ {
+ name: "MOVBELstoreidx1",
+ auxType: auxSymOff,
+ argLen: 4,
+ commutative: true,
+ symEffect: SymWrite,
+ asm: x86.AMOVBEL,
+ scale: 1,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
+ },
+ },
+ },
+ {
+ name: "MOVBELstoreidx4",
+ auxType: auxSymOff,
+ argLen: 4,
+ symEffect: SymWrite,
+ asm: x86.AMOVBEL,
+ scale: 4,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
+ },
+ },
+ },
+ {
+ name: "MOVBELstoreidx8",
+ auxType: auxSymOff,
+ argLen: 4,
+ symEffect: SymWrite,
+ asm: x86.AMOVBEL,
+ scale: 8,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
+ },
+ },
+ },
+ {
+ name: "MOVBEQstoreidx1",
+ auxType: auxSymOff,
+ argLen: 4,
+ commutative: true,
+ symEffect: SymWrite,
+ asm: x86.AMOVBEQ,
+ scale: 1,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
+ },
+ },
+ },
+ {
+ name: "MOVBEQstoreidx8",
+ auxType: auxSymOff,
+ argLen: 4,
+ symEffect: SymWrite,
+ asm: x86.AMOVBEQ,
+ scale: 8,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {2, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
+ },
+ },
+ },
+ {
+ name: "SARXQ",
+ argLen: 2,
+ asm: x86.ASARXQ,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ },
+ outputs: []outputInfo{
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ },
+ },
+ },
+ {
+ name: "SARXL",
+ argLen: 2,
+ asm: x86.ASARXL,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ },
+ outputs: []outputInfo{
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ },
+ },
+ },
+ {
+ name: "SHLXQ",
+ argLen: 2,
+ asm: x86.ASHLXQ,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ },
+ outputs: []outputInfo{
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ },
+ },
+ },
+ {
+ name: "SHLXL",
+ argLen: 2,
+ asm: x86.ASHLXL,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ },
+ outputs: []outputInfo{
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ },
+ },
+ },
+ {
+ name: "SHRXQ",
+ argLen: 2,
+ asm: x86.ASHRXQ,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ },
+ outputs: []outputInfo{
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ },
+ },
+ },
+ {
+ name: "SHRXL",
+ argLen: 2,
+ asm: x86.ASHRXL,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ },
+ outputs: []outputInfo{
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ },
+ },
+ },
+ {
+ name: "SARXLload",
+ auxType: auxSymOff,
+ argLen: 3,
+ faultOnNilArg0: true,
+ symEffect: SymRead,
+ asm: x86.ASARXL,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
+ },
+ outputs: []outputInfo{
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ },
+ },
+ },
+ {
+ name: "SARXQload",
+ auxType: auxSymOff,
+ argLen: 3,
+ faultOnNilArg0: true,
+ symEffect: SymRead,
+ asm: x86.ASARXQ,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {1, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
+ },
+ outputs: []outputInfo{
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ },
+ },
+ },
+ {
name: "SHLXLload",
auxType: auxSymOff,
argLen: 3,
@@ -13979,6 +14321,101 @@ var opcodeTable = [...]opInfo{
},
},
{
+ name: "SARXLloadidx1",
+ auxType: auxSymOff,
+ argLen: 4,
+ faultOnNilArg0: true,
+ symEffect: SymRead,
+ asm: x86.ASARXL,
+ scale: 1,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {2, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
+ },
+ outputs: []outputInfo{
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ },
+ },
+ },
+ {
+ name: "SARXLloadidx4",
+ auxType: auxSymOff,
+ argLen: 4,
+ faultOnNilArg0: true,
+ symEffect: SymRead,
+ asm: x86.ASARXL,
+ scale: 4,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {2, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
+ },
+ outputs: []outputInfo{
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ },
+ },
+ },
+ {
+ name: "SARXLloadidx8",
+ auxType: auxSymOff,
+ argLen: 4,
+ faultOnNilArg0: true,
+ symEffect: SymRead,
+ asm: x86.ASARXL,
+ scale: 8,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {2, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
+ },
+ outputs: []outputInfo{
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ },
+ },
+ },
+ {
+ name: "SARXQloadidx1",
+ auxType: auxSymOff,
+ argLen: 4,
+ faultOnNilArg0: true,
+ symEffect: SymRead,
+ asm: x86.ASARXQ,
+ scale: 1,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {2, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
+ },
+ outputs: []outputInfo{
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ },
+ },
+ },
+ {
+ name: "SARXQloadidx8",
+ auxType: auxSymOff,
+ argLen: 4,
+ faultOnNilArg0: true,
+ symEffect: SymRead,
+ asm: x86.ASARXQ,
+ scale: 8,
+ reg: regInfo{
+ inputs: []inputInfo{
+ {2, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {1, 49151}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 R15
+ {0, 4295032831}, // AX CX DX BX SP BP SI DI R8 R9 R10 R11 R12 R13 g R15 SB
+ },
+ outputs: []outputInfo{
+ {0, 49135}, // AX CX DX BX BP SI DI R8 R9 R10 R11 R12 R13 R15
+ },
+ },
+ },
+ {
name: "SHLXLloadidx1",
auxType: auxSymOff,
argLen: 4,
diff --git a/src/cmd/compile/internal/ssa/phielim.go b/src/cmd/compile/internal/ssa/phielim.go
index 761cb7a392f..4fc942375fd 100644
--- a/src/cmd/compile/internal/ssa/phielim.go
+++ b/src/cmd/compile/internal/ssa/phielim.go
@@ -8,13 +8,19 @@ package ssa
// A phi is redundant if its arguments are all equal. For
// purposes of counting, ignore the phi itself. Both of
// these phis are redundant:
-// v = phi(x,x,x)
-// v = phi(x,v,x,v)
+//
+// v = phi(x,x,x)
+// v = phi(x,v,x,v)
+//
// We repeat this process to also catch situations like:
-// v = phi(x, phi(x, x), phi(x, v))
+//
+// v = phi(x, phi(x, x), phi(x, v))
+//
// TODO: Can we also simplify cases like:
-// v = phi(v, w, x)
-// w = phi(v, w, x)
+//
+// v = phi(v, w, x)
+// w = phi(v, w, x)
+//
// and would that be useful?
func phielim(f *Func) {
for {
diff --git a/src/cmd/compile/internal/ssa/phiopt.go b/src/cmd/compile/internal/ssa/phiopt.go
index 0357442ae9f..037845eacf2 100644
--- a/src/cmd/compile/internal/ssa/phiopt.go
+++ b/src/cmd/compile/internal/ssa/phiopt.go
@@ -7,20 +7,22 @@ package ssa
// phiopt eliminates boolean Phis based on the previous if.
//
// Main use case is to transform:
-// x := false
-// if b {
-// x = true
-// }
+//
+// x := false
+// if b {
+// x = true
+// }
+//
// into x = b.
//
// In SSA code this appears as
//
-// b0
-// If b -> b1 b2
-// b1
-// Plain -> b2
-// b2
-// x = (OpPhi (ConstBool [true]) (ConstBool [false]))
+// b0
+// If b -> b1 b2
+// b1
+// Plain -> b2
+// b2
+// x = (OpPhi (ConstBool [true]) (ConstBool [false]))
//
// In this case we can replace x with a copy of b.
func phiopt(f *Func) {
diff --git a/src/cmd/compile/internal/ssa/poset.go b/src/cmd/compile/internal/ssa/poset.go
index 200106e66db..a3b4f0fea48 100644
--- a/src/cmd/compile/internal/ssa/poset.go
+++ b/src/cmd/compile/internal/ssa/poset.go
@@ -140,11 +140,11 @@ type posetNode struct {
// to record that A<I, A<J, A<K (with no known relation between I,J,K), we create the
// following DAG:
//
-// A
-// / \
-// I extra
-// / \
-// J K
+// A
+// / \
+// I extra
+// / \
+// J K
type poset struct {
lastidx uint32 // last generated dense index
flags uint8 // internal flags
diff --git a/src/cmd/compile/internal/ssa/prove.go b/src/cmd/compile/internal/ssa/prove.go
index d0c9a190adc..26176af07c8 100644
--- a/src/cmd/compile/internal/ssa/prove.go
+++ b/src/cmd/compile/internal/ssa/prove.go
@@ -16,6 +16,10 @@ const (
unknown branch = iota
positive
negative
+ // The outedges from a jump table are jumpTable0,
+ // jumpTable0+1, jumpTable0+2, etc. There could be an
+ // arbitrary number so we can't list them all here.
+ jumpTable0
)
// relation represents the set of possible relations between
@@ -27,17 +31,17 @@ const (
//
// E.g.
//
-// r := relation(...)
+// r := relation(...)
//
-// if v < w {
-// newR := r & lt
-// }
-// if v >= w {
-// newR := r & (eq|gt)
-// }
-// if v != w {
-// newR := r & (lt|gt)
-// }
+// if v < w {
+// newR := r & lt
+// }
+// if v >= w {
+// newR := r & (eq|gt)
+// }
+// if v != w {
+// newR := r & (lt|gt)
+// }
type relation uint
const (
@@ -746,19 +750,19 @@ func (ft *factsTable) cleanup(f *Func) {
// By far, the most common redundant pair are generated by bounds checking.
// For example for the code:
//
-// a[i] = 4
-// foo(a[i])
+// a[i] = 4
+// foo(a[i])
//
// The compiler will generate the following code:
//
-// if i >= len(a) {
-// panic("not in bounds")
-// }
-// a[i] = 4
-// if i >= len(a) {
-// panic("not in bounds")
-// }
-// foo(a[i])
+// if i >= len(a) {
+// panic("not in bounds")
+// }
+// a[i] = 4
+// if i >= len(a) {
+// panic("not in bounds")
+// }
+// foo(a[i])
//
// The second comparison i >= len(a) is clearly redundant because if the
// else branch of the first comparison is executed, we already know that i < len(a).
@@ -940,20 +944,31 @@ func prove(f *Func) {
// getBranch returns the range restrictions added by p
// when reaching b. p is the immediate dominator of b.
func getBranch(sdom SparseTree, p *Block, b *Block) branch {
- if p == nil || p.Kind != BlockIf {
+ if p == nil {
return unknown
}
- // If p and p.Succs[0] are dominators it means that every path
- // from entry to b passes through p and p.Succs[0]. We care that
- // no path from entry to b passes through p.Succs[1]. If p.Succs[0]
- // has one predecessor then (apart from the degenerate case),
- // there is no path from entry that can reach b through p.Succs[1].
- // TODO: how about p->yes->b->yes, i.e. a loop in yes.
- if sdom.IsAncestorEq(p.Succs[0].b, b) && len(p.Succs[0].b.Preds) == 1 {
- return positive
- }
- if sdom.IsAncestorEq(p.Succs[1].b, b) && len(p.Succs[1].b.Preds) == 1 {
- return negative
+ switch p.Kind {
+ case BlockIf:
+ // If p and p.Succs[0] are dominators it means that every path
+ // from entry to b passes through p and p.Succs[0]. We care that
+ // no path from entry to b passes through p.Succs[1]. If p.Succs[0]
+ // has one predecessor then (apart from the degenerate case),
+ // there is no path from entry that can reach b through p.Succs[1].
+ // TODO: how about p->yes->b->yes, i.e. a loop in yes.
+ if sdom.IsAncestorEq(p.Succs[0].b, b) && len(p.Succs[0].b.Preds) == 1 {
+ return positive
+ }
+ if sdom.IsAncestorEq(p.Succs[1].b, b) && len(p.Succs[1].b.Preds) == 1 {
+ return negative
+ }
+ case BlockJumpTable:
+ // TODO: this loop can lead to quadratic behavior, as
+ // getBranch can be called len(p.Succs) times.
+ for i, e := range p.Succs {
+ if sdom.IsAncestorEq(e.b, b) && len(e.b.Preds) == 1 {
+ return jumpTable0 + branch(i)
+ }
+ }
}
return unknown
}
@@ -984,11 +999,36 @@ func addIndVarRestrictions(ft *factsTable, b *Block, iv indVar) {
// branching from Block b in direction br.
func addBranchRestrictions(ft *factsTable, b *Block, br branch) {
c := b.Controls[0]
- switch br {
- case negative:
+ switch {
+ case br == negative:
addRestrictions(b, ft, boolean, nil, c, eq)
- case positive:
+ case br == positive:
addRestrictions(b, ft, boolean, nil, c, lt|gt)
+ case br >= jumpTable0:
+ idx := br - jumpTable0
+ val := int64(idx)
+ if v, off := isConstDelta(c); v != nil {
+ // Establish the bound on the underlying value we're switching on,
+ // not on the offset-ed value used as the jump table index.
+ c = v
+ val -= off
+ }
+ old, ok := ft.limits[c.ID]
+ if !ok {
+ old = noLimit
+ }
+ ft.limitStack = append(ft.limitStack, limitFact{c.ID, old})
+ if val < old.min || val > old.max || uint64(val) < old.umin || uint64(val) > old.umax {
+ ft.unsat = true
+ if b.Func.pass.debug > 2 {
+ b.Func.Warnl(b.Pos, "block=%s outedge=%d %s=%d unsat", b, idx, c, val)
+ }
+ } else {
+ ft.limits[c.ID] = limit{val, val, uint64(val), uint64(val)}
+ if b.Func.pass.debug > 2 {
+ b.Func.Warnl(b.Pos, "block=%s outedge=%d %s=%d", b, idx, c, val)
+ }
+ }
default:
panic("unknown branch")
}
@@ -1343,10 +1383,14 @@ func removeBranch(b *Block, branch branch) {
// attempt to preserve statement marker.
b.Pos = b.Pos.WithIsStmt()
}
- b.Kind = BlockFirst
- b.ResetControls()
- if branch == positive {
- b.swapSuccessors()
+ if branch == positive || branch == negative {
+ b.Kind = BlockFirst
+ b.ResetControls()
+ if branch == positive {
+ b.swapSuccessors()
+ }
+ } else {
+ // TODO: figure out how to remove an entry from a jump table
}
}
diff --git a/src/cmd/compile/internal/ssa/rewrite.go b/src/cmd/compile/internal/ssa/rewrite.go
index eb8fa0c02ab..4d615a064d9 100644
--- a/src/cmd/compile/internal/ssa/rewrite.go
+++ b/src/cmd/compile/internal/ssa/rewrite.go
@@ -5,6 +5,7 @@
package ssa
import (
+ "cmd/compile/internal/base"
"cmd/compile/internal/logopt"
"cmd/compile/internal/types"
"cmd/internal/obj"
@@ -962,8 +963,9 @@ found:
// clobber invalidates values. Returns true.
// clobber is used by rewrite rules to:
-// A) make sure the values are really dead and never used again.
-// B) decrement use counts of the values' args.
+//
+// A) make sure the values are really dead and never used again.
+// B) decrement use counts of the values' args.
func clobber(vv ...*Value) bool {
for _, v := range vv {
v.reset(OpInvalid)
@@ -985,7 +987,9 @@ func clobberIfDead(v *Value) bool {
// noteRule is an easy way to track if a rule is matched when writing
// new ones. Make the rule of interest also conditional on
-// noteRule("note to self: rule of interest matched")
+//
+// noteRule("note to self: rule of interest matched")
+//
// and that message will print when the rule matches.
func noteRule(s string) bool {
fmt.Println(s)
@@ -1789,9 +1793,11 @@ func sequentialAddresses(x, y *Value, n int64) bool {
// We happen to match the semantics to those of arm/arm64.
// Note that these semantics differ from x86: the carry flag has the opposite
// sense on a subtraction!
-// On amd64, C=1 represents a borrow, e.g. SBB on amd64 does x - y - C.
-// On arm64, C=0 represents a borrow, e.g. SBC on arm64 does x - y - ^C.
-// (because it does x + ^y + C).
+//
+// On amd64, C=1 represents a borrow, e.g. SBB on amd64 does x - y - C.
+// On arm64, C=0 represents a borrow, e.g. SBC on arm64 does x - y - ^C.
+// (because it does x + ^y + C).
+//
// See https://en.wikipedia.org/wiki/Carry_flag#Vs._borrow_flag
type flagConstant uint8
@@ -1949,3 +1955,9 @@ func logicFlags32(x int32) flagConstant {
fcb.N = x < 0
return fcb.encode()
}
+
+func makeJumpTableSym(b *Block) *obj.LSym {
+ s := base.Ctxt.Lookup(fmt.Sprintf("%s.jump%d", b.Func.fe.LSym(), b.ID))
+ s.Set(obj.AttrDuplicateOK, true)
+ return s
+}
diff --git a/src/cmd/compile/internal/ssa/rewriteAMD64.go b/src/cmd/compile/internal/ssa/rewriteAMD64.go
index addfaaa3a85..36e69781a5f 100644
--- a/src/cmd/compile/internal/ssa/rewriteAMD64.go
+++ b/src/cmd/compile/internal/ssa/rewriteAMD64.go
@@ -382,6 +382,14 @@ func rewriteValueAMD64(v *Value) bool {
return rewriteValueAMD64_OpAMD64SARW(v)
case OpAMD64SARWconst:
return rewriteValueAMD64_OpAMD64SARWconst(v)
+ case OpAMD64SARXL:
+ return rewriteValueAMD64_OpAMD64SARXL(v)
+ case OpAMD64SARXLload:
+ return rewriteValueAMD64_OpAMD64SARXLload(v)
+ case OpAMD64SARXQ:
+ return rewriteValueAMD64_OpAMD64SARXQ(v)
+ case OpAMD64SARXQload:
+ return rewriteValueAMD64_OpAMD64SARXQload(v)
case OpAMD64SBBLcarrymask:
return rewriteValueAMD64_OpAMD64SBBLcarrymask(v)
case OpAMD64SBBQ:
@@ -438,6 +446,14 @@ func rewriteValueAMD64(v *Value) bool {
return rewriteValueAMD64_OpAMD64SHLQ(v)
case OpAMD64SHLQconst:
return rewriteValueAMD64_OpAMD64SHLQconst(v)
+ case OpAMD64SHLXL:
+ return rewriteValueAMD64_OpAMD64SHLXL(v)
+ case OpAMD64SHLXLload:
+ return rewriteValueAMD64_OpAMD64SHLXLload(v)
+ case OpAMD64SHLXQ:
+ return rewriteValueAMD64_OpAMD64SHLXQ(v)
+ case OpAMD64SHLXQload:
+ return rewriteValueAMD64_OpAMD64SHLXQload(v)
case OpAMD64SHRB:
return rewriteValueAMD64_OpAMD64SHRB(v)
case OpAMD64SHRBconst:
@@ -454,6 +470,14 @@ func rewriteValueAMD64(v *Value) bool {
return rewriteValueAMD64_OpAMD64SHRW(v)
case OpAMD64SHRWconst:
return rewriteValueAMD64_OpAMD64SHRWconst(v)
+ case OpAMD64SHRXL:
+ return rewriteValueAMD64_OpAMD64SHRXL(v)
+ case OpAMD64SHRXLload:
+ return rewriteValueAMD64_OpAMD64SHRXLload(v)
+ case OpAMD64SHRXQ:
+ return rewriteValueAMD64_OpAMD64SHRXQ(v)
+ case OpAMD64SHRXQload:
+ return rewriteValueAMD64_OpAMD64SHRXQload(v)
case OpAMD64SUBL:
return rewriteValueAMD64_OpAMD64SUBL(v)
case OpAMD64SUBLconst:
@@ -2700,6 +2724,29 @@ func rewriteValueAMD64_OpAMD64ANDL(v *Value) bool {
}
break
}
+ // match: (ANDL (NOTL (SHLXL (MOVLconst [1]) y)) x)
+ // result: (BTRL x y)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ if v_0.Op != OpAMD64NOTL {
+ continue
+ }
+ v_0_0 := v_0.Args[0]
+ if v_0_0.Op != OpAMD64SHLXL {
+ continue
+ }
+ y := v_0_0.Args[1]
+ v_0_0_0 := v_0_0.Args[0]
+ if v_0_0_0.Op != OpAMD64MOVLconst || auxIntToInt32(v_0_0_0.AuxInt) != 1 {
+ continue
+ }
+ x := v_1
+ v.reset(OpAMD64BTRL)
+ v.AddArg2(x, y)
+ return true
+ }
+ break
+ }
// match: (ANDL (MOVLconst [c]) x)
// cond: isUint32PowerOfTwo(int64(^c)) && uint64(^c) >= 128
// result: (BTRLconst [int8(log32(^c))] x)
@@ -3117,6 +3164,22 @@ func rewriteValueAMD64_OpAMD64ANDNL(v *Value) bool {
v.AddArg2(x, y)
return true
}
+ // match: (ANDNL x (SHLXL (MOVLconst [1]) y))
+ // result: (BTRL x y)
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64SHLXL {
+ break
+ }
+ y := v_1.Args[1]
+ v_1_0 := v_1.Args[0]
+ if v_1_0.Op != OpAMD64MOVLconst || auxIntToInt32(v_1_0.AuxInt) != 1 {
+ break
+ }
+ v.reset(OpAMD64BTRL)
+ v.AddArg2(x, y)
+ return true
+ }
return false
}
func rewriteValueAMD64_OpAMD64ANDNQ(v *Value) bool {
@@ -3138,6 +3201,22 @@ func rewriteValueAMD64_OpAMD64ANDNQ(v *Value) bool {
v.AddArg2(x, y)
return true
}
+ // match: (ANDNQ x (SHLXQ (MOVQconst [1]) y))
+ // result: (BTRQ x y)
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64SHLXQ {
+ break
+ }
+ y := v_1.Args[1]
+ v_1_0 := v_1.Args[0]
+ if v_1_0.Op != OpAMD64MOVQconst || auxIntToInt64(v_1_0.AuxInt) != 1 {
+ break
+ }
+ v.reset(OpAMD64BTRQ)
+ v.AddArg2(x, y)
+ return true
+ }
return false
}
func rewriteValueAMD64_OpAMD64ANDQ(v *Value) bool {
@@ -3166,6 +3245,29 @@ func rewriteValueAMD64_OpAMD64ANDQ(v *Value) bool {
}
break
}
+ // match: (ANDQ (NOTQ (SHLXQ (MOVQconst [1]) y)) x)
+ // result: (BTRQ x y)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ if v_0.Op != OpAMD64NOTQ {
+ continue
+ }
+ v_0_0 := v_0.Args[0]
+ if v_0_0.Op != OpAMD64SHLXQ {
+ continue
+ }
+ y := v_0_0.Args[1]
+ v_0_0_0 := v_0_0.Args[0]
+ if v_0_0_0.Op != OpAMD64MOVQconst || auxIntToInt64(v_0_0_0.AuxInt) != 1 {
+ continue
+ }
+ x := v_1
+ v.reset(OpAMD64BTRQ)
+ v.AddArg2(x, y)
+ return true
+ }
+ break
+ }
// match: (ANDQ (MOVQconst [c]) x)
// cond: isUint64PowerOfTwo(^c) && uint64(^c) >= 128
// result: (BTRQconst [int8(log64(^c))] x)
@@ -3869,6 +3971,22 @@ func rewriteValueAMD64_OpAMD64BTLconst(v *Value) bool {
v.AddArg2(y, x)
return true
}
+ // match: (BTLconst [0] s:(SHRXQ x y))
+ // result: (BTQ y x)
+ for {
+ if auxIntToInt8(v.AuxInt) != 0 {
+ break
+ }
+ s := v_0
+ if s.Op != OpAMD64SHRXQ {
+ break
+ }
+ y := s.Args[1]
+ x := s.Args[0]
+ v.reset(OpAMD64BTQ)
+ v.AddArg2(y, x)
+ return true
+ }
// match: (BTLconst [c] (SHRLconst [d] x))
// cond: (c+d)<32
// result: (BTLconst [c+d] x)
@@ -3921,6 +4039,22 @@ func rewriteValueAMD64_OpAMD64BTLconst(v *Value) bool {
v.AddArg2(y, x)
return true
}
+ // match: (BTLconst [0] s:(SHRXL x y))
+ // result: (BTL y x)
+ for {
+ if auxIntToInt8(v.AuxInt) != 0 {
+ break
+ }
+ s := v_0
+ if s.Op != OpAMD64SHRXL {
+ break
+ }
+ y := s.Args[1]
+ x := s.Args[0]
+ v.reset(OpAMD64BTL)
+ v.AddArg2(y, x)
+ return true
+ }
return false
}
func rewriteValueAMD64_OpAMD64BTQconst(v *Value) bool {
@@ -3977,6 +4111,22 @@ func rewriteValueAMD64_OpAMD64BTQconst(v *Value) bool {
v.AddArg2(y, x)
return true
}
+ // match: (BTQconst [0] s:(SHRXQ x y))
+ // result: (BTQ y x)
+ for {
+ if auxIntToInt8(v.AuxInt) != 0 {
+ break
+ }
+ s := v_0
+ if s.Op != OpAMD64SHRXQ {
+ break
+ }
+ y := s.Args[1]
+ x := s.Args[0]
+ v.reset(OpAMD64BTQ)
+ v.AddArg2(y, x)
+ return true
+ }
return false
}
func rewriteValueAMD64_OpAMD64BTRLconst(v *Value) bool {
@@ -15886,6 +16036,25 @@ func rewriteValueAMD64_OpAMD64ORL(v *Value) bool {
}
break
}
+ // match: (ORL (SHLXL (MOVLconst [1]) y) x)
+ // result: (BTSL x y)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ if v_0.Op != OpAMD64SHLXL {
+ continue
+ }
+ y := v_0.Args[1]
+ v_0_0 := v_0.Args[0]
+ if v_0_0.Op != OpAMD64MOVLconst || auxIntToInt32(v_0_0.AuxInt) != 1 {
+ continue
+ }
+ x := v_1
+ v.reset(OpAMD64BTSL)
+ v.AddArg2(x, y)
+ return true
+ }
+ break
+ }
// match: (ORL (MOVLconst [c]) x)
// cond: isUint32PowerOfTwo(int64(c)) && uint64(c) >= 128
// result: (BTSLconst [int8(log32(c))] x)
@@ -16196,6 +16365,206 @@ func rewriteValueAMD64_OpAMD64ORL(v *Value) bool {
}
break
}
+ // match: (ORL (SHLXL x y) (ANDL (SHRXL x (NEGQ y)) (SBBLcarrymask (CMPQconst (NEGQ (ADDQconst (ANDQconst y [31]) [-32])) [32]))))
+ // result: (ROLL x y)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ if v_0.Op != OpAMD64SHLXL {
+ continue
+ }
+ y := v_0.Args[1]
+ x := v_0.Args[0]
+ if v_1.Op != OpAMD64ANDL {
+ continue
+ }
+ _ = v_1.Args[1]
+ v_1_0 := v_1.Args[0]
+ v_1_1 := v_1.Args[1]
+ for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 {
+ if v_1_0.Op != OpAMD64SHRXL {
+ continue
+ }
+ _ = v_1_0.Args[1]
+ if x != v_1_0.Args[0] {
+ continue
+ }
+ v_1_0_1 := v_1_0.Args[1]
+ if v_1_0_1.Op != OpAMD64NEGQ || y != v_1_0_1.Args[0] || v_1_1.Op != OpAMD64SBBLcarrymask {
+ continue
+ }
+ v_1_1_0 := v_1_1.Args[0]
+ if v_1_1_0.Op != OpAMD64CMPQconst || auxIntToInt32(v_1_1_0.AuxInt) != 32 {
+ continue
+ }
+ v_1_1_0_0 := v_1_1_0.Args[0]
+ if v_1_1_0_0.Op != OpAMD64NEGQ {
+ continue
+ }
+ v_1_1_0_0_0 := v_1_1_0_0.Args[0]
+ if v_1_1_0_0_0.Op != OpAMD64ADDQconst || auxIntToInt32(v_1_1_0_0_0.AuxInt) != -32 {
+ continue
+ }
+ v_1_1_0_0_0_0 := v_1_1_0_0_0.Args[0]
+ if v_1_1_0_0_0_0.Op != OpAMD64ANDQconst || auxIntToInt32(v_1_1_0_0_0_0.AuxInt) != 31 || y != v_1_1_0_0_0_0.Args[0] {
+ continue
+ }
+ v.reset(OpAMD64ROLL)
+ v.AddArg2(x, y)
+ return true
+ }
+ }
+ break
+ }
+ // match: (ORL (SHLXL x y) (ANDL (SHRXL x (NEGL y)) (SBBLcarrymask (CMPLconst (NEGL (ADDLconst (ANDLconst y [31]) [-32])) [32]))))
+ // result: (ROLL x y)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ if v_0.Op != OpAMD64SHLXL {
+ continue
+ }
+ y := v_0.Args[1]
+ x := v_0.Args[0]
+ if v_1.Op != OpAMD64ANDL {
+ continue
+ }
+ _ = v_1.Args[1]
+ v_1_0 := v_1.Args[0]
+ v_1_1 := v_1.Args[1]
+ for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 {
+ if v_1_0.Op != OpAMD64SHRXL {
+ continue
+ }
+ _ = v_1_0.Args[1]
+ if x != v_1_0.Args[0] {
+ continue
+ }
+ v_1_0_1 := v_1_0.Args[1]
+ if v_1_0_1.Op != OpAMD64NEGL || y != v_1_0_1.Args[0] || v_1_1.Op != OpAMD64SBBLcarrymask {
+ continue
+ }
+ v_1_1_0 := v_1_1.Args[0]
+ if v_1_1_0.Op != OpAMD64CMPLconst || auxIntToInt32(v_1_1_0.AuxInt) != 32 {
+ continue
+ }
+ v_1_1_0_0 := v_1_1_0.Args[0]
+ if v_1_1_0_0.Op != OpAMD64NEGL {
+ continue
+ }
+ v_1_1_0_0_0 := v_1_1_0_0.Args[0]
+ if v_1_1_0_0_0.Op != OpAMD64ADDLconst || auxIntToInt32(v_1_1_0_0_0.AuxInt) != -32 {
+ continue
+ }
+ v_1_1_0_0_0_0 := v_1_1_0_0_0.Args[0]
+ if v_1_1_0_0_0_0.Op != OpAMD64ANDLconst || auxIntToInt32(v_1_1_0_0_0_0.AuxInt) != 31 || y != v_1_1_0_0_0_0.Args[0] {
+ continue
+ }
+ v.reset(OpAMD64ROLL)
+ v.AddArg2(x, y)
+ return true
+ }
+ }
+ break
+ }
+ // match: (ORL (SHRXL x y) (ANDL (SHLXL x (NEGQ y)) (SBBLcarrymask (CMPQconst (NEGQ (ADDQconst (ANDQconst y [31]) [-32])) [32]))))
+ // result: (RORL x y)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ if v_0.Op != OpAMD64SHRXL {
+ continue
+ }
+ y := v_0.Args[1]
+ x := v_0.Args[0]
+ if v_1.Op != OpAMD64ANDL {
+ continue
+ }
+ _ = v_1.Args[1]
+ v_1_0 := v_1.Args[0]
+ v_1_1 := v_1.Args[1]
+ for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 {
+ if v_1_0.Op != OpAMD64SHLXL {
+ continue
+ }
+ _ = v_1_0.Args[1]
+ if x != v_1_0.Args[0] {
+ continue
+ }
+ v_1_0_1 := v_1_0.Args[1]
+ if v_1_0_1.Op != OpAMD64NEGQ || y != v_1_0_1.Args[0] || v_1_1.Op != OpAMD64SBBLcarrymask {
+ continue
+ }
+ v_1_1_0 := v_1_1.Args[0]
+ if v_1_1_0.Op != OpAMD64CMPQconst || auxIntToInt32(v_1_1_0.AuxInt) != 32 {
+ continue
+ }
+ v_1_1_0_0 := v_1_1_0.Args[0]
+ if v_1_1_0_0.Op != OpAMD64NEGQ {
+ continue
+ }
+ v_1_1_0_0_0 := v_1_1_0_0.Args[0]
+ if v_1_1_0_0_0.Op != OpAMD64ADDQconst || auxIntToInt32(v_1_1_0_0_0.AuxInt) != -32 {
+ continue
+ }
+ v_1_1_0_0_0_0 := v_1_1_0_0_0.Args[0]
+ if v_1_1_0_0_0_0.Op != OpAMD64ANDQconst || auxIntToInt32(v_1_1_0_0_0_0.AuxInt) != 31 || y != v_1_1_0_0_0_0.Args[0] {
+ continue
+ }
+ v.reset(OpAMD64RORL)
+ v.AddArg2(x, y)
+ return true
+ }
+ }
+ break
+ }
+ // match: (ORL (SHRXL x y) (ANDL (SHLXL x (NEGL y)) (SBBLcarrymask (CMPLconst (NEGL (ADDLconst (ANDLconst y [31]) [-32])) [32]))))
+ // result: (RORL x y)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ if v_0.Op != OpAMD64SHRXL {
+ continue
+ }
+ y := v_0.Args[1]
+ x := v_0.Args[0]
+ if v_1.Op != OpAMD64ANDL {
+ continue
+ }
+ _ = v_1.Args[1]
+ v_1_0 := v_1.Args[0]
+ v_1_1 := v_1.Args[1]
+ for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 {
+ if v_1_0.Op != OpAMD64SHLXL {
+ continue
+ }
+ _ = v_1_0.Args[1]
+ if x != v_1_0.Args[0] {
+ continue
+ }
+ v_1_0_1 := v_1_0.Args[1]
+ if v_1_0_1.Op != OpAMD64NEGL || y != v_1_0_1.Args[0] || v_1_1.Op != OpAMD64SBBLcarrymask {
+ continue
+ }
+ v_1_1_0 := v_1_1.Args[0]
+ if v_1_1_0.Op != OpAMD64CMPLconst || auxIntToInt32(v_1_1_0.AuxInt) != 32 {
+ continue
+ }
+ v_1_1_0_0 := v_1_1_0.Args[0]
+ if v_1_1_0_0.Op != OpAMD64NEGL {
+ continue
+ }
+ v_1_1_0_0_0 := v_1_1_0_0.Args[0]
+ if v_1_1_0_0_0.Op != OpAMD64ADDLconst || auxIntToInt32(v_1_1_0_0_0.AuxInt) != -32 {
+ continue
+ }
+ v_1_1_0_0_0_0 := v_1_1_0_0_0.Args[0]
+ if v_1_1_0_0_0_0.Op != OpAMD64ANDLconst || auxIntToInt32(v_1_1_0_0_0_0.AuxInt) != 31 || y != v_1_1_0_0_0_0.Args[0] {
+ continue
+ }
+ v.reset(OpAMD64RORL)
+ v.AddArg2(x, y)
+ return true
+ }
+ }
+ break
+ }
// match: (ORL (SHLL x (ANDQconst y [15])) (ANDL (SHRW x (NEGQ (ADDQconst (ANDQconst y [15]) [-16]))) (SBBLcarrymask (CMPQconst (NEGQ (ADDQconst (ANDQconst y [15]) [-16])) [16]))))
// cond: v.Type.Size() == 2
// result: (ROLW x y)
@@ -16404,6 +16773,214 @@ func rewriteValueAMD64_OpAMD64ORL(v *Value) bool {
}
break
}
+ // match: (ORL (SHLXL x (ANDQconst y [15])) (ANDL (SHRW x (NEGQ (ADDQconst (ANDQconst y [15]) [-16]))) (SBBLcarrymask (CMPQconst (NEGQ (ADDQconst (ANDQconst y [15]) [-16])) [16]))))
+ // cond: v.Type.Size() == 2
+ // result: (ROLW x y)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ if v_0.Op != OpAMD64SHLXL {
+ continue
+ }
+ _ = v_0.Args[1]
+ x := v_0.Args[0]
+ v_0_1 := v_0.Args[1]
+ if v_0_1.Op != OpAMD64ANDQconst || auxIntToInt32(v_0_1.AuxInt) != 15 {
+ continue
+ }
+ y := v_0_1.Args[0]
+ if v_1.Op != OpAMD64ANDL {
+ continue
+ }
+ _ = v_1.Args[1]
+ v_1_0 := v_1.Args[0]
+ v_1_1 := v_1.Args[1]
+ for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 {
+ if v_1_0.Op != OpAMD64SHRW {
+ continue
+ }
+ _ = v_1_0.Args[1]
+ if x != v_1_0.Args[0] {
+ continue
+ }
+ v_1_0_1 := v_1_0.Args[1]
+ if v_1_0_1.Op != OpAMD64NEGQ {
+ continue
+ }
+ v_1_0_1_0 := v_1_0_1.Args[0]
+ if v_1_0_1_0.Op != OpAMD64ADDQconst || auxIntToInt32(v_1_0_1_0.AuxInt) != -16 {
+ continue
+ }
+ v_1_0_1_0_0 := v_1_0_1_0.Args[0]
+ if v_1_0_1_0_0.Op != OpAMD64ANDQconst || auxIntToInt32(v_1_0_1_0_0.AuxInt) != 15 || y != v_1_0_1_0_0.Args[0] || v_1_1.Op != OpAMD64SBBLcarrymask {
+ continue
+ }
+ v_1_1_0 := v_1_1.Args[0]
+ if v_1_1_0.Op != OpAMD64CMPQconst || auxIntToInt32(v_1_1_0.AuxInt) != 16 {
+ continue
+ }
+ v_1_1_0_0 := v_1_1_0.Args[0]
+ if v_1_1_0_0.Op != OpAMD64NEGQ {
+ continue
+ }
+ v_1_1_0_0_0 := v_1_1_0_0.Args[0]
+ if v_1_1_0_0_0.Op != OpAMD64ADDQconst || auxIntToInt32(v_1_1_0_0_0.AuxInt) != -16 {
+ continue
+ }
+ v_1_1_0_0_0_0 := v_1_1_0_0_0.Args[0]
+ if v_1_1_0_0_0_0.Op != OpAMD64ANDQconst || auxIntToInt32(v_1_1_0_0_0_0.AuxInt) != 15 || y != v_1_1_0_0_0_0.Args[0] || !(v.Type.Size() == 2) {
+ continue
+ }
+ v.reset(OpAMD64ROLW)
+ v.AddArg2(x, y)
+ return true
+ }
+ }
+ break
+ }
+ // match: (ORL (SHLXL x (ANDLconst y [15])) (ANDL (SHRW x (NEGL (ADDLconst (ANDLconst y [15]) [-16]))) (SBBLcarrymask (CMPLconst (NEGL (ADDLconst (ANDLconst y [15]) [-16])) [16]))))
+ // cond: v.Type.Size() == 2
+ // result: (ROLW x y)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ if v_0.Op != OpAMD64SHLXL {
+ continue
+ }
+ _ = v_0.Args[1]
+ x := v_0.Args[0]
+ v_0_1 := v_0.Args[1]
+ if v_0_1.Op != OpAMD64ANDLconst || auxIntToInt32(v_0_1.AuxInt) != 15 {
+ continue
+ }
+ y := v_0_1.Args[0]
+ if v_1.Op != OpAMD64ANDL {
+ continue
+ }
+ _ = v_1.Args[1]
+ v_1_0 := v_1.Args[0]
+ v_1_1 := v_1.Args[1]
+ for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 {
+ if v_1_0.Op != OpAMD64SHRW {
+ continue
+ }
+ _ = v_1_0.Args[1]
+ if x != v_1_0.Args[0] {
+ continue
+ }
+ v_1_0_1 := v_1_0.Args[1]
+ if v_1_0_1.Op != OpAMD64NEGL {
+ continue
+ }
+ v_1_0_1_0 := v_1_0_1.Args[0]
+ if v_1_0_1_0.Op != OpAMD64ADDLconst || auxIntToInt32(v_1_0_1_0.AuxInt) != -16 {
+ continue
+ }
+ v_1_0_1_0_0 := v_1_0_1_0.Args[0]
+ if v_1_0_1_0_0.Op != OpAMD64ANDLconst || auxIntToInt32(v_1_0_1_0_0.AuxInt) != 15 || y != v_1_0_1_0_0.Args[0] || v_1_1.Op != OpAMD64SBBLcarrymask {
+ continue
+ }
+ v_1_1_0 := v_1_1.Args[0]
+ if v_1_1_0.Op != OpAMD64CMPLconst || auxIntToInt32(v_1_1_0.AuxInt) != 16 {
+ continue
+ }
+ v_1_1_0_0 := v_1_1_0.Args[0]
+ if v_1_1_0_0.Op != OpAMD64NEGL {
+ continue
+ }
+ v_1_1_0_0_0 := v_1_1_0_0.Args[0]
+ if v_1_1_0_0_0.Op != OpAMD64ADDLconst || auxIntToInt32(v_1_1_0_0_0.AuxInt) != -16 {
+ continue
+ }
+ v_1_1_0_0_0_0 := v_1_1_0_0_0.Args[0]
+ if v_1_1_0_0_0_0.Op != OpAMD64ANDLconst || auxIntToInt32(v_1_1_0_0_0_0.AuxInt) != 15 || y != v_1_1_0_0_0_0.Args[0] || !(v.Type.Size() == 2) {
+ continue
+ }
+ v.reset(OpAMD64ROLW)
+ v.AddArg2(x, y)
+ return true
+ }
+ }
+ break
+ }
+ // match: (ORL (SHRW x (ANDQconst y [15])) (SHLXL x (NEGQ (ADDQconst (ANDQconst y [15]) [-16]))))
+ // cond: v.Type.Size() == 2
+ // result: (RORW x y)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ if v_0.Op != OpAMD64SHRW {
+ continue
+ }
+ _ = v_0.Args[1]
+ x := v_0.Args[0]
+ v_0_1 := v_0.Args[1]
+ if v_0_1.Op != OpAMD64ANDQconst || auxIntToInt32(v_0_1.AuxInt) != 15 {
+ continue
+ }
+ y := v_0_1.Args[0]
+ if v_1.Op != OpAMD64SHLXL {
+ continue
+ }
+ _ = v_1.Args[1]
+ if x != v_1.Args[0] {
+ continue
+ }
+ v_1_1 := v_1.Args[1]
+ if v_1_1.Op != OpAMD64NEGQ {
+ continue
+ }
+ v_1_1_0 := v_1_1.Args[0]
+ if v_1_1_0.Op != OpAMD64ADDQconst || auxIntToInt32(v_1_1_0.AuxInt) != -16 {
+ continue
+ }
+ v_1_1_0_0 := v_1_1_0.Args[0]
+ if v_1_1_0_0.Op != OpAMD64ANDQconst || auxIntToInt32(v_1_1_0_0.AuxInt) != 15 || y != v_1_1_0_0.Args[0] || !(v.Type.Size() == 2) {
+ continue
+ }
+ v.reset(OpAMD64RORW)
+ v.AddArg2(x, y)
+ return true
+ }
+ break
+ }
+ // match: (ORL (SHRW x (ANDLconst y [15])) (SHLXL x (NEGL (ADDLconst (ANDLconst y [15]) [-16]))))
+ // cond: v.Type.Size() == 2
+ // result: (RORW x y)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ if v_0.Op != OpAMD64SHRW {
+ continue
+ }
+ _ = v_0.Args[1]
+ x := v_0.Args[0]
+ v_0_1 := v_0.Args[1]
+ if v_0_1.Op != OpAMD64ANDLconst || auxIntToInt32(v_0_1.AuxInt) != 15 {
+ continue
+ }
+ y := v_0_1.Args[0]
+ if v_1.Op != OpAMD64SHLXL {
+ continue
+ }
+ _ = v_1.Args[1]
+ if x != v_1.Args[0] {
+ continue
+ }
+ v_1_1 := v_1.Args[1]
+ if v_1_1.Op != OpAMD64NEGL {
+ continue
+ }
+ v_1_1_0 := v_1_1.Args[0]
+ if v_1_1_0.Op != OpAMD64ADDLconst || auxIntToInt32(v_1_1_0.AuxInt) != -16 {
+ continue
+ }
+ v_1_1_0_0 := v_1_1_0.Args[0]
+ if v_1_1_0_0.Op != OpAMD64ANDLconst || auxIntToInt32(v_1_1_0_0.AuxInt) != 15 || y != v_1_1_0_0.Args[0] || !(v.Type.Size() == 2) {
+ continue
+ }
+ v.reset(OpAMD64RORW)
+ v.AddArg2(x, y)
+ return true
+ }
+ break
+ }
// match: (ORL (SHLL x (ANDQconst y [ 7])) (ANDL (SHRB x (NEGQ (ADDQconst (ANDQconst y [ 7]) [ -8]))) (SBBLcarrymask (CMPQconst (NEGQ (ADDQconst (ANDQconst y [ 7]) [ -8])) [ 8]))))
// cond: v.Type.Size() == 1
// result: (ROLB x y)
@@ -16612,6 +17189,214 @@ func rewriteValueAMD64_OpAMD64ORL(v *Value) bool {
}
break
}
+ // match: (ORL (SHLXL x (ANDQconst y [ 7])) (ANDL (SHRB x (NEGQ (ADDQconst (ANDQconst y [ 7]) [ -8]))) (SBBLcarrymask (CMPQconst (NEGQ (ADDQconst (ANDQconst y [ 7]) [ -8])) [ 8]))))
+ // cond: v.Type.Size() == 1
+ // result: (ROLB x y)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ if v_0.Op != OpAMD64SHLXL {
+ continue
+ }
+ _ = v_0.Args[1]
+ x := v_0.Args[0]
+ v_0_1 := v_0.Args[1]
+ if v_0_1.Op != OpAMD64ANDQconst || auxIntToInt32(v_0_1.AuxInt) != 7 {
+ continue
+ }
+ y := v_0_1.Args[0]
+ if v_1.Op != OpAMD64ANDL {
+ continue
+ }
+ _ = v_1.Args[1]
+ v_1_0 := v_1.Args[0]
+ v_1_1 := v_1.Args[1]
+ for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 {
+ if v_1_0.Op != OpAMD64SHRB {
+ continue
+ }
+ _ = v_1_0.Args[1]
+ if x != v_1_0.Args[0] {
+ continue
+ }
+ v_1_0_1 := v_1_0.Args[1]
+ if v_1_0_1.Op != OpAMD64NEGQ {
+ continue
+ }
+ v_1_0_1_0 := v_1_0_1.Args[0]
+ if v_1_0_1_0.Op != OpAMD64ADDQconst || auxIntToInt32(v_1_0_1_0.AuxInt) != -8 {
+ continue
+ }
+ v_1_0_1_0_0 := v_1_0_1_0.Args[0]
+ if v_1_0_1_0_0.Op != OpAMD64ANDQconst || auxIntToInt32(v_1_0_1_0_0.AuxInt) != 7 || y != v_1_0_1_0_0.Args[0] || v_1_1.Op != OpAMD64SBBLcarrymask {
+ continue
+ }
+ v_1_1_0 := v_1_1.Args[0]
+ if v_1_1_0.Op != OpAMD64CMPQconst || auxIntToInt32(v_1_1_0.AuxInt) != 8 {
+ continue
+ }
+ v_1_1_0_0 := v_1_1_0.Args[0]
+ if v_1_1_0_0.Op != OpAMD64NEGQ {
+ continue
+ }
+ v_1_1_0_0_0 := v_1_1_0_0.Args[0]
+ if v_1_1_0_0_0.Op != OpAMD64ADDQconst || auxIntToInt32(v_1_1_0_0_0.AuxInt) != -8 {
+ continue
+ }
+ v_1_1_0_0_0_0 := v_1_1_0_0_0.Args[0]
+ if v_1_1_0_0_0_0.Op != OpAMD64ANDQconst || auxIntToInt32(v_1_1_0_0_0_0.AuxInt) != 7 || y != v_1_1_0_0_0_0.Args[0] || !(v.Type.Size() == 1) {
+ continue
+ }
+ v.reset(OpAMD64ROLB)
+ v.AddArg2(x, y)
+ return true
+ }
+ }
+ break
+ }
+ // match: (ORL (SHLXL x (ANDLconst y [ 7])) (ANDL (SHRB x (NEGL (ADDLconst (ANDLconst y [ 7]) [ -8]))) (SBBLcarrymask (CMPLconst (NEGL (ADDLconst (ANDLconst y [ 7]) [ -8])) [ 8]))))
+ // cond: v.Type.Size() == 1
+ // result: (ROLB x y)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ if v_0.Op != OpAMD64SHLXL {
+ continue
+ }
+ _ = v_0.Args[1]
+ x := v_0.Args[0]
+ v_0_1 := v_0.Args[1]
+ if v_0_1.Op != OpAMD64ANDLconst || auxIntToInt32(v_0_1.AuxInt) != 7 {
+ continue
+ }
+ y := v_0_1.Args[0]
+ if v_1.Op != OpAMD64ANDL {
+ continue
+ }
+ _ = v_1.Args[1]
+ v_1_0 := v_1.Args[0]
+ v_1_1 := v_1.Args[1]
+ for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 {
+ if v_1_0.Op != OpAMD64SHRB {
+ continue
+ }
+ _ = v_1_0.Args[1]
+ if x != v_1_0.Args[0] {
+ continue
+ }
+ v_1_0_1 := v_1_0.Args[1]
+ if v_1_0_1.Op != OpAMD64NEGL {
+ continue
+ }
+ v_1_0_1_0 := v_1_0_1.Args[0]
+ if v_1_0_1_0.Op != OpAMD64ADDLconst || auxIntToInt32(v_1_0_1_0.AuxInt) != -8 {
+ continue
+ }
+ v_1_0_1_0_0 := v_1_0_1_0.Args[0]
+ if v_1_0_1_0_0.Op != OpAMD64ANDLconst || auxIntToInt32(v_1_0_1_0_0.AuxInt) != 7 || y != v_1_0_1_0_0.Args[0] || v_1_1.Op != OpAMD64SBBLcarrymask {
+ continue
+ }
+ v_1_1_0 := v_1_1.Args[0]
+ if v_1_1_0.Op != OpAMD64CMPLconst || auxIntToInt32(v_1_1_0.AuxInt) != 8 {
+ continue
+ }
+ v_1_1_0_0 := v_1_1_0.Args[0]
+ if v_1_1_0_0.Op != OpAMD64NEGL {
+ continue
+ }
+ v_1_1_0_0_0 := v_1_1_0_0.Args[0]
+ if v_1_1_0_0_0.Op != OpAMD64ADDLconst || auxIntToInt32(v_1_1_0_0_0.AuxInt) != -8 {
+ continue
+ }
+ v_1_1_0_0_0_0 := v_1_1_0_0_0.Args[0]
+ if v_1_1_0_0_0_0.Op != OpAMD64ANDLconst || auxIntToInt32(v_1_1_0_0_0_0.AuxInt) != 7 || y != v_1_1_0_0_0_0.Args[0] || !(v.Type.Size() == 1) {
+ continue
+ }
+ v.reset(OpAMD64ROLB)
+ v.AddArg2(x, y)
+ return true
+ }
+ }
+ break
+ }
+ // match: (ORL (SHRB x (ANDQconst y [ 7])) (SHLXL x (NEGQ (ADDQconst (ANDQconst y [ 7]) [ -8]))))
+ // cond: v.Type.Size() == 1
+ // result: (RORB x y)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ if v_0.Op != OpAMD64SHRB {
+ continue
+ }
+ _ = v_0.Args[1]
+ x := v_0.Args[0]
+ v_0_1 := v_0.Args[1]
+ if v_0_1.Op != OpAMD64ANDQconst || auxIntToInt32(v_0_1.AuxInt) != 7 {
+ continue
+ }
+ y := v_0_1.Args[0]
+ if v_1.Op != OpAMD64SHLXL {
+ continue
+ }
+ _ = v_1.Args[1]
+ if x != v_1.Args[0] {
+ continue
+ }
+ v_1_1 := v_1.Args[1]
+ if v_1_1.Op != OpAMD64NEGQ {
+ continue
+ }
+ v_1_1_0 := v_1_1.Args[0]
+ if v_1_1_0.Op != OpAMD64ADDQconst || auxIntToInt32(v_1_1_0.AuxInt) != -8 {
+ continue
+ }
+ v_1_1_0_0 := v_1_1_0.Args[0]
+ if v_1_1_0_0.Op != OpAMD64ANDQconst || auxIntToInt32(v_1_1_0_0.AuxInt) != 7 || y != v_1_1_0_0.Args[0] || !(v.Type.Size() == 1) {
+ continue
+ }
+ v.reset(OpAMD64RORB)
+ v.AddArg2(x, y)
+ return true
+ }
+ break
+ }
+ // match: (ORL (SHRB x (ANDLconst y [ 7])) (SHLXL x (NEGL (ADDLconst (ANDLconst y [ 7]) [ -8]))))
+ // cond: v.Type.Size() == 1
+ // result: (RORB x y)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ if v_0.Op != OpAMD64SHRB {
+ continue
+ }
+ _ = v_0.Args[1]
+ x := v_0.Args[0]
+ v_0_1 := v_0.Args[1]
+ if v_0_1.Op != OpAMD64ANDLconst || auxIntToInt32(v_0_1.AuxInt) != 7 {
+ continue
+ }
+ y := v_0_1.Args[0]
+ if v_1.Op != OpAMD64SHLXL {
+ continue
+ }
+ _ = v_1.Args[1]
+ if x != v_1.Args[0] {
+ continue
+ }
+ v_1_1 := v_1.Args[1]
+ if v_1_1.Op != OpAMD64NEGL {
+ continue
+ }
+ v_1_1_0 := v_1_1.Args[0]
+ if v_1_1_0.Op != OpAMD64ADDLconst || auxIntToInt32(v_1_1_0.AuxInt) != -8 {
+ continue
+ }
+ v_1_1_0_0 := v_1_1_0.Args[0]
+ if v_1_1_0_0.Op != OpAMD64ANDLconst || auxIntToInt32(v_1_1_0_0.AuxInt) != 7 || y != v_1_1_0_0.Args[0] || !(v.Type.Size() == 1) {
+ continue
+ }
+ v.reset(OpAMD64RORB)
+ v.AddArg2(x, y)
+ return true
+ }
+ break
+ }
// match: (ORL x x)
// result: x
for {
@@ -17505,6 +18290,25 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value) bool {
}
break
}
+ // match: (ORQ (SHLXQ (MOVQconst [1]) y) x)
+ // result: (BTSQ x y)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ if v_0.Op != OpAMD64SHLXQ {
+ continue
+ }
+ y := v_0.Args[1]
+ v_0_0 := v_0.Args[0]
+ if v_0_0.Op != OpAMD64MOVQconst || auxIntToInt64(v_0_0.AuxInt) != 1 {
+ continue
+ }
+ x := v_1
+ v.reset(OpAMD64BTSQ)
+ v.AddArg2(x, y)
+ return true
+ }
+ break
+ }
// match: (ORQ (MOVQconst [c]) x)
// cond: isUint64PowerOfTwo(c) && uint64(c) >= 128
// result: (BTSQconst [int8(log64(c))] x)
@@ -17785,6 +18589,206 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value) bool {
}
break
}
+ // match: (ORQ (SHLXQ x y) (ANDQ (SHRXQ x (NEGQ y)) (SBBQcarrymask (CMPQconst (NEGQ (ADDQconst (ANDQconst y [63]) [-64])) [64]))))
+ // result: (ROLQ x y)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ if v_0.Op != OpAMD64SHLXQ {
+ continue
+ }
+ y := v_0.Args[1]
+ x := v_0.Args[0]
+ if v_1.Op != OpAMD64ANDQ {
+ continue
+ }
+ _ = v_1.Args[1]
+ v_1_0 := v_1.Args[0]
+ v_1_1 := v_1.Args[1]
+ for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 {
+ if v_1_0.Op != OpAMD64SHRXQ {
+ continue
+ }
+ _ = v_1_0.Args[1]
+ if x != v_1_0.Args[0] {
+ continue
+ }
+ v_1_0_1 := v_1_0.Args[1]
+ if v_1_0_1.Op != OpAMD64NEGQ || y != v_1_0_1.Args[0] || v_1_1.Op != OpAMD64SBBQcarrymask {
+ continue
+ }
+ v_1_1_0 := v_1_1.Args[0]
+ if v_1_1_0.Op != OpAMD64CMPQconst || auxIntToInt32(v_1_1_0.AuxInt) != 64 {
+ continue
+ }
+ v_1_1_0_0 := v_1_1_0.Args[0]
+ if v_1_1_0_0.Op != OpAMD64NEGQ {
+ continue
+ }
+ v_1_1_0_0_0 := v_1_1_0_0.Args[0]
+ if v_1_1_0_0_0.Op != OpAMD64ADDQconst || auxIntToInt32(v_1_1_0_0_0.AuxInt) != -64 {
+ continue
+ }
+ v_1_1_0_0_0_0 := v_1_1_0_0_0.Args[0]
+ if v_1_1_0_0_0_0.Op != OpAMD64ANDQconst || auxIntToInt32(v_1_1_0_0_0_0.AuxInt) != 63 || y != v_1_1_0_0_0_0.Args[0] {
+ continue
+ }
+ v.reset(OpAMD64ROLQ)
+ v.AddArg2(x, y)
+ return true
+ }
+ }
+ break
+ }
+ // match: (ORQ (SHLXQ x y) (ANDQ (SHRXQ x (NEGL y)) (SBBQcarrymask (CMPLconst (NEGL (ADDLconst (ANDLconst y [63]) [-64])) [64]))))
+ // result: (ROLQ x y)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ if v_0.Op != OpAMD64SHLXQ {
+ continue
+ }
+ y := v_0.Args[1]
+ x := v_0.Args[0]
+ if v_1.Op != OpAMD64ANDQ {
+ continue
+ }
+ _ = v_1.Args[1]
+ v_1_0 := v_1.Args[0]
+ v_1_1 := v_1.Args[1]
+ for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 {
+ if v_1_0.Op != OpAMD64SHRXQ {
+ continue
+ }
+ _ = v_1_0.Args[1]
+ if x != v_1_0.Args[0] {
+ continue
+ }
+ v_1_0_1 := v_1_0.Args[1]
+ if v_1_0_1.Op != OpAMD64NEGL || y != v_1_0_1.Args[0] || v_1_1.Op != OpAMD64SBBQcarrymask {
+ continue
+ }
+ v_1_1_0 := v_1_1.Args[0]
+ if v_1_1_0.Op != OpAMD64CMPLconst || auxIntToInt32(v_1_1_0.AuxInt) != 64 {
+ continue
+ }
+ v_1_1_0_0 := v_1_1_0.Args[0]
+ if v_1_1_0_0.Op != OpAMD64NEGL {
+ continue
+ }
+ v_1_1_0_0_0 := v_1_1_0_0.Args[0]
+ if v_1_1_0_0_0.Op != OpAMD64ADDLconst || auxIntToInt32(v_1_1_0_0_0.AuxInt) != -64 {
+ continue
+ }
+ v_1_1_0_0_0_0 := v_1_1_0_0_0.Args[0]
+ if v_1_1_0_0_0_0.Op != OpAMD64ANDLconst || auxIntToInt32(v_1_1_0_0_0_0.AuxInt) != 63 || y != v_1_1_0_0_0_0.Args[0] {
+ continue
+ }
+ v.reset(OpAMD64ROLQ)
+ v.AddArg2(x, y)
+ return true
+ }
+ }
+ break
+ }
+ // match: (ORQ (SHRXQ x y) (ANDQ (SHLXQ x (NEGQ y)) (SBBQcarrymask (CMPQconst (NEGQ (ADDQconst (ANDQconst y [63]) [-64])) [64]))))
+ // result: (RORQ x y)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ if v_0.Op != OpAMD64SHRXQ {
+ continue
+ }
+ y := v_0.Args[1]
+ x := v_0.Args[0]
+ if v_1.Op != OpAMD64ANDQ {
+ continue
+ }
+ _ = v_1.Args[1]
+ v_1_0 := v_1.Args[0]
+ v_1_1 := v_1.Args[1]
+ for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 {
+ if v_1_0.Op != OpAMD64SHLXQ {
+ continue
+ }
+ _ = v_1_0.Args[1]
+ if x != v_1_0.Args[0] {
+ continue
+ }
+ v_1_0_1 := v_1_0.Args[1]
+ if v_1_0_1.Op != OpAMD64NEGQ || y != v_1_0_1.Args[0] || v_1_1.Op != OpAMD64SBBQcarrymask {
+ continue
+ }
+ v_1_1_0 := v_1_1.Args[0]
+ if v_1_1_0.Op != OpAMD64CMPQconst || auxIntToInt32(v_1_1_0.AuxInt) != 64 {
+ continue
+ }
+ v_1_1_0_0 := v_1_1_0.Args[0]
+ if v_1_1_0_0.Op != OpAMD64NEGQ {
+ continue
+ }
+ v_1_1_0_0_0 := v_1_1_0_0.Args[0]
+ if v_1_1_0_0_0.Op != OpAMD64ADDQconst || auxIntToInt32(v_1_1_0_0_0.AuxInt) != -64 {
+ continue
+ }
+ v_1_1_0_0_0_0 := v_1_1_0_0_0.Args[0]
+ if v_1_1_0_0_0_0.Op != OpAMD64ANDQconst || auxIntToInt32(v_1_1_0_0_0_0.AuxInt) != 63 || y != v_1_1_0_0_0_0.Args[0] {
+ continue
+ }
+ v.reset(OpAMD64RORQ)
+ v.AddArg2(x, y)
+ return true
+ }
+ }
+ break
+ }
+ // match: (ORQ (SHRXQ x y) (ANDQ (SHLXQ x (NEGL y)) (SBBQcarrymask (CMPLconst (NEGL (ADDLconst (ANDLconst y [63]) [-64])) [64]))))
+ // result: (RORQ x y)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ if v_0.Op != OpAMD64SHRXQ {
+ continue
+ }
+ y := v_0.Args[1]
+ x := v_0.Args[0]
+ if v_1.Op != OpAMD64ANDQ {
+ continue
+ }
+ _ = v_1.Args[1]
+ v_1_0 := v_1.Args[0]
+ v_1_1 := v_1.Args[1]
+ for _i1 := 0; _i1 <= 1; _i1, v_1_0, v_1_1 = _i1+1, v_1_1, v_1_0 {
+ if v_1_0.Op != OpAMD64SHLXQ {
+ continue
+ }
+ _ = v_1_0.Args[1]
+ if x != v_1_0.Args[0] {
+ continue
+ }
+ v_1_0_1 := v_1_0.Args[1]
+ if v_1_0_1.Op != OpAMD64NEGL || y != v_1_0_1.Args[0] || v_1_1.Op != OpAMD64SBBQcarrymask {
+ continue
+ }
+ v_1_1_0 := v_1_1.Args[0]
+ if v_1_1_0.Op != OpAMD64CMPLconst || auxIntToInt32(v_1_1_0.AuxInt) != 64 {
+ continue
+ }
+ v_1_1_0_0 := v_1_1_0.Args[0]
+ if v_1_1_0_0.Op != OpAMD64NEGL {
+ continue
+ }
+ v_1_1_0_0_0 := v_1_1_0_0.Args[0]
+ if v_1_1_0_0_0.Op != OpAMD64ADDLconst || auxIntToInt32(v_1_1_0_0_0.AuxInt) != -64 {
+ continue
+ }
+ v_1_1_0_0_0_0 := v_1_1_0_0_0.Args[0]
+ if v_1_1_0_0_0_0.Op != OpAMD64ANDLconst || auxIntToInt32(v_1_1_0_0_0_0.AuxInt) != 63 || y != v_1_1_0_0_0_0.Args[0] {
+ continue
+ }
+ v.reset(OpAMD64RORQ)
+ v.AddArg2(x, y)
+ return true
+ }
+ }
+ break
+ }
// match: (ORQ (SHRQ lo bits) (SHLQ hi (NEGQ bits)))
// result: (SHRDQ lo hi bits)
for {
@@ -17833,6 +18837,54 @@ func rewriteValueAMD64_OpAMD64ORQ(v *Value) bool {
}
break
}
+ // match: (ORQ (SHRXQ lo bits) (SHLXQ hi (NEGQ bits)))
+ // result: (SHRDQ lo hi bits)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ if v_0.Op != OpAMD64SHRXQ {
+ continue
+ }
+ bits := v_0.Args[1]
+ lo := v_0.Args[0]
+ if v_1.Op != OpAMD64SHLXQ {
+ continue
+ }
+ _ = v_1.Args[1]
+ hi := v_1.Args[0]
+ v_1_1 := v_1.Args[1]
+ if v_1_1.Op != OpAMD64NEGQ || bits != v_1_1.Args[0] {
+ continue
+ }
+ v.reset(OpAMD64SHRDQ)
+ v.AddArg3(lo, hi, bits)
+ return true
+ }
+ break
+ }
+ // match: (ORQ (SHLXQ lo bits) (SHRXQ hi (NEGQ bits)))
+ // result: (SHLDQ lo hi bits)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ if v_0.Op != OpAMD64SHLXQ {
+ continue
+ }
+ bits := v_0.Args[1]
+ lo := v_0.Args[0]
+ if v_1.Op != OpAMD64SHRXQ {
+ continue
+ }
+ _ = v_1.Args[1]
+ hi := v_1.Args[0]
+ v_1_1 := v_1.Args[1]
+ if v_1_1.Op != OpAMD64NEGQ || bits != v_1_1.Args[0] {
+ continue
+ }
+ v.reset(OpAMD64SHLDQ)
+ v.AddArg3(lo, hi, bits)
+ return true
+ }
+ break
+ }
// match: (ORQ (MOVQconst [c]) (MOVQconst [d]))
// result: (MOVQconst [c|d])
for {
@@ -19844,6 +20896,19 @@ func rewriteValueAMD64_OpAMD64SARL(v *Value) bool {
v_1 := v.Args[1]
v_0 := v.Args[0]
b := v.Block
+ // match: (SARL x y)
+ // cond: buildcfg.GOAMD64 >= 3
+ // result: (SARXL x y)
+ for {
+ x := v_0
+ y := v_1
+ if !(buildcfg.GOAMD64 >= 3) {
+ break
+ }
+ v.reset(OpAMD64SARXL)
+ v.AddArg2(x, y)
+ return true
+ }
// match: (SARL x (MOVQconst [c]))
// result: (SARLconst [int8(c&31)] x)
for {
@@ -20066,6 +21131,19 @@ func rewriteValueAMD64_OpAMD64SARQ(v *Value) bool {
v_1 := v.Args[1]
v_0 := v.Args[0]
b := v.Block
+ // match: (SARQ x y)
+ // cond: buildcfg.GOAMD64 >= 3
+ // result: (SARXQ x y)
+ for {
+ x := v_0
+ y := v_1
+ if !(buildcfg.GOAMD64 >= 3) {
+ break
+ }
+ v.reset(OpAMD64SARXQ)
+ v.AddArg2(x, y)
+ return true
+ }
// match: (SARQ x (MOVQconst [c]))
// result: (SARQconst [int8(c&63)] x)
for {
@@ -20341,6 +21419,518 @@ func rewriteValueAMD64_OpAMD64SARWconst(v *Value) bool {
}
return false
}
+func rewriteValueAMD64_OpAMD64SARXL(v *Value) bool {
+ v_1 := v.Args[1]
+ v_0 := v.Args[0]
+ b := v.Block
+ // match: (SARXL x (MOVQconst [c]))
+ // result: (SARLconst [int8(c&31)] x)
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64MOVQconst {
+ break
+ }
+ c := auxIntToInt64(v_1.AuxInt)
+ v.reset(OpAMD64SARLconst)
+ v.AuxInt = int8ToAuxInt(int8(c & 31))
+ v.AddArg(x)
+ return true
+ }
+ // match: (SARXL x (MOVLconst [c]))
+ // result: (SARLconst [int8(c&31)] x)
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64MOVLconst {
+ break
+ }
+ c := auxIntToInt32(v_1.AuxInt)
+ v.reset(OpAMD64SARLconst)
+ v.AuxInt = int8ToAuxInt(int8(c & 31))
+ v.AddArg(x)
+ return true
+ }
+ // match: (SARXL x (ADDQconst [c] y))
+ // cond: c & 31 == 0
+ // result: (SARXL x y)
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64ADDQconst {
+ break
+ }
+ c := auxIntToInt32(v_1.AuxInt)
+ y := v_1.Args[0]
+ if !(c&31 == 0) {
+ break
+ }
+ v.reset(OpAMD64SARXL)
+ v.AddArg2(x, y)
+ return true
+ }
+ // match: (SARXL x (NEGQ <t> (ADDQconst [c] y)))
+ // cond: c & 31 == 0
+ // result: (SARXL x (NEGQ <t> y))
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64NEGQ {
+ break
+ }
+ t := v_1.Type
+ v_1_0 := v_1.Args[0]
+ if v_1_0.Op != OpAMD64ADDQconst {
+ break
+ }
+ c := auxIntToInt32(v_1_0.AuxInt)
+ y := v_1_0.Args[0]
+ if !(c&31 == 0) {
+ break
+ }
+ v.reset(OpAMD64SARXL)
+ v0 := b.NewValue0(v.Pos, OpAMD64NEGQ, t)
+ v0.AddArg(y)
+ v.AddArg2(x, v0)
+ return true
+ }
+ // match: (SARXL x (ANDQconst [c] y))
+ // cond: c & 31 == 31
+ // result: (SARXL x y)
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64ANDQconst {
+ break
+ }
+ c := auxIntToInt32(v_1.AuxInt)
+ y := v_1.Args[0]
+ if !(c&31 == 31) {
+ break
+ }
+ v.reset(OpAMD64SARXL)
+ v.AddArg2(x, y)
+ return true
+ }
+ // match: (SARXL x (NEGQ <t> (ANDQconst [c] y)))
+ // cond: c & 31 == 31
+ // result: (SARXL x (NEGQ <t> y))
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64NEGQ {
+ break
+ }
+ t := v_1.Type
+ v_1_0 := v_1.Args[0]
+ if v_1_0.Op != OpAMD64ANDQconst {
+ break
+ }
+ c := auxIntToInt32(v_1_0.AuxInt)
+ y := v_1_0.Args[0]
+ if !(c&31 == 31) {
+ break
+ }
+ v.reset(OpAMD64SARXL)
+ v0 := b.NewValue0(v.Pos, OpAMD64NEGQ, t)
+ v0.AddArg(y)
+ v.AddArg2(x, v0)
+ return true
+ }
+ // match: (SARXL x (ADDLconst [c] y))
+ // cond: c & 31 == 0
+ // result: (SARXL x y)
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64ADDLconst {
+ break
+ }
+ c := auxIntToInt32(v_1.AuxInt)
+ y := v_1.Args[0]
+ if !(c&31 == 0) {
+ break
+ }
+ v.reset(OpAMD64SARXL)
+ v.AddArg2(x, y)
+ return true
+ }
+ // match: (SARXL x (NEGL <t> (ADDLconst [c] y)))
+ // cond: c & 31 == 0
+ // result: (SARXL x (NEGL <t> y))
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64NEGL {
+ break
+ }
+ t := v_1.Type
+ v_1_0 := v_1.Args[0]
+ if v_1_0.Op != OpAMD64ADDLconst {
+ break
+ }
+ c := auxIntToInt32(v_1_0.AuxInt)
+ y := v_1_0.Args[0]
+ if !(c&31 == 0) {
+ break
+ }
+ v.reset(OpAMD64SARXL)
+ v0 := b.NewValue0(v.Pos, OpAMD64NEGL, t)
+ v0.AddArg(y)
+ v.AddArg2(x, v0)
+ return true
+ }
+ // match: (SARXL x (ANDLconst [c] y))
+ // cond: c & 31 == 31
+ // result: (SARXL x y)
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64ANDLconst {
+ break
+ }
+ c := auxIntToInt32(v_1.AuxInt)
+ y := v_1.Args[0]
+ if !(c&31 == 31) {
+ break
+ }
+ v.reset(OpAMD64SARXL)
+ v.AddArg2(x, y)
+ return true
+ }
+ // match: (SARXL x (NEGL <t> (ANDLconst [c] y)))
+ // cond: c & 31 == 31
+ // result: (SARXL x (NEGL <t> y))
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64NEGL {
+ break
+ }
+ t := v_1.Type
+ v_1_0 := v_1.Args[0]
+ if v_1_0.Op != OpAMD64ANDLconst {
+ break
+ }
+ c := auxIntToInt32(v_1_0.AuxInt)
+ y := v_1_0.Args[0]
+ if !(c&31 == 31) {
+ break
+ }
+ v.reset(OpAMD64SARXL)
+ v0 := b.NewValue0(v.Pos, OpAMD64NEGL, t)
+ v0.AddArg(y)
+ v.AddArg2(x, v0)
+ return true
+ }
+ // match: (SARXL l:(MOVLload [off] {sym} ptr mem) x)
+ // cond: canMergeLoad(v, l) && clobber(l)
+ // result: (SARXLload [off] {sym} ptr x mem)
+ for {
+ l := v_0
+ if l.Op != OpAMD64MOVLload {
+ break
+ }
+ off := auxIntToInt32(l.AuxInt)
+ sym := auxToSym(l.Aux)
+ mem := l.Args[1]
+ ptr := l.Args[0]
+ x := v_1
+ if !(canMergeLoad(v, l) && clobber(l)) {
+ break
+ }
+ v.reset(OpAMD64SARXLload)
+ v.AuxInt = int32ToAuxInt(off)
+ v.Aux = symToAux(sym)
+ v.AddArg3(ptr, x, mem)
+ return true
+ }
+ return false
+}
+func rewriteValueAMD64_OpAMD64SARXLload(v *Value) bool {
+ v_2 := v.Args[2]
+ v_1 := v.Args[1]
+ v_0 := v.Args[0]
+ b := v.Block
+ typ := &b.Func.Config.Types
+ // match: (SARXLload [off] {sym} ptr (MOVLconst [c]) mem)
+ // result: (SARLconst [int8(c&31)] (MOVLload [off] {sym} ptr mem))
+ for {
+ off := auxIntToInt32(v.AuxInt)
+ sym := auxToSym(v.Aux)
+ ptr := v_0
+ if v_1.Op != OpAMD64MOVLconst {
+ break
+ }
+ c := auxIntToInt32(v_1.AuxInt)
+ mem := v_2
+ v.reset(OpAMD64SARLconst)
+ v.AuxInt = int8ToAuxInt(int8(c & 31))
+ v0 := b.NewValue0(v.Pos, OpAMD64MOVLload, typ.UInt32)
+ v0.AuxInt = int32ToAuxInt(off)
+ v0.Aux = symToAux(sym)
+ v0.AddArg2(ptr, mem)
+ v.AddArg(v0)
+ return true
+ }
+ return false
+}
+func rewriteValueAMD64_OpAMD64SARXQ(v *Value) bool {
+ v_1 := v.Args[1]
+ v_0 := v.Args[0]
+ b := v.Block
+ // match: (SARXQ x (MOVQconst [c]))
+ // result: (SARQconst [int8(c&63)] x)
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64MOVQconst {
+ break
+ }
+ c := auxIntToInt64(v_1.AuxInt)
+ v.reset(OpAMD64SARQconst)
+ v.AuxInt = int8ToAuxInt(int8(c & 63))
+ v.AddArg(x)
+ return true
+ }
+ // match: (SARXQ x (MOVLconst [c]))
+ // result: (SARQconst [int8(c&63)] x)
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64MOVLconst {
+ break
+ }
+ c := auxIntToInt32(v_1.AuxInt)
+ v.reset(OpAMD64SARQconst)
+ v.AuxInt = int8ToAuxInt(int8(c & 63))
+ v.AddArg(x)
+ return true
+ }
+ // match: (SARXQ x (ADDQconst [c] y))
+ // cond: c & 63 == 0
+ // result: (SARXQ x y)
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64ADDQconst {
+ break
+ }
+ c := auxIntToInt32(v_1.AuxInt)
+ y := v_1.Args[0]
+ if !(c&63 == 0) {
+ break
+ }
+ v.reset(OpAMD64SARXQ)
+ v.AddArg2(x, y)
+ return true
+ }
+ // match: (SARXQ x (NEGQ <t> (ADDQconst [c] y)))
+ // cond: c & 63 == 0
+ // result: (SARXQ x (NEGQ <t> y))
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64NEGQ {
+ break
+ }
+ t := v_1.Type
+ v_1_0 := v_1.Args[0]
+ if v_1_0.Op != OpAMD64ADDQconst {
+ break
+ }
+ c := auxIntToInt32(v_1_0.AuxInt)
+ y := v_1_0.Args[0]
+ if !(c&63 == 0) {
+ break
+ }
+ v.reset(OpAMD64SARXQ)
+ v0 := b.NewValue0(v.Pos, OpAMD64NEGQ, t)
+ v0.AddArg(y)
+ v.AddArg2(x, v0)
+ return true
+ }
+ // match: (SARXQ x (ANDQconst [c] y))
+ // cond: c & 63 == 63
+ // result: (SARXQ x y)
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64ANDQconst {
+ break
+ }
+ c := auxIntToInt32(v_1.AuxInt)
+ y := v_1.Args[0]
+ if !(c&63 == 63) {
+ break
+ }
+ v.reset(OpAMD64SARXQ)
+ v.AddArg2(x, y)
+ return true
+ }
+ // match: (SARXQ x (NEGQ <t> (ANDQconst [c] y)))
+ // cond: c & 63 == 63
+ // result: (SARXQ x (NEGQ <t> y))
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64NEGQ {
+ break
+ }
+ t := v_1.Type
+ v_1_0 := v_1.Args[0]
+ if v_1_0.Op != OpAMD64ANDQconst {
+ break
+ }
+ c := auxIntToInt32(v_1_0.AuxInt)
+ y := v_1_0.Args[0]
+ if !(c&63 == 63) {
+ break
+ }
+ v.reset(OpAMD64SARXQ)
+ v0 := b.NewValue0(v.Pos, OpAMD64NEGQ, t)
+ v0.AddArg(y)
+ v.AddArg2(x, v0)
+ return true
+ }
+ // match: (SARXQ x (ADDLconst [c] y))
+ // cond: c & 63 == 0
+ // result: (SARXQ x y)
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64ADDLconst {
+ break
+ }
+ c := auxIntToInt32(v_1.AuxInt)
+ y := v_1.Args[0]
+ if !(c&63 == 0) {
+ break
+ }
+ v.reset(OpAMD64SARXQ)
+ v.AddArg2(x, y)
+ return true
+ }
+ // match: (SARXQ x (NEGL <t> (ADDLconst [c] y)))
+ // cond: c & 63 == 0
+ // result: (SARXQ x (NEGL <t> y))
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64NEGL {
+ break
+ }
+ t := v_1.Type
+ v_1_0 := v_1.Args[0]
+ if v_1_0.Op != OpAMD64ADDLconst {
+ break
+ }
+ c := auxIntToInt32(v_1_0.AuxInt)
+ y := v_1_0.Args[0]
+ if !(c&63 == 0) {
+ break
+ }
+ v.reset(OpAMD64SARXQ)
+ v0 := b.NewValue0(v.Pos, OpAMD64NEGL, t)
+ v0.AddArg(y)
+ v.AddArg2(x, v0)
+ return true
+ }
+ // match: (SARXQ x (ANDLconst [c] y))
+ // cond: c & 63 == 63
+ // result: (SARXQ x y)
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64ANDLconst {
+ break
+ }
+ c := auxIntToInt32(v_1.AuxInt)
+ y := v_1.Args[0]
+ if !(c&63 == 63) {
+ break
+ }
+ v.reset(OpAMD64SARXQ)
+ v.AddArg2(x, y)
+ return true
+ }
+ // match: (SARXQ x (NEGL <t> (ANDLconst [c] y)))
+ // cond: c & 63 == 63
+ // result: (SARXQ x (NEGL <t> y))
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64NEGL {
+ break
+ }
+ t := v_1.Type
+ v_1_0 := v_1.Args[0]
+ if v_1_0.Op != OpAMD64ANDLconst {
+ break
+ }
+ c := auxIntToInt32(v_1_0.AuxInt)
+ y := v_1_0.Args[0]
+ if !(c&63 == 63) {
+ break
+ }
+ v.reset(OpAMD64SARXQ)
+ v0 := b.NewValue0(v.Pos, OpAMD64NEGL, t)
+ v0.AddArg(y)
+ v.AddArg2(x, v0)
+ return true
+ }
+ // match: (SARXQ l:(MOVQload [off] {sym} ptr mem) x)
+ // cond: canMergeLoad(v, l) && clobber(l)
+ // result: (SARXQload [off] {sym} ptr x mem)
+ for {
+ l := v_0
+ if l.Op != OpAMD64MOVQload {
+ break
+ }
+ off := auxIntToInt32(l.AuxInt)
+ sym := auxToSym(l.Aux)
+ mem := l.Args[1]
+ ptr := l.Args[0]
+ x := v_1
+ if !(canMergeLoad(v, l) && clobber(l)) {
+ break
+ }
+ v.reset(OpAMD64SARXQload)
+ v.AuxInt = int32ToAuxInt(off)
+ v.Aux = symToAux(sym)
+ v.AddArg3(ptr, x, mem)
+ return true
+ }
+ return false
+}
+func rewriteValueAMD64_OpAMD64SARXQload(v *Value) bool {
+ v_2 := v.Args[2]
+ v_1 := v.Args[1]
+ v_0 := v.Args[0]
+ b := v.Block
+ typ := &b.Func.Config.Types
+ // match: (SARXQload [off] {sym} ptr (MOVQconst [c]) mem)
+ // result: (SARQconst [int8(c&63)] (MOVQload [off] {sym} ptr mem))
+ for {
+ off := auxIntToInt32(v.AuxInt)
+ sym := auxToSym(v.Aux)
+ ptr := v_0
+ if v_1.Op != OpAMD64MOVQconst {
+ break
+ }
+ c := auxIntToInt64(v_1.AuxInt)
+ mem := v_2
+ v.reset(OpAMD64SARQconst)
+ v.AuxInt = int8ToAuxInt(int8(c & 63))
+ v0 := b.NewValue0(v.Pos, OpAMD64MOVQload, typ.UInt64)
+ v0.AuxInt = int32ToAuxInt(off)
+ v0.Aux = symToAux(sym)
+ v0.AddArg2(ptr, mem)
+ v.AddArg(v0)
+ return true
+ }
+ // match: (SARXQload [off] {sym} ptr (MOVLconst [c]) mem)
+ // result: (SARQconst [int8(c&63)] (MOVQload [off] {sym} ptr mem))
+ for {
+ off := auxIntToInt32(v.AuxInt)
+ sym := auxToSym(v.Aux)
+ ptr := v_0
+ if v_1.Op != OpAMD64MOVLconst {
+ break
+ }
+ c := auxIntToInt32(v_1.AuxInt)
+ mem := v_2
+ v.reset(OpAMD64SARQconst)
+ v.AuxInt = int8ToAuxInt(int8(c & 63))
+ v0 := b.NewValue0(v.Pos, OpAMD64MOVQload, typ.UInt64)
+ v0.AuxInt = int32ToAuxInt(off)
+ v0.Aux = symToAux(sym)
+ v0.AddArg2(ptr, mem)
+ v.AddArg(v0)
+ return true
+ }
+ return false
+}
func rewriteValueAMD64_OpAMD64SBBLcarrymask(v *Value) bool {
v_0 := v.Args[0]
// match: (SBBLcarrymask (FlagEQ))
@@ -21596,6 +23186,60 @@ func rewriteValueAMD64_OpAMD64SETEQ(v *Value) bool {
}
break
}
+ // match: (SETEQ (TESTL (SHLXL (MOVLconst [1]) x) y))
+ // result: (SETAE (BTL x y))
+ for {
+ if v_0.Op != OpAMD64TESTL {
+ break
+ }
+ _ = v_0.Args[1]
+ v_0_0 := v_0.Args[0]
+ v_0_1 := v_0.Args[1]
+ for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 {
+ if v_0_0.Op != OpAMD64SHLXL {
+ continue
+ }
+ x := v_0_0.Args[1]
+ v_0_0_0 := v_0_0.Args[0]
+ if v_0_0_0.Op != OpAMD64MOVLconst || auxIntToInt32(v_0_0_0.AuxInt) != 1 {
+ continue
+ }
+ y := v_0_1
+ v.reset(OpAMD64SETAE)
+ v0 := b.NewValue0(v.Pos, OpAMD64BTL, types.TypeFlags)
+ v0.AddArg2(x, y)
+ v.AddArg(v0)
+ return true
+ }
+ break
+ }
+ // match: (SETEQ (TESTQ (SHLXQ (MOVQconst [1]) x) y))
+ // result: (SETAE (BTQ x y))
+ for {
+ if v_0.Op != OpAMD64TESTQ {
+ break
+ }
+ _ = v_0.Args[1]
+ v_0_0 := v_0.Args[0]
+ v_0_1 := v_0.Args[1]
+ for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 {
+ if v_0_0.Op != OpAMD64SHLXQ {
+ continue
+ }
+ x := v_0_0.Args[1]
+ v_0_0_0 := v_0_0.Args[0]
+ if v_0_0_0.Op != OpAMD64MOVQconst || auxIntToInt64(v_0_0_0.AuxInt) != 1 {
+ continue
+ }
+ y := v_0_1
+ v.reset(OpAMD64SETAE)
+ v0 := b.NewValue0(v.Pos, OpAMD64BTQ, types.TypeFlags)
+ v0.AddArg2(x, y)
+ v.AddArg(v0)
+ return true
+ }
+ break
+ }
// match: (SETEQ (TESTLconst [c] x))
// cond: isUint32PowerOfTwo(int64(c))
// result: (SETAE (BTLconst [int8(log32(c))] x))
@@ -22021,6 +23665,72 @@ func rewriteValueAMD64_OpAMD64SETEQstore(v *Value) bool {
}
break
}
+ // match: (SETEQstore [off] {sym} ptr (TESTL (SHLXL (MOVLconst [1]) x) y) mem)
+ // result: (SETAEstore [off] {sym} ptr (BTL x y) mem)
+ for {
+ off := auxIntToInt32(v.AuxInt)
+ sym := auxToSym(v.Aux)
+ ptr := v_0
+ if v_1.Op != OpAMD64TESTL {
+ break
+ }
+ _ = v_1.Args[1]
+ v_1_0 := v_1.Args[0]
+ v_1_1 := v_1.Args[1]
+ for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 {
+ if v_1_0.Op != OpAMD64SHLXL {
+ continue
+ }
+ x := v_1_0.Args[1]
+ v_1_0_0 := v_1_0.Args[0]
+ if v_1_0_0.Op != OpAMD64MOVLconst || auxIntToInt32(v_1_0_0.AuxInt) != 1 {
+ continue
+ }
+ y := v_1_1
+ mem := v_2
+ v.reset(OpAMD64SETAEstore)
+ v.AuxInt = int32ToAuxInt(off)
+ v.Aux = symToAux(sym)
+ v0 := b.NewValue0(v.Pos, OpAMD64BTL, types.TypeFlags)
+ v0.AddArg2(x, y)
+ v.AddArg3(ptr, v0, mem)
+ return true
+ }
+ break
+ }
+ // match: (SETEQstore [off] {sym} ptr (TESTQ (SHLXQ (MOVQconst [1]) x) y) mem)
+ // result: (SETAEstore [off] {sym} ptr (BTQ x y) mem)
+ for {
+ off := auxIntToInt32(v.AuxInt)
+ sym := auxToSym(v.Aux)
+ ptr := v_0
+ if v_1.Op != OpAMD64TESTQ {
+ break
+ }
+ _ = v_1.Args[1]
+ v_1_0 := v_1.Args[0]
+ v_1_1 := v_1.Args[1]
+ for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 {
+ if v_1_0.Op != OpAMD64SHLXQ {
+ continue
+ }
+ x := v_1_0.Args[1]
+ v_1_0_0 := v_1_0.Args[0]
+ if v_1_0_0.Op != OpAMD64MOVQconst || auxIntToInt64(v_1_0_0.AuxInt) != 1 {
+ continue
+ }
+ y := v_1_1
+ mem := v_2
+ v.reset(OpAMD64SETAEstore)
+ v.AuxInt = int32ToAuxInt(off)
+ v.Aux = symToAux(sym)
+ v0 := b.NewValue0(v.Pos, OpAMD64BTQ, types.TypeFlags)
+ v0.AddArg2(x, y)
+ v.AddArg3(ptr, v0, mem)
+ return true
+ }
+ break
+ }
// match: (SETEQstore [off] {sym} ptr (TESTLconst [c] x) mem)
// cond: isUint32PowerOfTwo(int64(c))
// result: (SETAEstore [off] {sym} ptr (BTLconst [int8(log32(c))] x) mem)
@@ -23512,6 +25222,60 @@ func rewriteValueAMD64_OpAMD64SETNE(v *Value) bool {
}
break
}
+ // match: (SETNE (TESTL (SHLXL (MOVLconst [1]) x) y))
+ // result: (SETB (BTL x y))
+ for {
+ if v_0.Op != OpAMD64TESTL {
+ break
+ }
+ _ = v_0.Args[1]
+ v_0_0 := v_0.Args[0]
+ v_0_1 := v_0.Args[1]
+ for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 {
+ if v_0_0.Op != OpAMD64SHLXL {
+ continue
+ }
+ x := v_0_0.Args[1]
+ v_0_0_0 := v_0_0.Args[0]
+ if v_0_0_0.Op != OpAMD64MOVLconst || auxIntToInt32(v_0_0_0.AuxInt) != 1 {
+ continue
+ }
+ y := v_0_1
+ v.reset(OpAMD64SETB)
+ v0 := b.NewValue0(v.Pos, OpAMD64BTL, types.TypeFlags)
+ v0.AddArg2(x, y)
+ v.AddArg(v0)
+ return true
+ }
+ break
+ }
+ // match: (SETNE (TESTQ (SHLXQ (MOVQconst [1]) x) y))
+ // result: (SETB (BTQ x y))
+ for {
+ if v_0.Op != OpAMD64TESTQ {
+ break
+ }
+ _ = v_0.Args[1]
+ v_0_0 := v_0.Args[0]
+ v_0_1 := v_0.Args[1]
+ for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 {
+ if v_0_0.Op != OpAMD64SHLXQ {
+ continue
+ }
+ x := v_0_0.Args[1]
+ v_0_0_0 := v_0_0.Args[0]
+ if v_0_0_0.Op != OpAMD64MOVQconst || auxIntToInt64(v_0_0_0.AuxInt) != 1 {
+ continue
+ }
+ y := v_0_1
+ v.reset(OpAMD64SETB)
+ v0 := b.NewValue0(v.Pos, OpAMD64BTQ, types.TypeFlags)
+ v0.AddArg2(x, y)
+ v.AddArg(v0)
+ return true
+ }
+ break
+ }
// match: (SETNE (TESTLconst [c] x))
// cond: isUint32PowerOfTwo(int64(c))
// result: (SETB (BTLconst [int8(log32(c))] x))
@@ -23937,6 +25701,72 @@ func rewriteValueAMD64_OpAMD64SETNEstore(v *Value) bool {
}
break
}
+ // match: (SETNEstore [off] {sym} ptr (TESTL (SHLXL (MOVLconst [1]) x) y) mem)
+ // result: (SETBstore [off] {sym} ptr (BTL x y) mem)
+ for {
+ off := auxIntToInt32(v.AuxInt)
+ sym := auxToSym(v.Aux)
+ ptr := v_0
+ if v_1.Op != OpAMD64TESTL {
+ break
+ }
+ _ = v_1.Args[1]
+ v_1_0 := v_1.Args[0]
+ v_1_1 := v_1.Args[1]
+ for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 {
+ if v_1_0.Op != OpAMD64SHLXL {
+ continue
+ }
+ x := v_1_0.Args[1]
+ v_1_0_0 := v_1_0.Args[0]
+ if v_1_0_0.Op != OpAMD64MOVLconst || auxIntToInt32(v_1_0_0.AuxInt) != 1 {
+ continue
+ }
+ y := v_1_1
+ mem := v_2
+ v.reset(OpAMD64SETBstore)
+ v.AuxInt = int32ToAuxInt(off)
+ v.Aux = symToAux(sym)
+ v0 := b.NewValue0(v.Pos, OpAMD64BTL, types.TypeFlags)
+ v0.AddArg2(x, y)
+ v.AddArg3(ptr, v0, mem)
+ return true
+ }
+ break
+ }
+ // match: (SETNEstore [off] {sym} ptr (TESTQ (SHLXQ (MOVQconst [1]) x) y) mem)
+ // result: (SETBstore [off] {sym} ptr (BTQ x y) mem)
+ for {
+ off := auxIntToInt32(v.AuxInt)
+ sym := auxToSym(v.Aux)
+ ptr := v_0
+ if v_1.Op != OpAMD64TESTQ {
+ break
+ }
+ _ = v_1.Args[1]
+ v_1_0 := v_1.Args[0]
+ v_1_1 := v_1.Args[1]
+ for _i0 := 0; _i0 <= 1; _i0, v_1_0, v_1_1 = _i0+1, v_1_1, v_1_0 {
+ if v_1_0.Op != OpAMD64SHLXQ {
+ continue
+ }
+ x := v_1_0.Args[1]
+ v_1_0_0 := v_1_0.Args[0]
+ if v_1_0_0.Op != OpAMD64MOVQconst || auxIntToInt64(v_1_0_0.AuxInt) != 1 {
+ continue
+ }
+ y := v_1_1
+ mem := v_2
+ v.reset(OpAMD64SETBstore)
+ v.AuxInt = int32ToAuxInt(off)
+ v.Aux = symToAux(sym)
+ v0 := b.NewValue0(v.Pos, OpAMD64BTQ, types.TypeFlags)
+ v0.AddArg2(x, y)
+ v.AddArg3(ptr, v0, mem)
+ return true
+ }
+ break
+ }
// match: (SETNEstore [off] {sym} ptr (TESTLconst [c] x) mem)
// cond: isUint32PowerOfTwo(int64(c))
// result: (SETBstore [off] {sym} ptr (BTLconst [int8(log32(c))] x) mem)
@@ -24451,6 +26281,19 @@ func rewriteValueAMD64_OpAMD64SHLL(v *Value) bool {
v_1 := v.Args[1]
v_0 := v.Args[0]
b := v.Block
+ // match: (SHLL x y)
+ // cond: buildcfg.GOAMD64 >= 3
+ // result: (SHLXL x y)
+ for {
+ x := v_0
+ y := v_1
+ if !(buildcfg.GOAMD64 >= 3) {
+ break
+ }
+ v.reset(OpAMD64SHLXL)
+ v.AddArg2(x, y)
+ return true
+ }
// match: (SHLL x (MOVQconst [c]))
// result: (SHLLconst [int8(c&31)] x)
for {
@@ -24641,28 +26484,6 @@ func rewriteValueAMD64_OpAMD64SHLL(v *Value) bool {
v.AddArg2(x, v0)
return true
}
- // match: (SHLL l:(MOVLload [off] {sym} ptr mem) x)
- // cond: buildcfg.GOAMD64 >= 3 && canMergeLoad(v, l) && clobber(l)
- // result: (SHLXLload [off] {sym} ptr x mem)
- for {
- l := v_0
- if l.Op != OpAMD64MOVLload {
- break
- }
- off := auxIntToInt32(l.AuxInt)
- sym := auxToSym(l.Aux)
- mem := l.Args[1]
- ptr := l.Args[0]
- x := v_1
- if !(buildcfg.GOAMD64 >= 3 && canMergeLoad(v, l) && clobber(l)) {
- break
- }
- v.reset(OpAMD64SHLXLload)
- v.AuxInt = int32ToAuxInt(off)
- v.Aux = symToAux(sym)
- v.AddArg3(ptr, x, mem)
- return true
- }
return false
}
func rewriteValueAMD64_OpAMD64SHLLconst(v *Value) bool {
@@ -24707,6 +26528,19 @@ func rewriteValueAMD64_OpAMD64SHLQ(v *Value) bool {
v_1 := v.Args[1]
v_0 := v.Args[0]
b := v.Block
+ // match: (SHLQ x y)
+ // cond: buildcfg.GOAMD64 >= 3
+ // result: (SHLXQ x y)
+ for {
+ x := v_0
+ y := v_1
+ if !(buildcfg.GOAMD64 >= 3) {
+ break
+ }
+ v.reset(OpAMD64SHLXQ)
+ v.AddArg2(x, y)
+ return true
+ }
// match: (SHLQ x (MOVQconst [c]))
// result: (SHLQconst [int8(c&63)] x)
for {
@@ -24897,28 +26731,6 @@ func rewriteValueAMD64_OpAMD64SHLQ(v *Value) bool {
v.AddArg2(x, v0)
return true
}
- // match: (SHLQ l:(MOVQload [off] {sym} ptr mem) x)
- // cond: buildcfg.GOAMD64 >= 3 && canMergeLoad(v, l) && clobber(l)
- // result: (SHLXQload [off] {sym} ptr x mem)
- for {
- l := v_0
- if l.Op != OpAMD64MOVQload {
- break
- }
- off := auxIntToInt32(l.AuxInt)
- sym := auxToSym(l.Aux)
- mem := l.Args[1]
- ptr := l.Args[0]
- x := v_1
- if !(buildcfg.GOAMD64 >= 3 && canMergeLoad(v, l) && clobber(l)) {
- break
- }
- v.reset(OpAMD64SHLXQload)
- v.AuxInt = int32ToAuxInt(off)
- v.Aux = symToAux(sym)
- v.AddArg3(ptr, x, mem)
- return true
- }
return false
}
func rewriteValueAMD64_OpAMD64SHLQconst(v *Value) bool {
@@ -24971,6 +26783,518 @@ func rewriteValueAMD64_OpAMD64SHLQconst(v *Value) bool {
}
return false
}
+func rewriteValueAMD64_OpAMD64SHLXL(v *Value) bool {
+ v_1 := v.Args[1]
+ v_0 := v.Args[0]
+ b := v.Block
+ // match: (SHLXL x (MOVQconst [c]))
+ // result: (SHLLconst [int8(c&31)] x)
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64MOVQconst {
+ break
+ }
+ c := auxIntToInt64(v_1.AuxInt)
+ v.reset(OpAMD64SHLLconst)
+ v.AuxInt = int8ToAuxInt(int8(c & 31))
+ v.AddArg(x)
+ return true
+ }
+ // match: (SHLXL x (MOVLconst [c]))
+ // result: (SHLLconst [int8(c&31)] x)
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64MOVLconst {
+ break
+ }
+ c := auxIntToInt32(v_1.AuxInt)
+ v.reset(OpAMD64SHLLconst)
+ v.AuxInt = int8ToAuxInt(int8(c & 31))
+ v.AddArg(x)
+ return true
+ }
+ // match: (SHLXL x (ADDQconst [c] y))
+ // cond: c & 31 == 0
+ // result: (SHLXL x y)
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64ADDQconst {
+ break
+ }
+ c := auxIntToInt32(v_1.AuxInt)
+ y := v_1.Args[0]
+ if !(c&31 == 0) {
+ break
+ }
+ v.reset(OpAMD64SHLXL)
+ v.AddArg2(x, y)
+ return true
+ }
+ // match: (SHLXL x (NEGQ <t> (ADDQconst [c] y)))
+ // cond: c & 31 == 0
+ // result: (SHLXL x (NEGQ <t> y))
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64NEGQ {
+ break
+ }
+ t := v_1.Type
+ v_1_0 := v_1.Args[0]
+ if v_1_0.Op != OpAMD64ADDQconst {
+ break
+ }
+ c := auxIntToInt32(v_1_0.AuxInt)
+ y := v_1_0.Args[0]
+ if !(c&31 == 0) {
+ break
+ }
+ v.reset(OpAMD64SHLXL)
+ v0 := b.NewValue0(v.Pos, OpAMD64NEGQ, t)
+ v0.AddArg(y)
+ v.AddArg2(x, v0)
+ return true
+ }
+ // match: (SHLXL x (ANDQconst [c] y))
+ // cond: c & 31 == 31
+ // result: (SHLXL x y)
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64ANDQconst {
+ break
+ }
+ c := auxIntToInt32(v_1.AuxInt)
+ y := v_1.Args[0]
+ if !(c&31 == 31) {
+ break
+ }
+ v.reset(OpAMD64SHLXL)
+ v.AddArg2(x, y)
+ return true
+ }
+ // match: (SHLXL x (NEGQ <t> (ANDQconst [c] y)))
+ // cond: c & 31 == 31
+ // result: (SHLXL x (NEGQ <t> y))
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64NEGQ {
+ break
+ }
+ t := v_1.Type
+ v_1_0 := v_1.Args[0]
+ if v_1_0.Op != OpAMD64ANDQconst {
+ break
+ }
+ c := auxIntToInt32(v_1_0.AuxInt)
+ y := v_1_0.Args[0]
+ if !(c&31 == 31) {
+ break
+ }
+ v.reset(OpAMD64SHLXL)
+ v0 := b.NewValue0(v.Pos, OpAMD64NEGQ, t)
+ v0.AddArg(y)
+ v.AddArg2(x, v0)
+ return true
+ }
+ // match: (SHLXL x (ADDLconst [c] y))
+ // cond: c & 31 == 0
+ // result: (SHLXL x y)
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64ADDLconst {
+ break
+ }
+ c := auxIntToInt32(v_1.AuxInt)
+ y := v_1.Args[0]
+ if !(c&31 == 0) {
+ break
+ }
+ v.reset(OpAMD64SHLXL)
+ v.AddArg2(x, y)
+ return true
+ }
+ // match: (SHLXL x (NEGL <t> (ADDLconst [c] y)))
+ // cond: c & 31 == 0
+ // result: (SHLXL x (NEGL <t> y))
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64NEGL {
+ break
+ }
+ t := v_1.Type
+ v_1_0 := v_1.Args[0]
+ if v_1_0.Op != OpAMD64ADDLconst {
+ break
+ }
+ c := auxIntToInt32(v_1_0.AuxInt)
+ y := v_1_0.Args[0]
+ if !(c&31 == 0) {
+ break
+ }
+ v.reset(OpAMD64SHLXL)
+ v0 := b.NewValue0(v.Pos, OpAMD64NEGL, t)
+ v0.AddArg(y)
+ v.AddArg2(x, v0)
+ return true
+ }
+ // match: (SHLXL x (ANDLconst [c] y))
+ // cond: c & 31 == 31
+ // result: (SHLXL x y)
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64ANDLconst {
+ break
+ }
+ c := auxIntToInt32(v_1.AuxInt)
+ y := v_1.Args[0]
+ if !(c&31 == 31) {
+ break
+ }
+ v.reset(OpAMD64SHLXL)
+ v.AddArg2(x, y)
+ return true
+ }
+ // match: (SHLXL x (NEGL <t> (ANDLconst [c] y)))
+ // cond: c & 31 == 31
+ // result: (SHLXL x (NEGL <t> y))
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64NEGL {
+ break
+ }
+ t := v_1.Type
+ v_1_0 := v_1.Args[0]
+ if v_1_0.Op != OpAMD64ANDLconst {
+ break
+ }
+ c := auxIntToInt32(v_1_0.AuxInt)
+ y := v_1_0.Args[0]
+ if !(c&31 == 31) {
+ break
+ }
+ v.reset(OpAMD64SHLXL)
+ v0 := b.NewValue0(v.Pos, OpAMD64NEGL, t)
+ v0.AddArg(y)
+ v.AddArg2(x, v0)
+ return true
+ }
+ // match: (SHLXL l:(MOVLload [off] {sym} ptr mem) x)
+ // cond: canMergeLoad(v, l) && clobber(l)
+ // result: (SHLXLload [off] {sym} ptr x mem)
+ for {
+ l := v_0
+ if l.Op != OpAMD64MOVLload {
+ break
+ }
+ off := auxIntToInt32(l.AuxInt)
+ sym := auxToSym(l.Aux)
+ mem := l.Args[1]
+ ptr := l.Args[0]
+ x := v_1
+ if !(canMergeLoad(v, l) && clobber(l)) {
+ break
+ }
+ v.reset(OpAMD64SHLXLload)
+ v.AuxInt = int32ToAuxInt(off)
+ v.Aux = symToAux(sym)
+ v.AddArg3(ptr, x, mem)
+ return true
+ }
+ return false
+}
+func rewriteValueAMD64_OpAMD64SHLXLload(v *Value) bool {
+ v_2 := v.Args[2]
+ v_1 := v.Args[1]
+ v_0 := v.Args[0]
+ b := v.Block
+ typ := &b.Func.Config.Types
+ // match: (SHLXLload [off] {sym} ptr (MOVLconst [c]) mem)
+ // result: (SHLLconst [int8(c&31)] (MOVLload [off] {sym} ptr mem))
+ for {
+ off := auxIntToInt32(v.AuxInt)
+ sym := auxToSym(v.Aux)
+ ptr := v_0
+ if v_1.Op != OpAMD64MOVLconst {
+ break
+ }
+ c := auxIntToInt32(v_1.AuxInt)
+ mem := v_2
+ v.reset(OpAMD64SHLLconst)
+ v.AuxInt = int8ToAuxInt(int8(c & 31))
+ v0 := b.NewValue0(v.Pos, OpAMD64MOVLload, typ.UInt32)
+ v0.AuxInt = int32ToAuxInt(off)
+ v0.Aux = symToAux(sym)
+ v0.AddArg2(ptr, mem)
+ v.AddArg(v0)
+ return true
+ }
+ return false
+}
+func rewriteValueAMD64_OpAMD64SHLXQ(v *Value) bool {
+ v_1 := v.Args[1]
+ v_0 := v.Args[0]
+ b := v.Block
+ // match: (SHLXQ x (MOVQconst [c]))
+ // result: (SHLQconst [int8(c&63)] x)
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64MOVQconst {
+ break
+ }
+ c := auxIntToInt64(v_1.AuxInt)
+ v.reset(OpAMD64SHLQconst)
+ v.AuxInt = int8ToAuxInt(int8(c & 63))
+ v.AddArg(x)
+ return true
+ }
+ // match: (SHLXQ x (MOVLconst [c]))
+ // result: (SHLQconst [int8(c&63)] x)
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64MOVLconst {
+ break
+ }
+ c := auxIntToInt32(v_1.AuxInt)
+ v.reset(OpAMD64SHLQconst)
+ v.AuxInt = int8ToAuxInt(int8(c & 63))
+ v.AddArg(x)
+ return true
+ }
+ // match: (SHLXQ x (ADDQconst [c] y))
+ // cond: c & 63 == 0
+ // result: (SHLXQ x y)
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64ADDQconst {
+ break
+ }
+ c := auxIntToInt32(v_1.AuxInt)
+ y := v_1.Args[0]
+ if !(c&63 == 0) {
+ break
+ }
+ v.reset(OpAMD64SHLXQ)
+ v.AddArg2(x, y)
+ return true
+ }
+ // match: (SHLXQ x (NEGQ <t> (ADDQconst [c] y)))
+ // cond: c & 63 == 0
+ // result: (SHLXQ x (NEGQ <t> y))
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64NEGQ {
+ break
+ }
+ t := v_1.Type
+ v_1_0 := v_1.Args[0]
+ if v_1_0.Op != OpAMD64ADDQconst {
+ break
+ }
+ c := auxIntToInt32(v_1_0.AuxInt)
+ y := v_1_0.Args[0]
+ if !(c&63 == 0) {
+ break
+ }
+ v.reset(OpAMD64SHLXQ)
+ v0 := b.NewValue0(v.Pos, OpAMD64NEGQ, t)
+ v0.AddArg(y)
+ v.AddArg2(x, v0)
+ return true
+ }
+ // match: (SHLXQ x (ANDQconst [c] y))
+ // cond: c & 63 == 63
+ // result: (SHLXQ x y)
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64ANDQconst {
+ break
+ }
+ c := auxIntToInt32(v_1.AuxInt)
+ y := v_1.Args[0]
+ if !(c&63 == 63) {
+ break
+ }
+ v.reset(OpAMD64SHLXQ)
+ v.AddArg2(x, y)
+ return true
+ }
+ // match: (SHLXQ x (NEGQ <t> (ANDQconst [c] y)))
+ // cond: c & 63 == 63
+ // result: (SHLXQ x (NEGQ <t> y))
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64NEGQ {
+ break
+ }
+ t := v_1.Type
+ v_1_0 := v_1.Args[0]
+ if v_1_0.Op != OpAMD64ANDQconst {
+ break
+ }
+ c := auxIntToInt32(v_1_0.AuxInt)
+ y := v_1_0.Args[0]
+ if !(c&63 == 63) {
+ break
+ }
+ v.reset(OpAMD64SHLXQ)
+ v0 := b.NewValue0(v.Pos, OpAMD64NEGQ, t)
+ v0.AddArg(y)
+ v.AddArg2(x, v0)
+ return true
+ }
+ // match: (SHLXQ x (ADDLconst [c] y))
+ // cond: c & 63 == 0
+ // result: (SHLXQ x y)
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64ADDLconst {
+ break
+ }
+ c := auxIntToInt32(v_1.AuxInt)
+ y := v_1.Args[0]
+ if !(c&63 == 0) {
+ break
+ }
+ v.reset(OpAMD64SHLXQ)
+ v.AddArg2(x, y)
+ return true
+ }
+ // match: (SHLXQ x (NEGL <t> (ADDLconst [c] y)))
+ // cond: c & 63 == 0
+ // result: (SHLXQ x (NEGL <t> y))
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64NEGL {
+ break
+ }
+ t := v_1.Type
+ v_1_0 := v_1.Args[0]
+ if v_1_0.Op != OpAMD64ADDLconst {
+ break
+ }
+ c := auxIntToInt32(v_1_0.AuxInt)
+ y := v_1_0.Args[0]
+ if !(c&63 == 0) {
+ break
+ }
+ v.reset(OpAMD64SHLXQ)
+ v0 := b.NewValue0(v.Pos, OpAMD64NEGL, t)
+ v0.AddArg(y)
+ v.AddArg2(x, v0)
+ return true
+ }
+ // match: (SHLXQ x (ANDLconst [c] y))
+ // cond: c & 63 == 63
+ // result: (SHLXQ x y)
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64ANDLconst {
+ break
+ }
+ c := auxIntToInt32(v_1.AuxInt)
+ y := v_1.Args[0]
+ if !(c&63 == 63) {
+ break
+ }
+ v.reset(OpAMD64SHLXQ)
+ v.AddArg2(x, y)
+ return true
+ }
+ // match: (SHLXQ x (NEGL <t> (ANDLconst [c] y)))
+ // cond: c & 63 == 63
+ // result: (SHLXQ x (NEGL <t> y))
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64NEGL {
+ break
+ }
+ t := v_1.Type
+ v_1_0 := v_1.Args[0]
+ if v_1_0.Op != OpAMD64ANDLconst {
+ break
+ }
+ c := auxIntToInt32(v_1_0.AuxInt)
+ y := v_1_0.Args[0]
+ if !(c&63 == 63) {
+ break
+ }
+ v.reset(OpAMD64SHLXQ)
+ v0 := b.NewValue0(v.Pos, OpAMD64NEGL, t)
+ v0.AddArg(y)
+ v.AddArg2(x, v0)
+ return true
+ }
+ // match: (SHLXQ l:(MOVQload [off] {sym} ptr mem) x)
+ // cond: canMergeLoad(v, l) && clobber(l)
+ // result: (SHLXQload [off] {sym} ptr x mem)
+ for {
+ l := v_0
+ if l.Op != OpAMD64MOVQload {
+ break
+ }
+ off := auxIntToInt32(l.AuxInt)
+ sym := auxToSym(l.Aux)
+ mem := l.Args[1]
+ ptr := l.Args[0]
+ x := v_1
+ if !(canMergeLoad(v, l) && clobber(l)) {
+ break
+ }
+ v.reset(OpAMD64SHLXQload)
+ v.AuxInt = int32ToAuxInt(off)
+ v.Aux = symToAux(sym)
+ v.AddArg3(ptr, x, mem)
+ return true
+ }
+ return false
+}
+func rewriteValueAMD64_OpAMD64SHLXQload(v *Value) bool {
+ v_2 := v.Args[2]
+ v_1 := v.Args[1]
+ v_0 := v.Args[0]
+ b := v.Block
+ typ := &b.Func.Config.Types
+ // match: (SHLXQload [off] {sym} ptr (MOVQconst [c]) mem)
+ // result: (SHLQconst [int8(c&63)] (MOVQload [off] {sym} ptr mem))
+ for {
+ off := auxIntToInt32(v.AuxInt)
+ sym := auxToSym(v.Aux)
+ ptr := v_0
+ if v_1.Op != OpAMD64MOVQconst {
+ break
+ }
+ c := auxIntToInt64(v_1.AuxInt)
+ mem := v_2
+ v.reset(OpAMD64SHLQconst)
+ v.AuxInt = int8ToAuxInt(int8(c & 63))
+ v0 := b.NewValue0(v.Pos, OpAMD64MOVQload, typ.UInt64)
+ v0.AuxInt = int32ToAuxInt(off)
+ v0.Aux = symToAux(sym)
+ v0.AddArg2(ptr, mem)
+ v.AddArg(v0)
+ return true
+ }
+ // match: (SHLXQload [off] {sym} ptr (MOVLconst [c]) mem)
+ // result: (SHLQconst [int8(c&63)] (MOVQload [off] {sym} ptr mem))
+ for {
+ off := auxIntToInt32(v.AuxInt)
+ sym := auxToSym(v.Aux)
+ ptr := v_0
+ if v_1.Op != OpAMD64MOVLconst {
+ break
+ }
+ c := auxIntToInt32(v_1.AuxInt)
+ mem := v_2
+ v.reset(OpAMD64SHLQconst)
+ v.AuxInt = int8ToAuxInt(int8(c & 63))
+ v0 := b.NewValue0(v.Pos, OpAMD64MOVQload, typ.UInt64)
+ v0.AuxInt = int32ToAuxInt(off)
+ v0.Aux = symToAux(sym)
+ v0.AddArg2(ptr, mem)
+ v.AddArg(v0)
+ return true
+ }
+ return false
+}
func rewriteValueAMD64_OpAMD64SHRB(v *Value) bool {
v_1 := v.Args[1]
v_0 := v.Args[0]
@@ -25058,6 +27382,19 @@ func rewriteValueAMD64_OpAMD64SHRL(v *Value) bool {
v_1 := v.Args[1]
v_0 := v.Args[0]
b := v.Block
+ // match: (SHRL x y)
+ // cond: buildcfg.GOAMD64 >= 3
+ // result: (SHRXL x y)
+ for {
+ x := v_0
+ y := v_1
+ if !(buildcfg.GOAMD64 >= 3) {
+ break
+ }
+ v.reset(OpAMD64SHRXL)
+ v.AddArg2(x, y)
+ return true
+ }
// match: (SHRL x (MOVQconst [c]))
// result: (SHRLconst [int8(c&31)] x)
for {
@@ -25248,28 +27585,6 @@ func rewriteValueAMD64_OpAMD64SHRL(v *Value) bool {
v.AddArg2(x, v0)
return true
}
- // match: (SHRL l:(MOVLload [off] {sym} ptr mem) x)
- // cond: buildcfg.GOAMD64 >= 3 && canMergeLoad(v, l) && clobber(l)
- // result: (SHRXLload [off] {sym} ptr x mem)
- for {
- l := v_0
- if l.Op != OpAMD64MOVLload {
- break
- }
- off := auxIntToInt32(l.AuxInt)
- sym := auxToSym(l.Aux)
- mem := l.Args[1]
- ptr := l.Args[0]
- x := v_1
- if !(buildcfg.GOAMD64 >= 3 && canMergeLoad(v, l) && clobber(l)) {
- break
- }
- v.reset(OpAMD64SHRXLload)
- v.AuxInt = int32ToAuxInt(off)
- v.Aux = symToAux(sym)
- v.AddArg3(ptr, x, mem)
- return true
- }
return false
}
func rewriteValueAMD64_OpAMD64SHRLconst(v *Value) bool {
@@ -25302,6 +27617,19 @@ func rewriteValueAMD64_OpAMD64SHRQ(v *Value) bool {
v_1 := v.Args[1]
v_0 := v.Args[0]
b := v.Block
+ // match: (SHRQ x y)
+ // cond: buildcfg.GOAMD64 >= 3
+ // result: (SHRXQ x y)
+ for {
+ x := v_0
+ y := v_1
+ if !(buildcfg.GOAMD64 >= 3) {
+ break
+ }
+ v.reset(OpAMD64SHRXQ)
+ v.AddArg2(x, y)
+ return true
+ }
// match: (SHRQ x (MOVQconst [c]))
// result: (SHRQconst [int8(c&63)] x)
for {
@@ -25492,28 +27820,6 @@ func rewriteValueAMD64_OpAMD64SHRQ(v *Value) bool {
v.AddArg2(x, v0)
return true
}
- // match: (SHRQ l:(MOVQload [off] {sym} ptr mem) x)
- // cond: buildcfg.GOAMD64 >= 3 && canMergeLoad(v, l) && clobber(l)
- // result: (SHRXQload [off] {sym} ptr x mem)
- for {
- l := v_0
- if l.Op != OpAMD64MOVQload {
- break
- }
- off := auxIntToInt32(l.AuxInt)
- sym := auxToSym(l.Aux)
- mem := l.Args[1]
- ptr := l.Args[0]
- x := v_1
- if !(buildcfg.GOAMD64 >= 3 && canMergeLoad(v, l) && clobber(l)) {
- break
- }
- v.reset(OpAMD64SHRXQload)
- v.AuxInt = int32ToAuxInt(off)
- v.Aux = symToAux(sym)
- v.AddArg3(ptr, x, mem)
- return true
- }
return false
}
func rewriteValueAMD64_OpAMD64SHRQconst(v *Value) bool {
@@ -25625,6 +27931,518 @@ func rewriteValueAMD64_OpAMD64SHRWconst(v *Value) bool {
}
return false
}
+func rewriteValueAMD64_OpAMD64SHRXL(v *Value) bool {
+ v_1 := v.Args[1]
+ v_0 := v.Args[0]
+ b := v.Block
+ // match: (SHRXL x (MOVQconst [c]))
+ // result: (SHRLconst [int8(c&31)] x)
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64MOVQconst {
+ break
+ }
+ c := auxIntToInt64(v_1.AuxInt)
+ v.reset(OpAMD64SHRLconst)
+ v.AuxInt = int8ToAuxInt(int8(c & 31))
+ v.AddArg(x)
+ return true
+ }
+ // match: (SHRXL x (MOVLconst [c]))
+ // result: (SHRLconst [int8(c&31)] x)
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64MOVLconst {
+ break
+ }
+ c := auxIntToInt32(v_1.AuxInt)
+ v.reset(OpAMD64SHRLconst)
+ v.AuxInt = int8ToAuxInt(int8(c & 31))
+ v.AddArg(x)
+ return true
+ }
+ // match: (SHRXL x (ADDQconst [c] y))
+ // cond: c & 31 == 0
+ // result: (SHRXL x y)
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64ADDQconst {
+ break
+ }
+ c := auxIntToInt32(v_1.AuxInt)
+ y := v_1.Args[0]
+ if !(c&31 == 0) {
+ break
+ }
+ v.reset(OpAMD64SHRXL)
+ v.AddArg2(x, y)
+ return true
+ }
+ // match: (SHRXL x (NEGQ <t> (ADDQconst [c] y)))
+ // cond: c & 31 == 0
+ // result: (SHRXL x (NEGQ <t> y))
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64NEGQ {
+ break
+ }
+ t := v_1.Type
+ v_1_0 := v_1.Args[0]
+ if v_1_0.Op != OpAMD64ADDQconst {
+ break
+ }
+ c := auxIntToInt32(v_1_0.AuxInt)
+ y := v_1_0.Args[0]
+ if !(c&31 == 0) {
+ break
+ }
+ v.reset(OpAMD64SHRXL)
+ v0 := b.NewValue0(v.Pos, OpAMD64NEGQ, t)
+ v0.AddArg(y)
+ v.AddArg2(x, v0)
+ return true
+ }
+ // match: (SHRXL x (ANDQconst [c] y))
+ // cond: c & 31 == 31
+ // result: (SHRXL x y)
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64ANDQconst {
+ break
+ }
+ c := auxIntToInt32(v_1.AuxInt)
+ y := v_1.Args[0]
+ if !(c&31 == 31) {
+ break
+ }
+ v.reset(OpAMD64SHRXL)
+ v.AddArg2(x, y)
+ return true
+ }
+ // match: (SHRXL x (NEGQ <t> (ANDQconst [c] y)))
+ // cond: c & 31 == 31
+ // result: (SHRXL x (NEGQ <t> y))
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64NEGQ {
+ break
+ }
+ t := v_1.Type
+ v_1_0 := v_1.Args[0]
+ if v_1_0.Op != OpAMD64ANDQconst {
+ break
+ }
+ c := auxIntToInt32(v_1_0.AuxInt)
+ y := v_1_0.Args[0]
+ if !(c&31 == 31) {
+ break
+ }
+ v.reset(OpAMD64SHRXL)
+ v0 := b.NewValue0(v.Pos, OpAMD64NEGQ, t)
+ v0.AddArg(y)
+ v.AddArg2(x, v0)
+ return true
+ }
+ // match: (SHRXL x (ADDLconst [c] y))
+ // cond: c & 31 == 0
+ // result: (SHRXL x y)
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64ADDLconst {
+ break
+ }
+ c := auxIntToInt32(v_1.AuxInt)
+ y := v_1.Args[0]
+ if !(c&31 == 0) {
+ break
+ }
+ v.reset(OpAMD64SHRXL)
+ v.AddArg2(x, y)
+ return true
+ }
+ // match: (SHRXL x (NEGL <t> (ADDLconst [c] y)))
+ // cond: c & 31 == 0
+ // result: (SHRXL x (NEGL <t> y))
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64NEGL {
+ break
+ }
+ t := v_1.Type
+ v_1_0 := v_1.Args[0]
+ if v_1_0.Op != OpAMD64ADDLconst {
+ break
+ }
+ c := auxIntToInt32(v_1_0.AuxInt)
+ y := v_1_0.Args[0]
+ if !(c&31 == 0) {
+ break
+ }
+ v.reset(OpAMD64SHRXL)
+ v0 := b.NewValue0(v.Pos, OpAMD64NEGL, t)
+ v0.AddArg(y)
+ v.AddArg2(x, v0)
+ return true
+ }
+ // match: (SHRXL x (ANDLconst [c] y))
+ // cond: c & 31 == 31
+ // result: (SHRXL x y)
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64ANDLconst {
+ break
+ }
+ c := auxIntToInt32(v_1.AuxInt)
+ y := v_1.Args[0]
+ if !(c&31 == 31) {
+ break
+ }
+ v.reset(OpAMD64SHRXL)
+ v.AddArg2(x, y)
+ return true
+ }
+ // match: (SHRXL x (NEGL <t> (ANDLconst [c] y)))
+ // cond: c & 31 == 31
+ // result: (SHRXL x (NEGL <t> y))
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64NEGL {
+ break
+ }
+ t := v_1.Type
+ v_1_0 := v_1.Args[0]
+ if v_1_0.Op != OpAMD64ANDLconst {
+ break
+ }
+ c := auxIntToInt32(v_1_0.AuxInt)
+ y := v_1_0.Args[0]
+ if !(c&31 == 31) {
+ break
+ }
+ v.reset(OpAMD64SHRXL)
+ v0 := b.NewValue0(v.Pos, OpAMD64NEGL, t)
+ v0.AddArg(y)
+ v.AddArg2(x, v0)
+ return true
+ }
+ // match: (SHRXL l:(MOVLload [off] {sym} ptr mem) x)
+ // cond: canMergeLoad(v, l) && clobber(l)
+ // result: (SHRXLload [off] {sym} ptr x mem)
+ for {
+ l := v_0
+ if l.Op != OpAMD64MOVLload {
+ break
+ }
+ off := auxIntToInt32(l.AuxInt)
+ sym := auxToSym(l.Aux)
+ mem := l.Args[1]
+ ptr := l.Args[0]
+ x := v_1
+ if !(canMergeLoad(v, l) && clobber(l)) {
+ break
+ }
+ v.reset(OpAMD64SHRXLload)
+ v.AuxInt = int32ToAuxInt(off)
+ v.Aux = symToAux(sym)
+ v.AddArg3(ptr, x, mem)
+ return true
+ }
+ return false
+}
+func rewriteValueAMD64_OpAMD64SHRXLload(v *Value) bool {
+ v_2 := v.Args[2]
+ v_1 := v.Args[1]
+ v_0 := v.Args[0]
+ b := v.Block
+ typ := &b.Func.Config.Types
+ // match: (SHRXLload [off] {sym} ptr (MOVLconst [c]) mem)
+ // result: (SHRLconst [int8(c&31)] (MOVLload [off] {sym} ptr mem))
+ for {
+ off := auxIntToInt32(v.AuxInt)
+ sym := auxToSym(v.Aux)
+ ptr := v_0
+ if v_1.Op != OpAMD64MOVLconst {
+ break
+ }
+ c := auxIntToInt32(v_1.AuxInt)
+ mem := v_2
+ v.reset(OpAMD64SHRLconst)
+ v.AuxInt = int8ToAuxInt(int8(c & 31))
+ v0 := b.NewValue0(v.Pos, OpAMD64MOVLload, typ.UInt32)
+ v0.AuxInt = int32ToAuxInt(off)
+ v0.Aux = symToAux(sym)
+ v0.AddArg2(ptr, mem)
+ v.AddArg(v0)
+ return true
+ }
+ return false
+}
+func rewriteValueAMD64_OpAMD64SHRXQ(v *Value) bool {
+ v_1 := v.Args[1]
+ v_0 := v.Args[0]
+ b := v.Block
+ // match: (SHRXQ x (MOVQconst [c]))
+ // result: (SHRQconst [int8(c&63)] x)
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64MOVQconst {
+ break
+ }
+ c := auxIntToInt64(v_1.AuxInt)
+ v.reset(OpAMD64SHRQconst)
+ v.AuxInt = int8ToAuxInt(int8(c & 63))
+ v.AddArg(x)
+ return true
+ }
+ // match: (SHRXQ x (MOVLconst [c]))
+ // result: (SHRQconst [int8(c&63)] x)
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64MOVLconst {
+ break
+ }
+ c := auxIntToInt32(v_1.AuxInt)
+ v.reset(OpAMD64SHRQconst)
+ v.AuxInt = int8ToAuxInt(int8(c & 63))
+ v.AddArg(x)
+ return true
+ }
+ // match: (SHRXQ x (ADDQconst [c] y))
+ // cond: c & 63 == 0
+ // result: (SHRXQ x y)
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64ADDQconst {
+ break
+ }
+ c := auxIntToInt32(v_1.AuxInt)
+ y := v_1.Args[0]
+ if !(c&63 == 0) {
+ break
+ }
+ v.reset(OpAMD64SHRXQ)
+ v.AddArg2(x, y)
+ return true
+ }
+ // match: (SHRXQ x (NEGQ <t> (ADDQconst [c] y)))
+ // cond: c & 63 == 0
+ // result: (SHRXQ x (NEGQ <t> y))
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64NEGQ {
+ break
+ }
+ t := v_1.Type
+ v_1_0 := v_1.Args[0]
+ if v_1_0.Op != OpAMD64ADDQconst {
+ break
+ }
+ c := auxIntToInt32(v_1_0.AuxInt)
+ y := v_1_0.Args[0]
+ if !(c&63 == 0) {
+ break
+ }
+ v.reset(OpAMD64SHRXQ)
+ v0 := b.NewValue0(v.Pos, OpAMD64NEGQ, t)
+ v0.AddArg(y)
+ v.AddArg2(x, v0)
+ return true
+ }
+ // match: (SHRXQ x (ANDQconst [c] y))
+ // cond: c & 63 == 63
+ // result: (SHRXQ x y)
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64ANDQconst {
+ break
+ }
+ c := auxIntToInt32(v_1.AuxInt)
+ y := v_1.Args[0]
+ if !(c&63 == 63) {
+ break
+ }
+ v.reset(OpAMD64SHRXQ)
+ v.AddArg2(x, y)
+ return true
+ }
+ // match: (SHRXQ x (NEGQ <t> (ANDQconst [c] y)))
+ // cond: c & 63 == 63
+ // result: (SHRXQ x (NEGQ <t> y))
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64NEGQ {
+ break
+ }
+ t := v_1.Type
+ v_1_0 := v_1.Args[0]
+ if v_1_0.Op != OpAMD64ANDQconst {
+ break
+ }
+ c := auxIntToInt32(v_1_0.AuxInt)
+ y := v_1_0.Args[0]
+ if !(c&63 == 63) {
+ break
+ }
+ v.reset(OpAMD64SHRXQ)
+ v0 := b.NewValue0(v.Pos, OpAMD64NEGQ, t)
+ v0.AddArg(y)
+ v.AddArg2(x, v0)
+ return true
+ }
+ // match: (SHRXQ x (ADDLconst [c] y))
+ // cond: c & 63 == 0
+ // result: (SHRXQ x y)
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64ADDLconst {
+ break
+ }
+ c := auxIntToInt32(v_1.AuxInt)
+ y := v_1.Args[0]
+ if !(c&63 == 0) {
+ break
+ }
+ v.reset(OpAMD64SHRXQ)
+ v.AddArg2(x, y)
+ return true
+ }
+ // match: (SHRXQ x (NEGL <t> (ADDLconst [c] y)))
+ // cond: c & 63 == 0
+ // result: (SHRXQ x (NEGL <t> y))
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64NEGL {
+ break
+ }
+ t := v_1.Type
+ v_1_0 := v_1.Args[0]
+ if v_1_0.Op != OpAMD64ADDLconst {
+ break
+ }
+ c := auxIntToInt32(v_1_0.AuxInt)
+ y := v_1_0.Args[0]
+ if !(c&63 == 0) {
+ break
+ }
+ v.reset(OpAMD64SHRXQ)
+ v0 := b.NewValue0(v.Pos, OpAMD64NEGL, t)
+ v0.AddArg(y)
+ v.AddArg2(x, v0)
+ return true
+ }
+ // match: (SHRXQ x (ANDLconst [c] y))
+ // cond: c & 63 == 63
+ // result: (SHRXQ x y)
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64ANDLconst {
+ break
+ }
+ c := auxIntToInt32(v_1.AuxInt)
+ y := v_1.Args[0]
+ if !(c&63 == 63) {
+ break
+ }
+ v.reset(OpAMD64SHRXQ)
+ v.AddArg2(x, y)
+ return true
+ }
+ // match: (SHRXQ x (NEGL <t> (ANDLconst [c] y)))
+ // cond: c & 63 == 63
+ // result: (SHRXQ x (NEGL <t> y))
+ for {
+ x := v_0
+ if v_1.Op != OpAMD64NEGL {
+ break
+ }
+ t := v_1.Type
+ v_1_0 := v_1.Args[0]
+ if v_1_0.Op != OpAMD64ANDLconst {
+ break
+ }
+ c := auxIntToInt32(v_1_0.AuxInt)
+ y := v_1_0.Args[0]
+ if !(c&63 == 63) {
+ break
+ }
+ v.reset(OpAMD64SHRXQ)
+ v0 := b.NewValue0(v.Pos, OpAMD64NEGL, t)
+ v0.AddArg(y)
+ v.AddArg2(x, v0)
+ return true
+ }
+ // match: (SHRXQ l:(MOVQload [off] {sym} ptr mem) x)
+ // cond: canMergeLoad(v, l) && clobber(l)
+ // result: (SHRXQload [off] {sym} ptr x mem)
+ for {
+ l := v_0
+ if l.Op != OpAMD64MOVQload {
+ break
+ }
+ off := auxIntToInt32(l.AuxInt)
+ sym := auxToSym(l.Aux)
+ mem := l.Args[1]
+ ptr := l.Args[0]
+ x := v_1
+ if !(canMergeLoad(v, l) && clobber(l)) {
+ break
+ }
+ v.reset(OpAMD64SHRXQload)
+ v.AuxInt = int32ToAuxInt(off)
+ v.Aux = symToAux(sym)
+ v.AddArg3(ptr, x, mem)
+ return true
+ }
+ return false
+}
+func rewriteValueAMD64_OpAMD64SHRXQload(v *Value) bool {
+ v_2 := v.Args[2]
+ v_1 := v.Args[1]
+ v_0 := v.Args[0]
+ b := v.Block
+ typ := &b.Func.Config.Types
+ // match: (SHRXQload [off] {sym} ptr (MOVQconst [c]) mem)
+ // result: (SHRQconst [int8(c&63)] (MOVQload [off] {sym} ptr mem))
+ for {
+ off := auxIntToInt32(v.AuxInt)
+ sym := auxToSym(v.Aux)
+ ptr := v_0
+ if v_1.Op != OpAMD64MOVQconst {
+ break
+ }
+ c := auxIntToInt64(v_1.AuxInt)
+ mem := v_2
+ v.reset(OpAMD64SHRQconst)
+ v.AuxInt = int8ToAuxInt(int8(c & 63))
+ v0 := b.NewValue0(v.Pos, OpAMD64MOVQload, typ.UInt64)
+ v0.AuxInt = int32ToAuxInt(off)
+ v0.Aux = symToAux(sym)
+ v0.AddArg2(ptr, mem)
+ v.AddArg(v0)
+ return true
+ }
+ // match: (SHRXQload [off] {sym} ptr (MOVLconst [c]) mem)
+ // result: (SHRQconst [int8(c&63)] (MOVQload [off] {sym} ptr mem))
+ for {
+ off := auxIntToInt32(v.AuxInt)
+ sym := auxToSym(v.Aux)
+ ptr := v_0
+ if v_1.Op != OpAMD64MOVLconst {
+ break
+ }
+ c := auxIntToInt32(v_1.AuxInt)
+ mem := v_2
+ v.reset(OpAMD64SHRQconst)
+ v.AuxInt = int8ToAuxInt(int8(c & 63))
+ v0 := b.NewValue0(v.Pos, OpAMD64MOVQload, typ.UInt64)
+ v0.AuxInt = int32ToAuxInt(off)
+ v0.Aux = symToAux(sym)
+ v0.AddArg2(ptr, mem)
+ v.AddArg(v0)
+ return true
+ }
+ return false
+}
func rewriteValueAMD64_OpAMD64SUBL(v *Value) bool {
v_1 := v.Args[1]
v_0 := v.Args[0]
@@ -26908,6 +29726,25 @@ func rewriteValueAMD64_OpAMD64XORL(v *Value) bool {
}
break
}
+ // match: (XORL (SHLXL (MOVLconst [1]) y) x)
+ // result: (BTCL x y)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ if v_0.Op != OpAMD64SHLXL {
+ continue
+ }
+ y := v_0.Args[1]
+ v_0_0 := v_0.Args[0]
+ if v_0_0.Op != OpAMD64MOVLconst || auxIntToInt32(v_0_0.AuxInt) != 1 {
+ continue
+ }
+ x := v_1
+ v.reset(OpAMD64BTCL)
+ v.AddArg2(x, y)
+ return true
+ }
+ break
+ }
// match: (XORL (MOVLconst [c]) x)
// cond: isUint32PowerOfTwo(int64(c)) && uint64(c) >= 128
// result: (BTCLconst [int8(log32(c))] x)
@@ -27445,6 +30282,25 @@ func rewriteValueAMD64_OpAMD64XORQ(v *Value) bool {
}
break
}
+ // match: (XORQ (SHLXQ (MOVQconst [1]) y) x)
+ // result: (BTCQ x y)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ if v_0.Op != OpAMD64SHLXQ {
+ continue
+ }
+ y := v_0.Args[1]
+ v_0_0 := v_0.Args[0]
+ if v_0_0.Op != OpAMD64MOVQconst || auxIntToInt64(v_0_0.AuxInt) != 1 {
+ continue
+ }
+ x := v_1
+ v.reset(OpAMD64BTCQ)
+ v.AddArg2(x, y)
+ return true
+ }
+ break
+ }
// match: (XORQ (MOVQconst [c]) x)
// cond: isUint64PowerOfTwo(c) && uint64(c) >= 128
// result: (BTCQconst [int8(log64(c))] x)
@@ -34017,6 +36873,7 @@ func rewriteValueAMD64_OpZero(v *Value) bool {
return false
}
func rewriteBlockAMD64(b *Block) bool {
+ typ := &b.Func.Config.Types
switch b.Kind {
case BlockAMD64EQ:
// match: (EQ (TESTL (SHLL (MOVLconst [1]) x) y))
@@ -34067,6 +36924,54 @@ func rewriteBlockAMD64(b *Block) bool {
}
break
}
+ // match: (EQ (TESTL (SHLXL (MOVLconst [1]) x) y))
+ // result: (UGE (BTL x y))
+ for b.Controls[0].Op == OpAMD64TESTL {
+ v_0 := b.Controls[0]
+ _ = v_0.Args[1]
+ v_0_0 := v_0.Args[0]
+ v_0_1 := v_0.Args[1]
+ for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 {
+ if v_0_0.Op != OpAMD64SHLXL {
+ continue
+ }
+ x := v_0_0.Args[1]
+ v_0_0_0 := v_0_0.Args[0]
+ if v_0_0_0.Op != OpAMD64MOVLconst || auxIntToInt32(v_0_0_0.AuxInt) != 1 {
+ continue
+ }
+ y := v_0_1
+ v0 := b.NewValue0(v_0.Pos, OpAMD64BTL, types.TypeFlags)
+ v0.AddArg2(x, y)
+ b.resetWithControl(BlockAMD64UGE, v0)
+ return true
+ }
+ break
+ }
+ // match: (EQ (TESTQ (SHLXQ (MOVQconst [1]) x) y))
+ // result: (UGE (BTQ x y))
+ for b.Controls[0].Op == OpAMD64TESTQ {
+ v_0 := b.Controls[0]
+ _ = v_0.Args[1]
+ v_0_0 := v_0.Args[0]
+ v_0_1 := v_0.Args[1]
+ for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 {
+ if v_0_0.Op != OpAMD64SHLXQ {
+ continue
+ }
+ x := v_0_0.Args[1]
+ v_0_0_0 := v_0_0.Args[0]
+ if v_0_0_0.Op != OpAMD64MOVQconst || auxIntToInt64(v_0_0_0.AuxInt) != 1 {
+ continue
+ }
+ y := v_0_1
+ v0 := b.NewValue0(v_0.Pos, OpAMD64BTQ, types.TypeFlags)
+ v0.AddArg2(x, y)
+ b.resetWithControl(BlockAMD64UGE, v0)
+ return true
+ }
+ break
+ }
// match: (EQ (TESTLconst [c] x))
// cond: isUint32PowerOfTwo(int64(c))
// result: (UGE (BTLconst [int8(log32(c))] x))
@@ -34551,6 +37456,19 @@ func rewriteBlockAMD64(b *Block) bool {
b.resetWithControl(BlockAMD64NE, v0)
return true
}
+ case BlockJumpTable:
+ // match: (JumpTable idx)
+ // result: (JUMPTABLE {makeJumpTableSym(b)} idx (LEAQ <typ.Uintptr> {makeJumpTableSym(b)} (SB)))
+ for {
+ idx := b.Controls[0]
+ v0 := b.NewValue0(b.Pos, OpAMD64LEAQ, typ.Uintptr)
+ v0.Aux = symToAux(makeJumpTableSym(b))
+ v1 := b.NewValue0(b.Pos, OpSB, typ.Uintptr)
+ v0.AddArg(v1)
+ b.resetWithControl2(BlockAMD64JUMPTABLE, idx, v0)
+ b.Aux = symToAux(makeJumpTableSym(b))
+ return true
+ }
case BlockAMD64LE:
// match: (LE (InvertFlags cmp) yes no)
// result: (GE cmp yes no)
@@ -34870,6 +37788,54 @@ func rewriteBlockAMD64(b *Block) bool {
}
break
}
+ // match: (NE (TESTL (SHLXL (MOVLconst [1]) x) y))
+ // result: (ULT (BTL x y))
+ for b.Controls[0].Op == OpAMD64TESTL {
+ v_0 := b.Controls[0]
+ _ = v_0.Args[1]
+ v_0_0 := v_0.Args[0]
+ v_0_1 := v_0.Args[1]
+ for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 {
+ if v_0_0.Op != OpAMD64SHLXL {
+ continue
+ }
+ x := v_0_0.Args[1]
+ v_0_0_0 := v_0_0.Args[0]
+ if v_0_0_0.Op != OpAMD64MOVLconst || auxIntToInt32(v_0_0_0.AuxInt) != 1 {
+ continue
+ }
+ y := v_0_1
+ v0 := b.NewValue0(v_0.Pos, OpAMD64BTL, types.TypeFlags)
+ v0.AddArg2(x, y)
+ b.resetWithControl(BlockAMD64ULT, v0)
+ return true
+ }
+ break
+ }
+ // match: (NE (TESTQ (SHLXQ (MOVQconst [1]) x) y))
+ // result: (ULT (BTQ x y))
+ for b.Controls[0].Op == OpAMD64TESTQ {
+ v_0 := b.Controls[0]
+ _ = v_0.Args[1]
+ v_0_0 := v_0.Args[0]
+ v_0_1 := v_0.Args[1]
+ for _i0 := 0; _i0 <= 1; _i0, v_0_0, v_0_1 = _i0+1, v_0_1, v_0_0 {
+ if v_0_0.Op != OpAMD64SHLXQ {
+ continue
+ }
+ x := v_0_0.Args[1]
+ v_0_0_0 := v_0_0.Args[0]
+ if v_0_0_0.Op != OpAMD64MOVQconst || auxIntToInt64(v_0_0_0.AuxInt) != 1 {
+ continue
+ }
+ y := v_0_1
+ v0 := b.NewValue0(v_0.Pos, OpAMD64BTQ, types.TypeFlags)
+ v0.AddArg2(x, y)
+ b.resetWithControl(BlockAMD64ULT, v0)
+ return true
+ }
+ break
+ }
// match: (NE (TESTLconst [c] x))
// cond: isUint32PowerOfTwo(int64(c))
// result: (ULT (BTLconst [int8(log32(c))] x))
diff --git a/src/cmd/compile/internal/ssa/rewriteCond_test.go b/src/cmd/compile/internal/ssa/rewriteCond_test.go
index 2c26fdf1427..ca74ed59471 100644
--- a/src/cmd/compile/internal/ssa/rewriteCond_test.go
+++ b/src/cmd/compile/internal/ssa/rewriteCond_test.go
@@ -68,8 +68,10 @@ func TestCondRewrite(t *testing.T) {
}
// Profile the aforementioned optimization from two angles:
-// SoloJump: generated branching code has one 'jump', for '<' and '>='
-// CombJump: generated branching code has two consecutive 'jump', for '<=' and '>'
+//
+// SoloJump: generated branching code has one 'jump', for '<' and '>='
+// CombJump: generated branching code has two consecutive 'jump', for '<=' and '>'
+//
// We expect that 'CombJump' is generally on par with the non-optimized code, and
// 'SoloJump' demonstrates some improvement.
// It's for arm64 initially, please see https://github.com/golang/go/issues/38740
diff --git a/src/cmd/compile/internal/ssa/rewritegeneric.go b/src/cmd/compile/internal/ssa/rewritegeneric.go
index fbf227562a9..f61b6ca3ece 100644
--- a/src/cmd/compile/internal/ssa/rewritegeneric.go
+++ b/src/cmd/compile/internal/ssa/rewritegeneric.go
@@ -519,6 +519,38 @@ func rewriteValuegeneric_OpAdd16(v *Value) bool {
}
break
}
+ // match: (Add16 x (Neg16 y))
+ // result: (Sub16 x y)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ x := v_0
+ if v_1.Op != OpNeg16 {
+ continue
+ }
+ y := v_1.Args[0]
+ v.reset(OpSub16)
+ v.AddArg2(x, y)
+ return true
+ }
+ break
+ }
+ // match: (Add16 (Com16 x) x)
+ // result: (Const16 [-1])
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ if v_0.Op != OpCom16 {
+ continue
+ }
+ x := v_0.Args[0]
+ if x != v_1 {
+ continue
+ }
+ v.reset(OpConst16)
+ v.AuxInt = int16ToAuxInt(-1)
+ return true
+ }
+ break
+ }
// match: (Add16 (Const16 [1]) (Com16 x))
// result: (Neg16 x)
for {
@@ -764,6 +796,38 @@ func rewriteValuegeneric_OpAdd32(v *Value) bool {
}
break
}
+ // match: (Add32 x (Neg32 y))
+ // result: (Sub32 x y)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ x := v_0
+ if v_1.Op != OpNeg32 {
+ continue
+ }
+ y := v_1.Args[0]
+ v.reset(OpSub32)
+ v.AddArg2(x, y)
+ return true
+ }
+ break
+ }
+ // match: (Add32 (Com32 x) x)
+ // result: (Const32 [-1])
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ if v_0.Op != OpCom32 {
+ continue
+ }
+ x := v_0.Args[0]
+ if x != v_1 {
+ continue
+ }
+ v.reset(OpConst32)
+ v.AuxInt = int32ToAuxInt(-1)
+ return true
+ }
+ break
+ }
// match: (Add32 (Const32 [1]) (Com32 x))
// result: (Neg32 x)
for {
@@ -1036,6 +1100,38 @@ func rewriteValuegeneric_OpAdd64(v *Value) bool {
}
break
}
+ // match: (Add64 x (Neg64 y))
+ // result: (Sub64 x y)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ x := v_0
+ if v_1.Op != OpNeg64 {
+ continue
+ }
+ y := v_1.Args[0]
+ v.reset(OpSub64)
+ v.AddArg2(x, y)
+ return true
+ }
+ break
+ }
+ // match: (Add64 (Com64 x) x)
+ // result: (Const64 [-1])
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ if v_0.Op != OpCom64 {
+ continue
+ }
+ x := v_0.Args[0]
+ if x != v_1 {
+ continue
+ }
+ v.reset(OpConst64)
+ v.AuxInt = int64ToAuxInt(-1)
+ return true
+ }
+ break
+ }
// match: (Add64 (Const64 [1]) (Com64 x))
// result: (Neg64 x)
for {
@@ -1308,6 +1404,38 @@ func rewriteValuegeneric_OpAdd8(v *Value) bool {
}
break
}
+ // match: (Add8 x (Neg8 y))
+ // result: (Sub8 x y)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ x := v_0
+ if v_1.Op != OpNeg8 {
+ continue
+ }
+ y := v_1.Args[0]
+ v.reset(OpSub8)
+ v.AddArg2(x, y)
+ return true
+ }
+ break
+ }
+ // match: (Add8 (Com8 x) x)
+ // result: (Const8 [-1])
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ if v_0.Op != OpCom8 {
+ continue
+ }
+ x := v_0.Args[0]
+ if x != v_1 {
+ continue
+ }
+ v.reset(OpConst8)
+ v.AuxInt = int8ToAuxInt(-1)
+ return true
+ }
+ break
+ }
// match: (Add8 (Const8 [1]) (Com8 x))
// result: (Neg8 x)
for {
@@ -1630,6 +1758,23 @@ func rewriteValuegeneric_OpAnd16(v *Value) bool {
}
break
}
+ // match: (And16 (Com16 x) x)
+ // result: (Const16 [0])
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ if v_0.Op != OpCom16 {
+ continue
+ }
+ x := v_0.Args[0]
+ if x != v_1 {
+ continue
+ }
+ v.reset(OpConst16)
+ v.AuxInt = int16ToAuxInt(0)
+ return true
+ }
+ break
+ }
// match: (And16 x (And16 x y))
// result: (And16 x y)
for {
@@ -1828,6 +1973,23 @@ func rewriteValuegeneric_OpAnd32(v *Value) bool {
}
break
}
+ // match: (And32 (Com32 x) x)
+ // result: (Const32 [0])
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ if v_0.Op != OpCom32 {
+ continue
+ }
+ x := v_0.Args[0]
+ if x != v_1 {
+ continue
+ }
+ v.reset(OpConst32)
+ v.AuxInt = int32ToAuxInt(0)
+ return true
+ }
+ break
+ }
// match: (And32 x (And32 x y))
// result: (And32 x y)
for {
@@ -2026,6 +2188,23 @@ func rewriteValuegeneric_OpAnd64(v *Value) bool {
}
break
}
+ // match: (And64 (Com64 x) x)
+ // result: (Const64 [0])
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ if v_0.Op != OpCom64 {
+ continue
+ }
+ x := v_0.Args[0]
+ if x != v_1 {
+ continue
+ }
+ v.reset(OpConst64)
+ v.AuxInt = int64ToAuxInt(0)
+ return true
+ }
+ break
+ }
// match: (And64 x (And64 x y))
// result: (And64 x y)
for {
@@ -2224,6 +2403,23 @@ func rewriteValuegeneric_OpAnd8(v *Value) bool {
}
break
}
+ // match: (And8 (Com8 x) x)
+ // result: (Const8 [0])
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ if v_0.Op != OpCom8 {
+ continue
+ }
+ x := v_0.Args[0]
+ if x != v_1 {
+ continue
+ }
+ v.reset(OpConst8)
+ v.AuxInt = int8ToAuxInt(0)
+ return true
+ }
+ break
+ }
// match: (And8 x (And8 x y))
// result: (And8 x y)
for {
@@ -16964,6 +17160,23 @@ func rewriteValuegeneric_OpOr16(v *Value) bool {
}
break
}
+ // match: (Or16 (Com16 x) x)
+ // result: (Const16 [-1])
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ if v_0.Op != OpCom16 {
+ continue
+ }
+ x := v_0.Args[0]
+ if x != v_1 {
+ continue
+ }
+ v.reset(OpConst16)
+ v.AuxInt = int16ToAuxInt(-1)
+ return true
+ }
+ break
+ }
// match: (Or16 x (Or16 x y))
// result: (Or16 x y)
for {
@@ -17142,6 +17355,23 @@ func rewriteValuegeneric_OpOr32(v *Value) bool {
}
break
}
+ // match: (Or32 (Com32 x) x)
+ // result: (Const32 [-1])
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ if v_0.Op != OpCom32 {
+ continue
+ }
+ x := v_0.Args[0]
+ if x != v_1 {
+ continue
+ }
+ v.reset(OpConst32)
+ v.AuxInt = int32ToAuxInt(-1)
+ return true
+ }
+ break
+ }
// match: (Or32 x (Or32 x y))
// result: (Or32 x y)
for {
@@ -17320,6 +17550,23 @@ func rewriteValuegeneric_OpOr64(v *Value) bool {
}
break
}
+ // match: (Or64 (Com64 x) x)
+ // result: (Const64 [-1])
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ if v_0.Op != OpCom64 {
+ continue
+ }
+ x := v_0.Args[0]
+ if x != v_1 {
+ continue
+ }
+ v.reset(OpConst64)
+ v.AuxInt = int64ToAuxInt(-1)
+ return true
+ }
+ break
+ }
// match: (Or64 x (Or64 x y))
// result: (Or64 x y)
for {
@@ -17498,6 +17745,23 @@ func rewriteValuegeneric_OpOr8(v *Value) bool {
}
break
}
+ // match: (Or8 (Com8 x) x)
+ // result: (Const8 [-1])
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ if v_0.Op != OpCom8 {
+ continue
+ }
+ x := v_0.Args[0]
+ if x != v_1 {
+ continue
+ }
+ v.reset(OpConst8)
+ v.AuxInt = int8ToAuxInt(-1)
+ return true
+ }
+ break
+ }
// match: (Or8 x (Or8 x y))
// result: (Or8 x y)
for {
@@ -22994,6 +23258,34 @@ func rewriteValuegeneric_OpSub16(v *Value) bool {
v.AuxInt = int16ToAuxInt(0)
return true
}
+ // match: (Sub16 (Neg16 x) (Com16 x))
+ // result: (Const16 [1])
+ for {
+ if v_0.Op != OpNeg16 {
+ break
+ }
+ x := v_0.Args[0]
+ if v_1.Op != OpCom16 || x != v_1.Args[0] {
+ break
+ }
+ v.reset(OpConst16)
+ v.AuxInt = int16ToAuxInt(1)
+ return true
+ }
+ // match: (Sub16 (Com16 x) (Neg16 x))
+ // result: (Const16 [-1])
+ for {
+ if v_0.Op != OpCom16 {
+ break
+ }
+ x := v_0.Args[0]
+ if v_1.Op != OpNeg16 || x != v_1.Args[0] {
+ break
+ }
+ v.reset(OpConst16)
+ v.AuxInt = int16ToAuxInt(-1)
+ return true
+ }
// match: (Sub16 (Add16 x y) x)
// result: y
for {
@@ -23309,6 +23601,34 @@ func rewriteValuegeneric_OpSub32(v *Value) bool {
v.AuxInt = int32ToAuxInt(0)
return true
}
+ // match: (Sub32 (Neg32 x) (Com32 x))
+ // result: (Const32 [1])
+ for {
+ if v_0.Op != OpNeg32 {
+ break
+ }
+ x := v_0.Args[0]
+ if v_1.Op != OpCom32 || x != v_1.Args[0] {
+ break
+ }
+ v.reset(OpConst32)
+ v.AuxInt = int32ToAuxInt(1)
+ return true
+ }
+ // match: (Sub32 (Com32 x) (Neg32 x))
+ // result: (Const32 [-1])
+ for {
+ if v_0.Op != OpCom32 {
+ break
+ }
+ x := v_0.Args[0]
+ if v_1.Op != OpNeg32 || x != v_1.Args[0] {
+ break
+ }
+ v.reset(OpConst32)
+ v.AuxInt = int32ToAuxInt(-1)
+ return true
+ }
// match: (Sub32 (Add32 x y) x)
// result: y
for {
@@ -23648,6 +23968,34 @@ func rewriteValuegeneric_OpSub64(v *Value) bool {
v.AuxInt = int64ToAuxInt(0)
return true
}
+ // match: (Sub64 (Neg64 x) (Com64 x))
+ // result: (Const64 [1])
+ for {
+ if v_0.Op != OpNeg64 {
+ break
+ }
+ x := v_0.Args[0]
+ if v_1.Op != OpCom64 || x != v_1.Args[0] {
+ break
+ }
+ v.reset(OpConst64)
+ v.AuxInt = int64ToAuxInt(1)
+ return true
+ }
+ // match: (Sub64 (Com64 x) (Neg64 x))
+ // result: (Const64 [-1])
+ for {
+ if v_0.Op != OpCom64 {
+ break
+ }
+ x := v_0.Args[0]
+ if v_1.Op != OpNeg64 || x != v_1.Args[0] {
+ break
+ }
+ v.reset(OpConst64)
+ v.AuxInt = int64ToAuxInt(-1)
+ return true
+ }
// match: (Sub64 (Add64 x y) x)
// result: y
for {
@@ -23987,6 +24335,34 @@ func rewriteValuegeneric_OpSub8(v *Value) bool {
v.AuxInt = int8ToAuxInt(0)
return true
}
+ // match: (Sub8 (Neg8 x) (Com8 x))
+ // result: (Const8 [1])
+ for {
+ if v_0.Op != OpNeg8 {
+ break
+ }
+ x := v_0.Args[0]
+ if v_1.Op != OpCom8 || x != v_1.Args[0] {
+ break
+ }
+ v.reset(OpConst8)
+ v.AuxInt = int8ToAuxInt(1)
+ return true
+ }
+ // match: (Sub8 (Com8 x) (Neg8 x))
+ // result: (Const8 [-1])
+ for {
+ if v_0.Op != OpCom8 {
+ break
+ }
+ x := v_0.Args[0]
+ if v_1.Op != OpNeg8 || x != v_1.Args[0] {
+ break
+ }
+ v.reset(OpConst8)
+ v.AuxInt = int8ToAuxInt(-1)
+ return true
+ }
// match: (Sub8 (Add8 x y) x)
// result: y
for {
@@ -24714,6 +25090,37 @@ func rewriteValuegeneric_OpXor16(v *Value) bool {
}
break
}
+ // match: (Xor16 (Com16 x) x)
+ // result: (Const16 [-1])
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ if v_0.Op != OpCom16 {
+ continue
+ }
+ x := v_0.Args[0]
+ if x != v_1 {
+ continue
+ }
+ v.reset(OpConst16)
+ v.AuxInt = int16ToAuxInt(-1)
+ return true
+ }
+ break
+ }
+ // match: (Xor16 (Const16 [-1]) x)
+ // result: (Com16 x)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ if v_0.Op != OpConst16 || auxIntToInt16(v_0.AuxInt) != -1 {
+ continue
+ }
+ x := v_1
+ v.reset(OpCom16)
+ v.AddArg(x)
+ return true
+ }
+ break
+ }
// match: (Xor16 x (Xor16 x y))
// result: y
for {
@@ -24845,6 +25252,37 @@ func rewriteValuegeneric_OpXor32(v *Value) bool {
}
break
}
+ // match: (Xor32 (Com32 x) x)
+ // result: (Const32 [-1])
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ if v_0.Op != OpCom32 {
+ continue
+ }
+ x := v_0.Args[0]
+ if x != v_1 {
+ continue
+ }
+ v.reset(OpConst32)
+ v.AuxInt = int32ToAuxInt(-1)
+ return true
+ }
+ break
+ }
+ // match: (Xor32 (Const32 [-1]) x)
+ // result: (Com32 x)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ if v_0.Op != OpConst32 || auxIntToInt32(v_0.AuxInt) != -1 {
+ continue
+ }
+ x := v_1
+ v.reset(OpCom32)
+ v.AddArg(x)
+ return true
+ }
+ break
+ }
// match: (Xor32 x (Xor32 x y))
// result: y
for {
@@ -24976,6 +25414,37 @@ func rewriteValuegeneric_OpXor64(v *Value) bool {
}
break
}
+ // match: (Xor64 (Com64 x) x)
+ // result: (Const64 [-1])
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ if v_0.Op != OpCom64 {
+ continue
+ }
+ x := v_0.Args[0]
+ if x != v_1 {
+ continue
+ }
+ v.reset(OpConst64)
+ v.AuxInt = int64ToAuxInt(-1)
+ return true
+ }
+ break
+ }
+ // match: (Xor64 (Const64 [-1]) x)
+ // result: (Com64 x)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ if v_0.Op != OpConst64 || auxIntToInt64(v_0.AuxInt) != -1 {
+ continue
+ }
+ x := v_1
+ v.reset(OpCom64)
+ v.AddArg(x)
+ return true
+ }
+ break
+ }
// match: (Xor64 x (Xor64 x y))
// result: y
for {
@@ -25107,6 +25576,37 @@ func rewriteValuegeneric_OpXor8(v *Value) bool {
}
break
}
+ // match: (Xor8 (Com8 x) x)
+ // result: (Const8 [-1])
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ if v_0.Op != OpCom8 {
+ continue
+ }
+ x := v_0.Args[0]
+ if x != v_1 {
+ continue
+ }
+ v.reset(OpConst8)
+ v.AuxInt = int8ToAuxInt(-1)
+ return true
+ }
+ break
+ }
+ // match: (Xor8 (Const8 [-1]) x)
+ // result: (Com8 x)
+ for {
+ for _i0 := 0; _i0 <= 1; _i0, v_0, v_1 = _i0+1, v_1, v_0 {
+ if v_0.Op != OpConst8 || auxIntToInt8(v_0.AuxInt) != -1 {
+ continue
+ }
+ x := v_1
+ v.reset(OpCom8)
+ v.AddArg(x)
+ return true
+ }
+ break
+ }
// match: (Xor8 x (Xor8 x y))
// result: y
for {
diff --git a/src/cmd/compile/internal/ssa/schedule.go b/src/cmd/compile/internal/ssa/schedule.go
index c5130b2ee50..170d8b70950 100644
--- a/src/cmd/compile/internal/ssa/schedule.go
+++ b/src/cmd/compile/internal/ssa/schedule.go
@@ -338,13 +338,15 @@ func schedule(f *Func) {
// if v transitively depends on store s, v is ordered after s,
// otherwise v is ordered before s.
// Specifically, values are ordered like
-// store1
-// NilCheck that depends on store1
-// other values that depends on store1
-// store2
-// NilCheck that depends on store2
-// other values that depends on store2
-// ...
+//
+// store1
+// NilCheck that depends on store1
+// other values that depends on store1
+// store2
+// NilCheck that depends on store2
+// other values that depends on store2
+// ...
+//
// The order of non-store and non-NilCheck values are undefined
// (not necessarily dependency order). This should be cheaper
// than a full scheduling as done above.
diff --git a/src/cmd/compile/internal/ssa/shift_test.go b/src/cmd/compile/internal/ssa/shift_test.go
index 3876d8df12c..06c2f6720ff 100644
--- a/src/cmd/compile/internal/ssa/shift_test.go
+++ b/src/cmd/compile/internal/ssa/shift_test.go
@@ -85,7 +85,7 @@ func TestShiftToExtensionAMD64(t *testing.T) {
// makeShiftExtensionFunc generates a function containing:
//
-// (rshift (lshift (Const64 [amount])) (Const64 [amount]))
+// (rshift (lshift (Const64 [amount])) (Const64 [amount]))
//
// This may be equivalent to a sign or zero extension.
func makeShiftExtensionFunc(c *Conf, amount int64, lshift, rshift Op, typ *types.Type) fun {
diff --git a/src/cmd/compile/internal/ssa/shortcircuit.go b/src/cmd/compile/internal/ssa/shortcircuit.go
index c0b9eacf417..5f1f8921207 100644
--- a/src/cmd/compile/internal/ssa/shortcircuit.go
+++ b/src/cmd/compile/internal/ssa/shortcircuit.go
@@ -67,11 +67,11 @@ func shortcircuit(f *Func) {
//
// (1) Look for a CFG of the form
//
-// p other pred(s)
-// \ /
-// b
-// / \
-// t other succ
+// p other pred(s)
+// \ /
+// b
+// / \
+// t other succ
//
// in which b is an If block containing a single phi value with a single use (b's Control),
// which has a ConstBool arg.
@@ -80,21 +80,21 @@ func shortcircuit(f *Func) {
//
// Rewrite this into
//
-// p other pred(s)
-// | /
-// | b
-// |/ \
-// t u
+// p other pred(s)
+// | /
+// | b
+// |/ \
+// t u
//
// and remove the appropriate phi arg(s).
//
// (2) Look for a CFG of the form
//
-// p q
-// \ /
-// b
-// / \
-// t u
+// p q
+// \ /
+// b
+// / \
+// t u
//
// in which b is as described in (1).
// However, b may also contain other phi values.
diff --git a/src/cmd/compile/internal/ssa/sparsetree.go b/src/cmd/compile/internal/ssa/sparsetree.go
index 732bb8e3214..9f4e0007d3a 100644
--- a/src/cmd/compile/internal/ssa/sparsetree.go
+++ b/src/cmd/compile/internal/ssa/sparsetree.go
@@ -210,6 +210,7 @@ func (t SparseTree) isAncestor(x, y *Block) bool {
// 1. If domorder(x) > domorder(y) then x does not dominate y.
// 2. If domorder(x) < domorder(y) and domorder(y) < domorder(z) and x does not dominate y,
// then x does not dominate z.
+//
// Property (1) means that blocks sorted by domorder always have a maximal dominant block first.
// Property (2) allows searches for dominated blocks to exit early.
func (t SparseTree) domorder(x *Block) int32 {
diff --git a/src/cmd/compile/internal/ssa/trim.go b/src/cmd/compile/internal/ssa/trim.go
index c930a205c17..1fd7b33d5f2 100644
--- a/src/cmd/compile/internal/ssa/trim.go
+++ b/src/cmd/compile/internal/ssa/trim.go
@@ -130,11 +130,11 @@ func emptyBlock(b *Block) bool {
// trimmableBlock reports whether the block can be trimmed from the CFG,
// subject to the following criteria:
-// - it should not be the first block
-// - it should be BlockPlain
-// - it should not loop back to itself
-// - it either is the single predecessor of the successor block or
-// contains no actual instructions
+// - it should not be the first block
+// - it should be BlockPlain
+// - it should not loop back to itself
+// - it either is the single predecessor of the successor block or
+// contains no actual instructions
func trimmableBlock(b *Block) bool {
if b.Kind != BlockPlain || b == b.Func.Entry {
return false
diff --git a/src/cmd/compile/internal/ssa/value.go b/src/cmd/compile/internal/ssa/value.go
index 7b411a46124..8f125cef993 100644
--- a/src/cmd/compile/internal/ssa/value.go
+++ b/src/cmd/compile/internal/ssa/value.go
@@ -228,6 +228,7 @@ func (v *Value) auxString() string {
// If/when midstack inlining is enabled (-l=4), the compiler gets both larger and slower.
// Not-inlining this method is a help (*Value.reset and *Block.NewValue0 are similar).
+//
//go:noinline
func (v *Value) AddArg(w *Value) {
if v.Args == nil {
@@ -331,6 +332,7 @@ func (v *Value) resetArgs() {
// reset is called from most rewrite rules.
// Allowing it to be inlined increases the size
// of cmd/compile by almost 10%, and slows it down.
+//
//go:noinline
func (v *Value) reset(op Op) {
if v.InCache {
@@ -377,6 +379,7 @@ func (v *Value) invalidateRecursively() bool {
// copyOf is called from rewrite rules.
// It modifies v to be (Copy a).
+//
//go:noinline
func (v *Value) copyOf(a *Value) {
if v == a {
diff --git a/src/cmd/compile/internal/ssa/writebarrier.go b/src/cmd/compile/internal/ssa/writebarrier.go
index 21eee12c853..65ff960c847 100644
--- a/src/cmd/compile/internal/ssa/writebarrier.go
+++ b/src/cmd/compile/internal/ssa/writebarrier.go
@@ -486,7 +486,7 @@ func wbcall(pos src.XPos, b *Block, fn, typ *obj.LSym, ptr, val, mem, sp, sb *Va
inRegs := b.Func.ABIDefault == b.Func.ABI1 && len(config.intParamRegs) >= 3
// put arguments on stack
- off := config.ctxt.FixedFrameSize()
+ off := config.ctxt.Arch.FixedFrameSize
var argTypes []*types.Type
if typ != nil { // for typedmemmove
@@ -529,7 +529,7 @@ func wbcall(pos src.XPos, b *Block, fn, typ *obj.LSym, ptr, val, mem, sp, sb *Va
// issue call
call := b.NewValue0A(pos, OpStaticCall, types.TypeResultMem, StaticAuxCall(fn, b.Func.ABIDefault.ABIAnalyzeTypes(nil, argTypes, nil)))
call.AddArgs(wbargs...)
- call.AuxInt = off - config.ctxt.FixedFrameSize()
+ call.AuxInt = off - config.ctxt.Arch.FixedFrameSize
return b.NewValue1I(pos, OpSelectN, types.TypeMem, 0, call)
}
@@ -629,7 +629,7 @@ func IsNewObject(v *Value) (mem *Value, ok bool) {
if v.Args[0].Args[0].Op != OpSP {
return nil, false
}
- if v.Args[0].AuxInt != c.ctxt.FixedFrameSize()+c.RegSize { // offset of return value
+ if v.Args[0].AuxInt != c.ctxt.Arch.FixedFrameSize+c.RegSize { // offset of return value
return nil, false
}
return mem, true
diff --git a/src/cmd/compile/internal/ssagen/pgen.go b/src/cmd/compile/internal/ssagen/pgen.go
index 86d40e239d6..825b32aa808 100644
--- a/src/cmd/compile/internal/ssagen/pgen.go
+++ b/src/cmd/compile/internal/ssagen/pgen.go
@@ -225,13 +225,13 @@ func StackOffset(slot ssa.LocalSlot) int32 {
switch n.Class {
case ir.PPARAM, ir.PPARAMOUT:
if !n.IsOutputParamInRegisters() {
- off = n.FrameOffset() + base.Ctxt.FixedFrameSize()
+ off = n.FrameOffset() + base.Ctxt.Arch.FixedFrameSize
break
}
fallthrough // PPARAMOUT in registers allocates like an AUTO
case ir.PAUTO:
off = n.FrameOffset()
- if base.Ctxt.FixedFrameSize() == 0 {
+ if base.Ctxt.Arch.FixedFrameSize == 0 {
off -= int64(types.PtrSize)
}
if buildcfg.FramePointerEnabled {
diff --git a/src/cmd/compile/internal/ssagen/ssa.go b/src/cmd/compile/internal/ssagen/ssa.go
index 883772b341c..adb95445c43 100644
--- a/src/cmd/compile/internal/ssagen/ssa.go
+++ b/src/cmd/compile/internal/ssagen/ssa.go
@@ -286,10 +286,10 @@ func dvarint(x *obj.LSym, off int, v int64) int {
// for stack variables are specified as the number of bytes below varp (pointer to the
// top of the local variables) for their starting address. The format is:
//
-// - Offset of the deferBits variable
-// - Number of defers in the function
-// - Information about each defer call, in reverse order of appearance in the function:
-// - Offset of the closure value to call
+// - Offset of the deferBits variable
+// - Number of defers in the function
+// - Information about each defer call, in reverse order of appearance in the function:
+// - Offset of the closure value to call
func (s *state) emitOpenDeferInfo() {
x := base.Ctxt.Lookup(s.curfn.LSym.Name + ".opendefer")
x.Set(obj.AttrContentAddressable, true)
@@ -1861,6 +1861,84 @@ func (s *state) stmt(n ir.Node) {
}
s.startBlock(bEnd)
+ case ir.OJUMPTABLE:
+ n := n.(*ir.JumpTableStmt)
+
+ // Make blocks we'll need.
+ jt := s.f.NewBlock(ssa.BlockJumpTable)
+ bEnd := s.f.NewBlock(ssa.BlockPlain)
+
+ // The only thing that needs evaluating is the index we're looking up.
+ idx := s.expr(n.Idx)
+ unsigned := idx.Type.IsUnsigned()
+
+ // Extend so we can do everything in uintptr arithmetic.
+ t := types.Types[types.TUINTPTR]
+ idx = s.conv(nil, idx, idx.Type, t)
+
+ // The ending condition for the current block decides whether we'll use
+ // the jump table at all.
+ // We check that min <= idx <= max and jump around the jump table
+ // if that test fails.
+ // We implement min <= idx <= max with 0 <= idx-min <= max-min, because
+ // we'll need idx-min anyway as the control value for the jump table.
+ var min, max uint64
+ if unsigned {
+ min, _ = constant.Uint64Val(n.Cases[0])
+ max, _ = constant.Uint64Val(n.Cases[len(n.Cases)-1])
+ } else {
+ mn, _ := constant.Int64Val(n.Cases[0])
+ mx, _ := constant.Int64Val(n.Cases[len(n.Cases)-1])
+ min = uint64(mn)
+ max = uint64(mx)
+ }
+ // Compare idx-min with max-min, to see if we can use the jump table.
+ idx = s.newValue2(s.ssaOp(ir.OSUB, t), t, idx, s.uintptrConstant(min))
+ width := s.uintptrConstant(max - min)
+ cmp := s.newValue2(s.ssaOp(ir.OLE, t), types.Types[types.TBOOL], idx, width)
+ b := s.endBlock()
+ b.Kind = ssa.BlockIf
+ b.SetControl(cmp)
+ b.AddEdgeTo(jt) // in range - use jump table
+ b.AddEdgeTo(bEnd) // out of range - no case in the jump table will trigger
+ b.Likely = ssa.BranchLikely // TODO: assumes missing the table entirely is unlikely. True?
+
+ // Build jump table block.
+ s.startBlock(jt)
+ jt.Pos = n.Pos()
+ if base.Flag.Cfg.SpectreIndex {
+ idx = s.newValue2(ssa.OpSpectreSliceIndex, t, idx, width)
+ }
+ jt.SetControl(idx)
+
+ // Figure out where we should go for each index in the table.
+ table := make([]*ssa.Block, max-min+1)
+ for i := range table {
+ table[i] = bEnd // default target
+ }
+ for i := range n.Targets {
+ c := n.Cases[i]
+ lab := s.label(n.Targets[i])
+ if lab.target == nil {
+ lab.target = s.f.NewBlock(ssa.BlockPlain)
+ }
+ var val uint64
+ if unsigned {
+ val, _ = constant.Uint64Val(c)
+ } else {
+ vl, _ := constant.Int64Val(c)
+ val = uint64(vl)
+ }
+ // Overwrite the default target.
+ table[val-min] = lab.target
+ }
+ for _, t := range table {
+ jt.AddEdgeTo(t)
+ }
+ s.endBlock()
+
+ s.startBlock(bEnd)
+
case ir.OVARDEF:
n := n.(*ir.UnaryExpr)
if !s.canSSA(n.X) {
@@ -2351,6 +2429,13 @@ func (s *state) ssaShiftOp(op ir.Op, t *types.Type, u *types.Type) ssa.Op {
return x
}
+func (s *state) uintptrConstant(v uint64) *ssa.Value {
+ if s.config.PtrSize == 4 {
+ return s.newValue0I(ssa.OpConst32, types.Types[types.TUINTPTR], int64(v))
+ }
+ return s.newValue0I(ssa.OpConst64, types.Types[types.TUINTPTR], int64(v))
+}
+
func (s *state) conv(n ir.Node, v *ssa.Value, ft, tt *types.Type) *ssa.Value {
if ft.IsBoolean() && tt.IsKind(types.TUINT8) {
// Bool -> uint8 is generated internally when indexing into runtime.staticbyte.
@@ -5017,7 +5102,7 @@ func (s *state) call(n *ir.CallExpr, k callKind, returnResultAddr bool) *ssa.Val
} else {
// Store arguments to stack, including defer/go arguments and receiver for method calls.
// These are written in SP-offset order.
- argStart := base.Ctxt.FixedFrameSize()
+ argStart := base.Ctxt.Arch.FixedFrameSize
// Defer/go args.
if k != callNormal && k != callTail {
// Write closure (arg to newproc/deferproc).
@@ -5521,7 +5606,7 @@ func (s *state) intDivide(n ir.Node, a, b *ssa.Value) *ssa.Value {
func (s *state) rtcall(fn *obj.LSym, returns bool, results []*types.Type, args ...*ssa.Value) []*ssa.Value {
s.prevCall = nil
// Write args to the stack
- off := base.Ctxt.FixedFrameSize()
+ off := base.Ctxt.Arch.FixedFrameSize
var callArgs []*ssa.Value
var callArgTypes []*types.Type
@@ -5535,13 +5620,6 @@ func (s *state) rtcall(fn *obj.LSym, returns bool, results []*types.Type, args .
}
off = types.Rnd(off, int64(types.RegSize))
- // Accumulate results types and offsets
- offR := off
- for _, t := range results {
- offR = types.Rnd(offR, t.Alignment())
- offR += t.Size()
- }
-
// Issue call
var call *ssa.Value
aux := ssa.StaticAuxCall(fn, s.f.ABIDefault.ABIAnalyzeTypes(nil, callArgTypes, results))
@@ -5555,7 +5633,7 @@ func (s *state) rtcall(fn *obj.LSym, returns bool, results []*types.Type, args .
b := s.endBlock()
b.Kind = ssa.BlockExit
b.SetControl(call)
- call.AuxInt = off - base.Ctxt.FixedFrameSize()
+ call.AuxInt = off - base.Ctxt.Arch.FixedFrameSize
if len(results) > 0 {
s.Fatalf("panic call can't have results")
}
@@ -6447,6 +6525,9 @@ type State struct {
// and where they would like to go.
Branches []Branch
+ // JumpTables remembers all the jump tables we've seen.
+ JumpTables []*ssa.Block
+
// bstart remembers where each block starts (indexed by block ID)
bstart []*obj.Prog
@@ -7059,6 +7140,20 @@ func genssa(f *ssa.Func, pp *objw.Progs) {
}
+ // Resolve jump table destinations.
+ for _, jt := range s.JumpTables {
+ // Convert from *Block targets to *Prog targets.
+ targets := make([]*obj.Prog, len(jt.Succs))
+ for i, e := range jt.Succs {
+ targets[i] = s.bstart[e.Block().ID]
+ }
+ // Add to list of jump tables to be resolved at assembly time.
+ // The assembler converts from *Prog entries to absolute addresses
+ // once it knows instruction byte offsets.
+ fi := pp.CurFunc.LSym.Func()
+ fi.JumpTables = append(fi.JumpTables, obj.JumpTable{Sym: jt.Aux.(*obj.LSym), Targets: targets})
+ }
+
if e.log { // spew to stdout
filename := ""
for p := pp.Text; p != nil; p = p.Link {
@@ -7712,6 +7807,10 @@ func (e *ssafn) MyImportPath() string {
return base.Ctxt.Pkgpath
}
+func (e *ssafn) LSym() string {
+ return e.curfn.LSym.Name
+}
+
func clobberBase(n ir.Node) ir.Node {
if n.Op() == ir.ODOT {
n := n.(*ir.SelectorExpr)
diff --git a/src/cmd/compile/internal/syntax/branches.go b/src/cmd/compile/internal/syntax/branches.go
index 56e97c71d8a..60790974265 100644
--- a/src/cmd/compile/internal/syntax/branches.go
+++ b/src/cmd/compile/internal/syntax/branches.go
@@ -11,10 +11,10 @@ import "fmt"
// checkBranches checks correct use of labels and branch
// statements (break, continue, goto) in a function body.
// It catches:
-// - misplaced breaks and continues
-// - bad labeled breaks and continues
-// - invalid, unused, duplicate, and missing labels
-// - gotos jumping over variable declarations and into blocks
+// - misplaced breaks and continues
+// - bad labeled breaks and continues
+// - invalid, unused, duplicate, and missing labels
+// - gotos jumping over variable declarations and into blocks
func checkBranches(body *BlockStmt, errh ErrorHandler) {
if body == nil {
return
diff --git a/src/cmd/compile/internal/test/float_test.go b/src/cmd/compile/internal/test/float_test.go
index 884a983bdd7..c736f970f99 100644
--- a/src/cmd/compile/internal/test/float_test.go
+++ b/src/cmd/compile/internal/test/float_test.go
@@ -170,6 +170,7 @@ func cvt8(a float32) int32 {
}
// make sure to cover int, uint cases (issue #16738)
+//
//go:noinline
func cvt9(a float64) int {
return int(a)
diff --git a/src/cmd/compile/internal/test/inl_test.go b/src/cmd/compile/internal/test/inl_test.go
index b10d37a17cf..211068e1dc3 100644
--- a/src/cmd/compile/internal/test/inl_test.go
+++ b/src/cmd/compile/internal/test/inl_test.go
@@ -136,6 +136,7 @@ func TestIntendedInlining(t *testing.T) {
"Value.CanSet",
"Value.CanInterface",
"Value.IsValid",
+ "Value.MapRange",
"Value.pointer",
"add",
"align",
diff --git a/src/cmd/compile/internal/test/shift_test.go b/src/cmd/compile/internal/test/shift_test.go
index ea88f0a70ae..58c8dde1a01 100644
--- a/src/cmd/compile/internal/test/shift_test.go
+++ b/src/cmd/compile/internal/test/shift_test.go
@@ -1029,3 +1029,13 @@ func TestShiftGeneric(t *testing.T) {
}
}
}
+
+var shiftSink64 int64
+
+func BenchmarkShiftArithmeticRight(b *testing.B) {
+ x := shiftSink64
+ for i := 0; i < b.N; i++ {
+ x = x >> (i & 63)
+ }
+ shiftSink64 = x
+}
diff --git a/src/cmd/compile/internal/test/switch_test.go b/src/cmd/compile/internal/test/switch_test.go
new file mode 100644
index 00000000000..30dee6257e3
--- /dev/null
+++ b/src/cmd/compile/internal/test/switch_test.go
@@ -0,0 +1,137 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package test
+
+import (
+ "math/bits"
+ "testing"
+)
+
+func BenchmarkSwitch8Predictable(b *testing.B) {
+ benchmarkSwitch8(b, true)
+}
+func BenchmarkSwitch8Unpredictable(b *testing.B) {
+ benchmarkSwitch8(b, false)
+}
+func benchmarkSwitch8(b *testing.B, predictable bool) {
+ n := 0
+ rng := newRNG()
+ for i := 0; i < b.N; i++ {
+ rng = rng.next(predictable)
+ switch rng.value() & 7 {
+ case 0:
+ n += 1
+ case 1:
+ n += 2
+ case 2:
+ n += 3
+ case 3:
+ n += 4
+ case 4:
+ n += 5
+ case 5:
+ n += 6
+ case 6:
+ n += 7
+ case 7:
+ n += 8
+ }
+ }
+ sink = n
+}
+
+func BenchmarkSwitch32Predictable(b *testing.B) {
+ benchmarkSwitch32(b, true)
+}
+func BenchmarkSwitch32Unpredictable(b *testing.B) {
+ benchmarkSwitch32(b, false)
+}
+func benchmarkSwitch32(b *testing.B, predictable bool) {
+ n := 0
+ rng := newRNG()
+ for i := 0; i < b.N; i++ {
+ rng = rng.next(predictable)
+ switch rng.value() & 31 {
+ case 0, 1, 2:
+ n += 1
+ case 4, 5, 6:
+ n += 2
+ case 8, 9, 10:
+ n += 3
+ case 12, 13, 14:
+ n += 4
+ case 16, 17, 18:
+ n += 5
+ case 20, 21, 22:
+ n += 6
+ case 24, 25, 26:
+ n += 7
+ case 28, 29, 30:
+ n += 8
+ default:
+ n += 9
+ }
+ }
+ sink = n
+}
+
+func BenchmarkSwitchStringPredictable(b *testing.B) {
+ benchmarkSwitchString(b, true)
+}
+func BenchmarkSwitchStringUnpredictable(b *testing.B) {
+ benchmarkSwitchString(b, false)
+}
+func benchmarkSwitchString(b *testing.B, predictable bool) {
+ a := []string{
+ "foo",
+ "foo1",
+ "foo22",
+ "foo333",
+ "foo4444",
+ "foo55555",
+ "foo666666",
+ "foo7777777",
+ }
+ n := 0
+ rng := newRNG()
+ for i := 0; i < b.N; i++ {
+ rng = rng.next(predictable)
+ switch a[rng.value()&7] {
+ case "foo":
+ n += 1
+ case "foo1":
+ n += 2
+ case "foo22":
+ n += 3
+ case "foo333":
+ n += 4
+ case "foo4444":
+ n += 5
+ case "foo55555":
+ n += 6
+ case "foo666666":
+ n += 7
+ case "foo7777777":
+ n += 8
+ }
+ }
+ sink = n
+}
+
+// A simple random number generator used to make switches conditionally predictable.
+type rng uint64
+
+func newRNG() rng {
+ return 1
+}
+func (r rng) next(predictable bool) rng {
+ if predictable {
+ return r + 1
+ }
+ return rng(bits.RotateLeft64(uint64(r), 13) * 0x3c374d)
+}
+func (r rng) value() uint64 {
+ return uint64(r)
+}
diff --git a/src/cmd/compile/internal/test/testdata/addressed_test.go b/src/cmd/compile/internal/test/testdata/addressed_test.go
index cdabf978f08..4cc9ac4d5b2 100644
--- a/src/cmd/compile/internal/test/testdata/addressed_test.go
+++ b/src/cmd/compile/internal/test/testdata/addressed_test.go
@@ -145,6 +145,7 @@ func (v V) val() int64 {
// and y.val() should be equal to which and y.p.val() should
// be equal to z.val(). Also, x(.p)**8 == x; that is, the
// autos are all linked into a ring.
+//
//go:noinline
func (v V) autos_ssa(which, w1, x1, w2, x2 int64) (y, z V) {
fill_ssa(v.w, v.x, &v, v.p) // gratuitous no-op to force addressing
@@ -191,6 +192,7 @@ func (v V) autos_ssa(which, w1, x1, w2, x2 int64) (y, z V) {
// gets is an address-mentioning way of implementing
// structure assignment.
+//
//go:noinline
func (to *V) gets(from *V) {
*to = *from
@@ -198,12 +200,14 @@ func (to *V) gets(from *V) {
// gets is an address-and-interface-mentioning way of
// implementing structure assignment.
+//
//go:noinline
func (to *V) getsI(from interface{}) {
*to = *from.(*V)
}
// fill_ssa initializes r with V{w:w, x:x, p:p}
+//
//go:noinline
func fill_ssa(w, x int64, r, p *V) {
*r = V{w: w, x: x, p: p}
diff --git a/src/cmd/compile/internal/test/testdata/arith_test.go b/src/cmd/compile/internal/test/testdata/arith_test.go
index 7d54a9181d1..253142a0fbc 100644
--- a/src/cmd/compile/internal/test/testdata/arith_test.go
+++ b/src/cmd/compile/internal/test/testdata/arith_test.go
@@ -225,6 +225,7 @@ func testArithConstShift(t *testing.T) {
// overflowConstShift_ssa verifes that constant folding for shift
// doesn't wrap (i.e. x << MAX_INT << 1 doesn't get folded to x << 0).
+//
//go:noinline
func overflowConstShift64_ssa(x int64) int64 {
return x << uint64(0xffffffffffffffff) << uint64(1)
diff --git a/src/cmd/compile/internal/test/testdata/ctl_test.go b/src/cmd/compile/internal/test/testdata/ctl_test.go
index 16d571ce2cb..ff3a1609c5a 100644
--- a/src/cmd/compile/internal/test/testdata/ctl_test.go
+++ b/src/cmd/compile/internal/test/testdata/ctl_test.go
@@ -117,6 +117,7 @@ type junk struct {
// flagOverwrite_ssa is intended to reproduce an issue seen where a XOR
// was scheduled between a compare and branch, clearing flags.
+//
//go:noinline
func flagOverwrite_ssa(s *junk, c int) int {
if '0' <= c && c <= '9' {
diff --git a/src/cmd/compile/internal/test/testdata/fp_test.go b/src/cmd/compile/internal/test/testdata/fp_test.go
index 7d61a8063ee..b96ce84a6ca 100644
--- a/src/cmd/compile/internal/test/testdata/fp_test.go
+++ b/src/cmd/compile/internal/test/testdata/fp_test.go
@@ -14,6 +14,7 @@ import (
// manysub_ssa is designed to tickle bugs that depend on register
// pressure or unfriendly operand ordering in registers (and at
// least once it succeeded in this).
+//
//go:noinline
func manysub_ssa(a, b, c, d float64) (aa, ab, ac, ad, ba, bb, bc, bd, ca, cb, cc, cd, da, db, dc, dd float64) {
aa = a + 11.0 - a
@@ -37,6 +38,7 @@ func manysub_ssa(a, b, c, d float64) (aa, ab, ac, ad, ba, bb, bc, bd, ca, cb, cc
// fpspill_ssa attempts to trigger a bug where phis with floating point values
// were stored in non-fp registers causing an error in doasm.
+//
//go:noinline
func fpspill_ssa(a int) float64 {
diff --git a/src/cmd/compile/internal/test/testdata/loadstore_test.go b/src/cmd/compile/internal/test/testdata/loadstore_test.go
index 57571f5d170..052172819a7 100644
--- a/src/cmd/compile/internal/test/testdata/loadstore_test.go
+++ b/src/cmd/compile/internal/test/testdata/loadstore_test.go
@@ -73,6 +73,7 @@ var b int
// testDeadStorePanic_ssa ensures that we don't optimize away stores
// that could be read by after recover(). Modeled after fixedbugs/issue1304.
+//
//go:noinline
func testDeadStorePanic_ssa(a int) (r int) {
defer func() {
diff --git a/src/cmd/compile/internal/typecheck/builtin.go b/src/cmd/compile/internal/typecheck/builtin.go
index 67597cebb46..581928c0051 100644
--- a/src/cmd/compile/internal/typecheck/builtin.go
+++ b/src/cmd/compile/internal/typecheck/builtin.go
@@ -212,6 +212,7 @@ var runtimeDecls = [...]struct {
}
// Not inlining this function removes a significant chunk of init code.
+//
//go:noinline
func newSig(params, results []*types.Field) *types.Type {
return types.NewSignature(types.NoPkg, nil, nil, params, results)
diff --git a/src/cmd/compile/internal/typecheck/const.go b/src/cmd/compile/internal/typecheck/const.go
index 1422ab00312..a626c000be3 100644
--- a/src/cmd/compile/internal/typecheck/const.go
+++ b/src/cmd/compile/internal/typecheck/const.go
@@ -620,7 +620,8 @@ func OrigInt(n ir.Node, v int64) ir.Node {
// get the same type going out.
// force means must assign concrete (non-ideal) type.
// The results of defaultlit2 MUST be assigned back to l and r, e.g.
-// n.Left, n.Right = defaultlit2(n.Left, n.Right, force)
+//
+// n.Left, n.Right = defaultlit2(n.Left, n.Right, force)
func defaultlit2(l ir.Node, r ir.Node, force bool) (ir.Node, ir.Node) {
if l.Type() == nil || r.Type() == nil {
return l, r
diff --git a/src/cmd/compile/internal/typecheck/expr.go b/src/cmd/compile/internal/typecheck/expr.go
index e6adc05a658..f0b7b74aedd 100644
--- a/src/cmd/compile/internal/typecheck/expr.go
+++ b/src/cmd/compile/internal/typecheck/expr.go
@@ -76,8 +76,9 @@ func tcShift(n, l, r ir.Node) (ir.Node, ir.Node, *types.Type) {
// tcArith typechecks operands of a binary arithmetic expression.
// The result of tcArith MUST be assigned back to original operands,
// t is the type of the expression, and should be set by the caller. e.g:
-// n.X, n.Y, t = tcArith(n, op, n.X, n.Y)
-// n.SetType(t)
+//
+// n.X, n.Y, t = tcArith(n, op, n.X, n.Y)
+// n.SetType(t)
func tcArith(n ir.Node, op ir.Op, l, r ir.Node) (ir.Node, ir.Node, *types.Type) {
l, r = defaultlit2(l, r, false)
if l.Type() == nil || r.Type() == nil {
@@ -194,7 +195,8 @@ func tcArith(n ir.Node, op ir.Op, l, r ir.Node) (ir.Node, ir.Node, *types.Type)
}
// The result of tcCompLit MUST be assigned back to n, e.g.
-// n.Left = tcCompLit(n.Left)
+//
+// n.Left = tcCompLit(n.Left)
func tcCompLit(n *ir.CompLitExpr) (res ir.Node) {
if base.EnableTrace && base.Flag.LowerT {
defer tracePrint("tcCompLit", n)(&res)
diff --git a/src/cmd/compile/internal/typecheck/iexport.go b/src/cmd/compile/internal/typecheck/iexport.go
index 5d319eaca36..12159b71e1b 100644
--- a/src/cmd/compile/internal/typecheck/iexport.go
+++ b/src/cmd/compile/internal/typecheck/iexport.go
@@ -258,7 +258,7 @@ import (
// 1: added column details to Pos
// 2: added information for generic function/types. The export of non-generic
// functions/types remains largely backward-compatible. Breaking changes include:
-// - a 'kind' byte is added to constant values
+// - a 'kind' byte is added to constant values
const (
iexportVersionGo1_11 = 0
iexportVersionPosCol = 1
diff --git a/src/cmd/compile/internal/typecheck/mkbuiltin.go b/src/cmd/compile/internal/typecheck/mkbuiltin.go
index 6dbd1869b3e..9b27557956d 100644
--- a/src/cmd/compile/internal/typecheck/mkbuiltin.go
+++ b/src/cmd/compile/internal/typecheck/mkbuiltin.go
@@ -105,6 +105,7 @@ func mkbuiltin(w io.Writer, name string) {
fmt.Fprintln(w, `
// Not inlining this function removes a significant chunk of init code.
+//
//go:noinline
func newSig(params, results []*types.Field) *types.Type {
return types.NewSignature(types.NoPkg, nil, nil, params, results)
diff --git a/src/cmd/compile/internal/typecheck/syms.go b/src/cmd/compile/internal/typecheck/syms.go
index 6c2e84680bc..1f60f318510 100644
--- a/src/cmd/compile/internal/typecheck/syms.go
+++ b/src/cmd/compile/internal/typecheck/syms.go
@@ -24,7 +24,8 @@ func LookupRuntime(name string) *ir.Name {
// successive occurrences of the "any" placeholder in the
// type syntax expression n.Type.
// The result of SubstArgTypes MUST be assigned back to old, e.g.
-// n.Left = SubstArgTypes(n.Left, t1, t2)
+//
+// n.Left = SubstArgTypes(n.Left, t1, t2)
func SubstArgTypes(old *ir.Name, types_ ...*types.Type) *ir.Name {
for _, t := range types_ {
types.CalcSize(t)
diff --git a/src/cmd/compile/internal/typecheck/typecheck.go b/src/cmd/compile/internal/typecheck/typecheck.go
index 85de653a82a..2eb9e6d718c 100644
--- a/src/cmd/compile/internal/typecheck/typecheck.go
+++ b/src/cmd/compile/internal/typecheck/typecheck.go
@@ -240,7 +240,8 @@ func typecheckNtype(n ir.Ntype) ir.Ntype {
// typecheck type checks node n.
// The result of typecheck MUST be assigned back to n, e.g.
-// n.Left = typecheck(n.Left, top)
+//
+// n.Left = typecheck(n.Left, top)
func typecheck(n ir.Node, top int) (res ir.Node) {
// cannot type check until all the source has been parsed
if !TypecheckAllowed {
@@ -414,7 +415,8 @@ func typecheck(n ir.Node, top int) (res ir.Node) {
// but also accepts untyped numeric values representable as
// value of type int (see also checkmake for comparison).
// The result of indexlit MUST be assigned back to n, e.g.
-// n.Left = indexlit(n.Left)
+//
+// n.Left = indexlit(n.Left)
func indexlit(n ir.Node) ir.Node {
if n != nil && n.Type() != nil && n.Type().Kind() == types.TIDEAL {
return DefaultLit(n, types.Types[types.TINT])
@@ -961,7 +963,8 @@ func checksliceconst(lo ir.Node, hi ir.Node) bool {
}
// The result of implicitstar MUST be assigned back to n, e.g.
-// n.Left = implicitstar(n.Left)
+//
+// n.Left = implicitstar(n.Left)
func implicitstar(n ir.Node) ir.Node {
// insert implicit * if needed for fixed array
t := n.Type()
@@ -1607,7 +1610,8 @@ func checkassignto(src *types.Type, dst ir.Node) {
}
// The result of stringtoruneslit MUST be assigned back to n, e.g.
-// n.Left = stringtoruneslit(n.Left)
+//
+// n.Left = stringtoruneslit(n.Left)
func stringtoruneslit(n *ir.ConvExpr) ir.Node {
if n.X.Op() != ir.OLITERAL || n.X.Val().Kind() != constant.String {
base.Fatalf("stringtoarraylit %v", n)
diff --git a/src/cmd/compile/internal/types/type.go b/src/cmd/compile/internal/types/type.go
index 147194c3697..987352babc7 100644
--- a/src/cmd/compile/internal/types/type.go
+++ b/src/cmd/compile/internal/types/type.go
@@ -492,9 +492,9 @@ type Slice struct {
// A Field is a (Sym, Type) pairing along with some other information, and,
// depending on the context, is used to represent:
-// - a field in a struct
-// - a method in an interface or associated with a named type
-// - a function parameter
+// - a field in a struct
+// - a method in an interface or associated with a named type
+// - a function parameter
type Field struct {
flags bitset8
@@ -1121,9 +1121,10 @@ func (t *Type) SimpleString() string {
}
// Cmp is a comparison between values a and b.
-// -1 if a < b
-// 0 if a == b
-// 1 if a > b
+//
+// -1 if a < b
+// 0 if a == b
+// 1 if a > b
type Cmp int8
const (
diff --git a/src/cmd/compile/internal/types2/api.go b/src/cmd/compile/internal/types2/api.go
index 34bb29cadcc..54cddaee28d 100644
--- a/src/cmd/compile/internal/types2/api.go
+++ b/src/cmd/compile/internal/types2/api.go
@@ -21,7 +21,6 @@
// Type inference computes the type (Type) of every expression (syntax.Expr)
// and checks for compliance with the language specification.
// Use Info.Types[expr].Type for the results of type inference.
-//
package types2
import (
diff --git a/src/cmd/compile/internal/types2/api_test.go b/src/cmd/compile/internal/types2/api_test.go
index 528beaaceab..fde7291b03e 100644
--- a/src/cmd/compile/internal/types2/api_test.go
+++ b/src/cmd/compile/internal/types2/api_test.go
@@ -311,6 +311,18 @@ func TestTypesInfo(t *testing.T) {
`[][]struct{}`,
},
+ // issue 47243
+ {`package issue47243_a; var x int32; var _ = x << 3`, `3`, `untyped int`},
+ {`package issue47243_b; var x int32; var _ = x << 3.`, `3.`, `untyped float`},
+ {`package issue47243_c; var x int32; var _ = 1 << x`, `1 << x`, `int`},
+ {`package issue47243_d; var x int32; var _ = 1 << x`, `1`, `int`},
+ {`package issue47243_e; var x int32; var _ = 1 << 2`, `1`, `untyped int`},
+ {`package issue47243_f; var x int32; var _ = 1 << 2`, `2`, `untyped int`},
+ {`package issue47243_g; var x int32; var _ = int(1) << 2`, `2`, `untyped int`},
+ {`package issue47243_h; var x int32; var _ = 1 << (2 << x)`, `1`, `int`},
+ {`package issue47243_i; var x int32; var _ = 1 << (2 << x)`, `(2 << x)`, `untyped int`},
+ {`package issue47243_j; var x int32; var _ = 1 << (2 << x)`, `2`, `untyped int`},
+
// tests for broken code that doesn't parse or type-check
{brokenPkg + `x0; func _() { var x struct {f string}; x.f := 0 }`, `x.f`, `string`},
{brokenPkg + `x1; func _() { var z string; type x struct {f string}; y := &x{q: z}}`, `z`, `string`},
diff --git a/src/cmd/compile/internal/types2/check_test.go b/src/cmd/compile/internal/types2/check_test.go
index ec242c5e222..2e1ae0d2bee 100644
--- a/src/cmd/compile/internal/types2/check_test.go
+++ b/src/cmd/compile/internal/types2/check_test.go
@@ -263,7 +263,7 @@ func testFiles(t *testing.T, filenames []string, colDelta uint, manual bool) {
// (and a separating "--"). For instance, to test the package made
// of the files foo.go and bar.go, use:
//
-// go test -run Manual -- foo.go bar.go
+// go test -run Manual -- foo.go bar.go
//
// If no source arguments are provided, the file testdata/manual.go
// is used instead.
diff --git a/src/cmd/compile/internal/types2/expr.go b/src/cmd/compile/internal/types2/expr.go
index 1ecb4ff54bd..e0c22f5b038 100644
--- a/src/cmd/compile/internal/types2/expr.go
+++ b/src/cmd/compile/internal/types2/expr.go
@@ -954,32 +954,48 @@ func (check *Checker) shift(x, y *operand, e syntax.Expr, op syntax.Operator) {
// spec: "The right operand in a shift expression must have integer type
// or be an untyped constant representable by a value of type uint."
- // Provide a good error message for negative shift counts.
+ // Check that constants are representable by uint, but do not convert them
+ // (see also issue #47243).
if y.mode == constant_ {
+ // Provide a good error message for negative shift counts.
yval := constant.ToInt(y.val) // consider -1, 1.0, but not -1.1
if yval.Kind() == constant.Int && constant.Sign(yval) < 0 {
check.errorf(y, invalidOp+"negative shift count %s", y)
x.mode = invalid
return
}
- }
- // Caution: Check for isUntyped first because isInteger includes untyped
- // integers (was bug #43697).
- if isUntyped(y.typ) {
- check.convertUntyped(y, Typ[Uint])
- if y.mode == invalid {
+ if isUntyped(y.typ) {
+ // Caution: Check for representability here, rather than in the switch
+ // below, because isInteger includes untyped integers (was bug #43697).
+ check.representable(y, Typ[Uint])
+ if y.mode == invalid {
+ x.mode = invalid
+ return
+ }
+ }
+ } else {
+ // Check that RHS is otherwise at least of integer type.
+ switch {
+ case allInteger(y.typ):
+ if !allUnsigned(y.typ) && !check.allowVersion(check.pkg, 1, 13) {
+ check.errorf(y, invalidOp+"signed shift count %s requires go1.13 or later", y)
+ x.mode = invalid
+ return
+ }
+ case isUntyped(y.typ):
+ // This is incorrect, but preserves pre-existing behavior.
+ // See also bug #47410.
+ check.convertUntyped(y, Typ[Uint])
+ if y.mode == invalid {
+ x.mode = invalid
+ return
+ }
+ default:
+ check.errorf(y, invalidOp+"shift count %s must be integer", y)
x.mode = invalid
return
}
- } else if !allInteger(y.typ) {
- check.errorf(y, invalidOp+"shift count %s must be integer", y)
- x.mode = invalid
- return
- } else if !allUnsigned(y.typ) && !check.allowVersion(check.pkg, 1, 13) {
- check.versionErrorf(y, "go1.13", invalidOp+"signed shift count %s", y)
- x.mode = invalid
- return
}
if x.mode == constant_ {
diff --git a/src/cmd/compile/internal/types2/infer.go b/src/cmd/compile/internal/types2/infer.go
index 9f7e593eeb3..9e77d67a7de 100644
--- a/src/cmd/compile/internal/types2/infer.go
+++ b/src/cmd/compile/internal/types2/infer.go
@@ -23,10 +23,10 @@ const useConstraintTypeInference = true
//
// Inference proceeds as follows. Starting with given type arguments:
//
-// 1) apply FTI (function type inference) with typed arguments,
-// 2) apply CTI (constraint type inference),
-// 3) apply FTI with untyped function arguments,
-// 4) apply CTI.
+// 1. apply FTI (function type inference) with typed arguments,
+// 2. apply CTI (constraint type inference),
+// 3. apply FTI with untyped function arguments,
+// 4. apply CTI.
//
// The process stops as soon as all type arguments are known or an error occurs.
func (check *Checker) infer(pos syntax.Pos, tparams []*TypeParam, targs []Type, params *Tuple, args []*operand) (result []Type) {
diff --git a/src/cmd/compile/internal/types2/lookup.go b/src/cmd/compile/internal/types2/lookup.go
index 93defd66185..684bbf7a8bc 100644
--- a/src/cmd/compile/internal/types2/lookup.go
+++ b/src/cmd/compile/internal/types2/lookup.go
@@ -25,9 +25,9 @@ import (
// The last index entry is the field or method index in the (possibly embedded)
// type where the entry was found, either:
//
-// 1) the list of declared methods of a named type; or
-// 2) the list of all methods (method set) of an interface type; or
-// 3) the list of fields of a struct type.
+// 1. the list of declared methods of a named type; or
+// 2. the list of all methods (method set) of an interface type; or
+// 3. the list of fields of a struct type.
//
// The earlier index entries are the indices of the embedded struct fields
// traversed to get to the found entry, starting at depth 0.
@@ -35,12 +35,12 @@ import (
// If no entry is found, a nil object is returned. In this case, the returned
// index and indirect values have the following meaning:
//
-// - If index != nil, the index sequence points to an ambiguous entry
-// (the same name appeared more than once at the same embedding level).
+// - If index != nil, the index sequence points to an ambiguous entry
+// (the same name appeared more than once at the same embedding level).
//
-// - If indirect is set, a method with a pointer receiver type was found
-// but there was no pointer on the path from the actual receiver type to
-// the method's formal receiver base type, nor was the receiver addressable.
+// - If indirect is set, a method with a pointer receiver type was found
+// but there was no pointer on the path from the actual receiver type to
+// the method's formal receiver base type, nor was the receiver addressable.
func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) {
if T == nil {
panic("LookupFieldOrMethod on nil type")
diff --git a/src/cmd/compile/internal/types2/selection.go b/src/cmd/compile/internal/types2/selection.go
index ee632144071..c820a29fad2 100644
--- a/src/cmd/compile/internal/types2/selection.go
+++ b/src/cmd/compile/internal/types2/selection.go
@@ -92,9 +92,9 @@ func (s *Selection) Type() Type {
// The last index entry is the field or method index of the type declaring f;
// either:
//
-// 1) the list of declared methods of a named type; or
-// 2) the list of methods of an interface type; or
-// 3) the list of fields of a struct type.
+// 1. the list of declared methods of a named type; or
+// 2. the list of methods of an interface type; or
+// 3. the list of fields of a struct type.
//
// The earlier index entries are the indices of the embedded fields implicitly
// traversed to get from (the type of) x to f, starting at embedding depth 0.
@@ -111,6 +111,7 @@ func (s *Selection) String() string { return SelectionString(s, nil) }
// package-level objects, and may be nil.
//
// Examples:
+//
// "field (T) f int"
// "method (T) f(X) Y"
// "method expr (T) f(X) Y"
diff --git a/src/cmd/compile/internal/types2/sizes.go b/src/cmd/compile/internal/types2/sizes.go
index 7a34b6474cd..f530849a9d9 100644
--- a/src/cmd/compile/internal/types2/sizes.go
+++ b/src/cmd/compile/internal/types2/sizes.go
@@ -24,19 +24,19 @@ type Sizes interface {
// StdSizes is a convenience type for creating commonly used Sizes.
// It makes the following simplifying assumptions:
//
-// - The size of explicitly sized basic types (int16, etc.) is the
-// specified size.
-// - The size of strings and interfaces is 2*WordSize.
-// - The size of slices is 3*WordSize.
-// - The size of an array of n elements corresponds to the size of
-// a struct of n consecutive fields of the array's element type.
-// - The size of a struct is the offset of the last field plus that
-// field's size. As with all element types, if the struct is used
-// in an array its size must first be aligned to a multiple of the
-// struct's alignment.
-// - All other types have size WordSize.
-// - Arrays and structs are aligned per spec definition; all other
-// types are naturally aligned with a maximum alignment MaxAlign.
+// - The size of explicitly sized basic types (int16, etc.) is the
+// specified size.
+// - The size of strings and interfaces is 2*WordSize.
+// - The size of slices is 3*WordSize.
+// - The size of an array of n elements corresponds to the size of
+// a struct of n consecutive fields of the array's element type.
+// - The size of a struct is the offset of the last field plus that
+// field's size. As with all element types, if the struct is used
+// in an array its size must first be aligned to a multiple of the
+// struct's alignment.
+// - All other types have size WordSize.
+// - Arrays and structs are aligned per spec definition; all other
+// types are naturally aligned with a maximum alignment MaxAlign.
//
// *StdSizes implements Sizes.
type StdSizes struct {
diff --git a/src/cmd/compile/internal/types2/testdata/fixedbugs/issue52031.go b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue52031.go
new file mode 100644
index 00000000000..448a550b250
--- /dev/null
+++ b/src/cmd/compile/internal/types2/testdata/fixedbugs/issue52031.go
@@ -0,0 +1,33 @@
+// -lang=go1.12
+
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+type resultFlags uint
+
+// Example from #52031.
+//
+// The following shifts should not produce errors on Go < 1.13, as their
+// untyped constant operands are representable by type uint.
+const (
+ _ resultFlags = (1 << iota) / 2
+
+ reportEqual
+ reportUnequal
+ reportByIgnore
+ reportByMethod
+ reportByFunc
+ reportByCycle
+)
+
+// Invalid cases.
+var x int = 1
+var _ = (8 << x /* ERROR "signed shift count .* requires go1.13 or later" */)
+
+const _ = (1 << 1.2 /* ERROR "truncated to uint" */)
+
+var y float64
+var _ = (1 << y /* ERROR "must be integer" */)
diff --git a/src/cmd/compile/internal/types2/typeterm.go b/src/cmd/compile/internal/types2/typeterm.go
index 3d82a37ab83..97791324e1e 100644
--- a/src/cmd/compile/internal/types2/typeterm.go
+++ b/src/cmd/compile/internal/types2/typeterm.go
@@ -6,10 +6,10 @@ package types2
// A term describes elementary type sets:
//
-// ∅: (*term)(nil) == ∅ // set of no types (empty set)
-// 𝓤: &term{} == 𝓤 // set of all types (𝓤niverse)
-// T: &term{false, T} == {T} // set of type T
-// ~t: &term{true, t} == {t' | under(t') == t} // set of types with underlying type t
+// ∅: (*term)(nil) == ∅ // set of no types (empty set)
+// 𝓤: &term{} == 𝓤 // set of all types (𝓤niverse)
+// T: &term{false, T} == {T} // set of type T
+// ~t: &term{true, t} == {t' | under(t') == t} // set of types with underlying type t
type term struct {
tilde bool // valid if typ != nil
typ Type
diff --git a/src/cmd/compile/internal/walk/assign.go b/src/cmd/compile/internal/walk/assign.go
index 9b09e097fac..c44d934f21e 100644
--- a/src/cmd/compile/internal/walk/assign.go
+++ b/src/cmd/compile/internal/walk/assign.go
@@ -242,6 +242,7 @@ func walkReturn(n *ir.ReturnStmt) ir.Node {
// check assign type list to
// an expression list. called in
+//
// expr-list = func()
func ascompatet(nl ir.Nodes, nr *types.Type) []ir.Node {
if len(nl) != nr.NumFields() {
@@ -273,6 +274,7 @@ func ascompatet(nl ir.Nodes, nr *types.Type) []ir.Node {
// check assign expression list to
// an expression list. called in
+//
// expr-list = expr-list
func ascompatee(op ir.Op, nl, nr []ir.Node) []ir.Node {
// cannot happen: should have been rejected during type checking
@@ -455,17 +457,18 @@ func readsMemory(n ir.Node) bool {
}
// expand append(l1, l2...) to
-// init {
-// s := l1
-// n := len(s) + len(l2)
-// // Compare as uint so growslice can panic on overflow.
-// if uint(n) > uint(cap(s)) {
-// s = growslice(s, n)
-// }
-// s = s[:n]
-// memmove(&s[len(l1)], &l2[0], len(l2)*sizeof(T))
-// }
-// s
+//
+// init {
+// s := l1
+// n := len(s) + len(l2)
+// // Compare as uint so growslice can panic on overflow.
+// if uint(n) > uint(cap(s)) {
+// s = growslice(s, n)
+// }
+// s = s[:n]
+// memmove(&s[len(l1)], &l2[0], len(l2)*sizeof(T))
+// }
+// s
//
// l2 is allowed to be a string.
func appendSlice(n *ir.CallExpr, init *ir.Nodes) ir.Node {
@@ -597,32 +600,33 @@ func isAppendOfMake(n ir.Node) bool {
}
// extendSlice rewrites append(l1, make([]T, l2)...) to
-// init {
-// if l2 >= 0 { // Empty if block here for more meaningful node.SetLikely(true)
-// } else {
-// panicmakeslicelen()
-// }
-// s := l1
-// n := len(s) + l2
-// // Compare n and s as uint so growslice can panic on overflow of len(s) + l2.
-// // cap is a positive int and n can become negative when len(s) + l2
-// // overflows int. Interpreting n when negative as uint makes it larger
-// // than cap(s). growslice will check the int n arg and panic if n is
-// // negative. This prevents the overflow from being undetected.
-// if uint(n) > uint(cap(s)) {
-// s = growslice(T, s, n)
-// }
-// s = s[:n]
-// lptr := &l1[0]
-// sptr := &s[0]
-// if lptr == sptr || !T.HasPointers() {
-// // growslice did not clear the whole underlying array (or did not get called)
-// hp := &s[len(l1)]
-// hn := l2 * sizeof(T)
-// memclr(hp, hn)
-// }
-// }
-// s
+//
+// init {
+// if l2 >= 0 { // Empty if block here for more meaningful node.SetLikely(true)
+// } else {
+// panicmakeslicelen()
+// }
+// s := l1
+// n := len(s) + l2
+// // Compare n and s as uint so growslice can panic on overflow of len(s) + l2.
+// // cap is a positive int and n can become negative when len(s) + l2
+// // overflows int. Interpreting n when negative as uint makes it larger
+// // than cap(s). growslice will check the int n arg and panic if n is
+// // negative. This prevents the overflow from being undetected.
+// if uint(n) > uint(cap(s)) {
+// s = growslice(T, s, n)
+// }
+// s = s[:n]
+// lptr := &l1[0]
+// sptr := &s[0]
+// if lptr == sptr || !T.HasPointers() {
+// // growslice did not clear the whole underlying array (or did not get called)
+// hp := &s[len(l1)]
+// hn := l2 * sizeof(T)
+// memclr(hp, hn)
+// }
+// }
+// s
func extendSlice(n *ir.CallExpr, init *ir.Nodes) ir.Node {
// isAppendOfMake made sure all possible positive values of l2 fit into an uint.
// The case of l2 overflow when converting from e.g. uint to int is handled by an explicit
diff --git a/src/cmd/compile/internal/walk/builtin.go b/src/cmd/compile/internal/walk/builtin.go
index 7ec5494d996..d7b553ed0c4 100644
--- a/src/cmd/compile/internal/walk/builtin.go
+++ b/src/cmd/compile/internal/walk/builtin.go
@@ -26,19 +26,19 @@ import (
//
// For race detector, expand append(src, a [, b]* ) to
//
-// init {
-// s := src
-// const argc = len(args) - 1
-// if cap(s) - len(s) < argc {
-// s = growslice(s, len(s)+argc)
-// }
-// n := len(s)
-// s = s[:n+argc]
-// s[n] = a
-// s[n+1] = b
-// ...
-// }
-// s
+// init {
+// s := src
+// const argc = len(args) - 1
+// if cap(s) - len(s) < argc {
+// s = growslice(s, len(s)+argc)
+// }
+// n := len(s)
+// s = s[:n+argc]
+// s[n] = a
+// s[n+1] = b
+// ...
+// }
+// s
func walkAppend(n *ir.CallExpr, init *ir.Nodes, dst ir.Node) ir.Node {
if !ir.SameSafeExpr(dst, n.Args[0]) {
n.Args[0] = safeExpr(n.Args[0], init)
diff --git a/src/cmd/compile/internal/walk/compare.go b/src/cmd/compile/internal/walk/compare.go
index 625e2160503..993f1392aa1 100644
--- a/src/cmd/compile/internal/walk/compare.go
+++ b/src/cmd/compile/internal/walk/compare.go
@@ -16,7 +16,8 @@ import (
)
// The result of walkCompare MUST be assigned back to n, e.g.
-// n.Left = walkCompare(n.Left, init)
+//
+// n.Left = walkCompare(n.Left, init)
func walkCompare(n *ir.BinaryExpr, init *ir.Nodes) ir.Node {
if n.X.Type().IsInterface() && n.Y.Type().IsInterface() && n.X.Op() != ir.ONIL && n.Y.Op() != ir.ONIL {
return walkCompareInterface(n, init)
@@ -404,7 +405,8 @@ func walkCompareString(n *ir.BinaryExpr, init *ir.Nodes) ir.Node {
}
// The result of finishCompare MUST be assigned back to n, e.g.
-// n.Left = finishCompare(n.Left, x, r, init)
+//
+// n.Left = finishCompare(n.Left, x, r, init)
func finishCompare(n *ir.BinaryExpr, r ir.Node, init *ir.Nodes) ir.Node {
r = typecheck.Expr(r)
r = typecheck.Conv(r, n.Type())
diff --git a/src/cmd/compile/internal/walk/expr.go b/src/cmd/compile/internal/walk/expr.go
index 4c1e7adddd7..26a23c4d091 100644
--- a/src/cmd/compile/internal/walk/expr.go
+++ b/src/cmd/compile/internal/walk/expr.go
@@ -20,7 +20,8 @@ import (
)
// The result of walkExpr MUST be assigned back to n, e.g.
-// n.Left = walkExpr(n.Left, init)
+//
+// n.Left = walkExpr(n.Left, init)
func walkExpr(n ir.Node, init *ir.Nodes) ir.Node {
if n == nil {
return n
diff --git a/src/cmd/compile/internal/walk/order.go b/src/cmd/compile/internal/walk/order.go
index cc37f957648..80806478beb 100644
--- a/src/cmd/compile/internal/walk/order.go
+++ b/src/cmd/compile/internal/walk/order.go
@@ -237,7 +237,8 @@ func isaddrokay(n ir.Node) bool {
// If the original argument n is not okay, addrTemp creates a tmp, emits
// tmp = n, and then returns tmp.
// The result of addrTemp MUST be assigned back to n, e.g.
-// n.Left = o.addrTemp(n.Left)
+//
+// n.Left = o.addrTemp(n.Left)
func (o *orderState) addrTemp(n ir.Node) ir.Node {
if n.Op() == ir.OLITERAL || n.Op() == ir.ONIL {
// TODO: expand this to all static composite literal nodes?
@@ -316,8 +317,10 @@ func (o *orderState) mapKeyTemp(t *types.Type, n ir.Node) ir.Node {
// Returns a bool that signals if a modification was made.
//
// For:
-// x = m[string(k)]
-// x = m[T1{... Tn{..., string(k), ...}]
+//
+// x = m[string(k)]
+// x = m[T1{... Tn{..., string(k), ...}]
+//
// where k is []byte, T1 to Tn is a nesting of struct and array literals,
// the allocation of backing bytes for the string can be avoided
// by reusing the []byte backing array. These are special cases
@@ -400,9 +403,12 @@ func (o *orderState) stmtList(l ir.Nodes) {
}
// orderMakeSliceCopy matches the pattern:
-// m = OMAKESLICE([]T, x); OCOPY(m, s)
+//
+// m = OMAKESLICE([]T, x); OCOPY(m, s)
+//
// and rewrites it to:
-// m = OMAKESLICECOPY([]T, x, s); nil
+//
+// m = OMAKESLICECOPY([]T, x, s); nil
func orderMakeSliceCopy(s []ir.Node) {
if base.Flag.N != 0 || base.Flag.Cfg.Instrumenting {
return
@@ -473,7 +479,8 @@ func orderBlock(n *ir.Nodes, free map[string][]*ir.Name) {
// exprInPlace orders the side effects in *np and
// leaves them as the init list of the final *np.
// The result of exprInPlace MUST be assigned back to n, e.g.
-// n.Left = o.exprInPlace(n.Left)
+//
+// n.Left = o.exprInPlace(n.Left)
func (o *orderState) exprInPlace(n ir.Node) ir.Node {
var order orderState
order.free = o.free
@@ -489,7 +496,9 @@ func (o *orderState) exprInPlace(n ir.Node) ir.Node {
// orderStmtInPlace orders the side effects of the single statement *np
// and replaces it with the resulting statement list.
// The result of orderStmtInPlace MUST be assigned back to n, e.g.
-// n.Left = orderStmtInPlace(n.Left)
+//
+// n.Left = orderStmtInPlace(n.Left)
+//
// free is a map that can be used to obtain temporary variables by type.
func orderStmtInPlace(n ir.Node, free map[string][]*ir.Name) ir.Node {
var order orderState
@@ -1087,7 +1096,8 @@ func (o *orderState) exprNoLHS(n ir.Node) ir.Node {
// Otherwise lhs == nil. (When lhs != nil it may be possible
// to avoid copying the result of the expression to a temporary.)
// The result of expr MUST be assigned back to n, e.g.
-// n.Left = o.expr(n.Left, lhs)
+//
+// n.Left = o.expr(n.Left, lhs)
func (o *orderState) expr(n, lhs ir.Node) ir.Node {
if n == nil {
return n
@@ -1451,10 +1461,14 @@ func (o *orderState) expr1(n, lhs ir.Node) ir.Node {
// as2func orders OAS2FUNC nodes. It creates temporaries to ensure left-to-right assignment.
// The caller should order the right-hand side of the assignment before calling order.as2func.
// It rewrites,
+//
// a, b, a = ...
+//
// as
+//
// tmp1, tmp2, tmp3 = ...
// a, b, a = tmp1, tmp2, tmp3
+//
// This is necessary to ensure left to right assignment order.
func (o *orderState) as2func(n *ir.AssignListStmt) {
results := n.Rhs[0].Type()
diff --git a/src/cmd/compile/internal/walk/stmt.go b/src/cmd/compile/internal/walk/stmt.go
index f09e9165464..8a42dbf777c 100644
--- a/src/cmd/compile/internal/walk/stmt.go
+++ b/src/cmd/compile/internal/walk/stmt.go
@@ -10,7 +10,8 @@ import (
)
// The result of walkStmt MUST be assigned back to n, e.g.
-// n.Left = walkStmt(n.Left)
+//
+// n.Left = walkStmt(n.Left)
func walkStmt(n ir.Node) ir.Node {
if n == nil {
return n
@@ -84,6 +85,7 @@ func walkStmt(n ir.Node) ir.Node {
ir.OFALL,
ir.OGOTO,
ir.OLABEL,
+ ir.OJUMPTABLE,
ir.ODCL,
ir.ODCLCONST,
ir.ODCLTYPE,
diff --git a/src/cmd/compile/internal/walk/switch.go b/src/cmd/compile/internal/walk/switch.go
index 3705c5b1926..5067d5eb499 100644
--- a/src/cmd/compile/internal/walk/switch.go
+++ b/src/cmd/compile/internal/walk/switch.go
@@ -11,6 +11,7 @@ import (
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
+ "cmd/compile/internal/ssagen"
"cmd/compile/internal/typecheck"
"cmd/compile/internal/types"
"cmd/internal/src"
@@ -66,6 +67,7 @@ func walkSwitchExpr(sw *ir.SwitchStmt) {
base.Pos = lno
s := exprSwitch{
+ pos: lno,
exprname: cond,
}
@@ -112,6 +114,7 @@ func walkSwitchExpr(sw *ir.SwitchStmt) {
// An exprSwitch walks an expression switch.
type exprSwitch struct {
+ pos src.XPos
exprname ir.Node // value being switched on
done ir.Nodes
@@ -182,17 +185,59 @@ func (s *exprSwitch) flush() {
}
runs = append(runs, cc[start:])
- // Perform two-level binary search.
- binarySearch(len(runs), &s.done,
- func(i int) ir.Node {
- return ir.NewBinaryExpr(base.Pos, ir.OLE, ir.NewUnaryExpr(base.Pos, ir.OLEN, s.exprname), ir.NewInt(runLen(runs[i-1])))
- },
- func(i int, nif *ir.IfStmt) {
- run := runs[i]
- nif.Cond = ir.NewBinaryExpr(base.Pos, ir.OEQ, ir.NewUnaryExpr(base.Pos, ir.OLEN, s.exprname), ir.NewInt(runLen(run)))
- s.search(run, &nif.Body)
- },
- )
+ if len(runs) == 1 {
+ s.search(runs[0], &s.done)
+ return
+ }
+ // We have strings of more than one length. Generate an
+ // outer switch which switches on the length of the string
+ // and an inner switch in each case which resolves all the
+ // strings of the same length. The code looks something like this:
+
+ // goto outerLabel
+ // len5:
+ // ... search among length 5 strings ...
+ // goto endLabel
+ // len8:
+ // ... search among length 8 strings ...
+ // goto endLabel
+ // ... other lengths ...
+ // outerLabel:
+ // switch len(s) {
+ // case 5: goto len5
+ // case 8: goto len8
+ // ... other lengths ...
+ // }
+ // endLabel:
+
+ outerLabel := typecheck.AutoLabel(".s")
+ endLabel := typecheck.AutoLabel(".s")
+
+ // Jump around all the individual switches for each length.
+ s.done.Append(ir.NewBranchStmt(s.pos, ir.OGOTO, outerLabel))
+
+ var outer exprSwitch
+ outer.exprname = ir.NewUnaryExpr(s.pos, ir.OLEN, s.exprname)
+ outer.exprname.SetType(types.Types[types.TINT])
+
+ for _, run := range runs {
+ // Target label to jump to when we match this length.
+ label := typecheck.AutoLabel(".s")
+
+ // Search within this run of same-length strings.
+ pos := run[0].pos
+ s.done.Append(ir.NewLabelStmt(pos, label))
+ s.search(run, &s.done)
+ s.done.Append(ir.NewBranchStmt(pos, ir.OGOTO, endLabel))
+
+ // Add length case to outer switch.
+ cas := ir.NewBasicLit(pos, constant.MakeInt64(runLen(run)))
+ jmp := ir.NewBranchStmt(pos, ir.OGOTO, label)
+ outer.Add(pos, cas, jmp)
+ }
+ s.done.Append(ir.NewLabelStmt(s.pos, outerLabel))
+ outer.Emit(&s.done)
+ s.done.Append(ir.NewLabelStmt(s.pos, endLabel))
return
}
@@ -223,6 +268,9 @@ func (s *exprSwitch) flush() {
}
func (s *exprSwitch) search(cc []exprClause, out *ir.Nodes) {
+ if s.tryJumpTable(cc, out) {
+ return
+ }
binarySearch(len(cc), out,
func(i int) ir.Node {
return ir.NewBinaryExpr(base.Pos, ir.OLE, s.exprname, cc[i-1].hi)
@@ -235,6 +283,48 @@ func (s *exprSwitch) search(cc []exprClause, out *ir.Nodes) {
)
}
+// Try to implement the clauses with a jump table. Returns true if successful.
+func (s *exprSwitch) tryJumpTable(cc []exprClause, out *ir.Nodes) bool {
+ const go119UseJumpTables = true
+ const minCases = 8 // have at least minCases cases in the switch
+ const minDensity = 4 // use at least 1 out of every minDensity entries
+
+ if !go119UseJumpTables || base.Flag.N != 0 || !ssagen.Arch.LinkArch.CanJumpTable {
+ return false
+ }
+ if len(cc) < minCases {
+ return false // not enough cases for it to be worth it
+ }
+ if cc[0].lo.Val().Kind() != constant.Int {
+ return false // e.g. float
+ }
+ if s.exprname.Type().Size() > int64(types.PtrSize) {
+ return false // 64-bit switches on 32-bit archs
+ }
+ min := cc[0].lo.Val()
+ max := cc[len(cc)-1].hi.Val()
+ width := constant.BinaryOp(constant.BinaryOp(max, token.SUB, min), token.ADD, constant.MakeInt64(1))
+ limit := constant.MakeInt64(int64(len(cc)) * minDensity)
+ if constant.Compare(width, token.GTR, limit) {
+ // We disable jump tables if we use less than a minimum fraction of the entries.
+ // i.e. for switch x {case 0: case 1000: case 2000:} we don't want to use a jump table.
+ return false
+ }
+ jt := ir.NewJumpTableStmt(base.Pos, s.exprname)
+ for _, c := range cc {
+ jmp := c.jmp.(*ir.BranchStmt)
+ if jmp.Op() != ir.OGOTO || jmp.Label == nil {
+ panic("bad switch case body")
+ }
+ for i := c.lo.Val(); constant.Compare(i, token.LEQ, c.hi.Val()); i = constant.BinaryOp(i, token.ADD, constant.MakeInt64(1)) {
+ jt.Cases = append(jt.Cases, i)
+ jt.Targets = append(jt.Targets, jmp.Label)
+ }
+ }
+ out.Append(jt)
+ return true
+}
+
func (c *exprClause) test(exprname ir.Node) ir.Node {
// Integer range.
if c.hi != c.lo {
@@ -540,6 +630,7 @@ func (s *typeSwitch) flush() {
}
cc = merged
+ // TODO: figure out if we could use a jump table using some low bits of the type hashes.
binarySearch(len(cc), &s.done,
func(i int) ir.Node {
return ir.NewBinaryExpr(base.Pos, ir.OLE, s.hashname, ir.NewInt(int64(cc[i-1].hash)))
@@ -562,7 +653,7 @@ func (s *typeSwitch) flush() {
// then cases before i will be tested; otherwise, cases i and later.
//
// leaf(i, nif) should setup nif (an OIF node) to test case i. In
-// particular, it should set nif.Left and nif.Nbody.
+// particular, it should set nif.Cond and nif.Body.
func binarySearch(n int, out *ir.Nodes, less func(i int) ir.Node, leaf func(i int, nif *ir.IfStmt)) {
const binarySearchMin = 4 // minimum number of cases for binary search
diff --git a/src/cmd/compile/internal/x86/ssa.go b/src/cmd/compile/internal/x86/ssa.go
index 32e29f347b9..378100b1627 100644
--- a/src/cmd/compile/internal/x86/ssa.go
+++ b/src/cmd/compile/internal/x86/ssa.go
@@ -106,7 +106,9 @@ func moveByType(t *types.Type) obj.As {
}
// opregreg emits instructions for
-// dest := dest(To) op src(From)
+//
+// dest := dest(To) op src(From)
+//
// and also returns the created obj.Prog so it
// may be further adjusted (offset, scale, etc).
func opregreg(s *ssagen.State, op obj.As, dest, src int16) *obj.Prog {
@@ -725,7 +727,7 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
// caller's SP is the address of the first arg
p := s.Prog(x86.AMOVL)
p.From.Type = obj.TYPE_ADDR
- p.From.Offset = -base.Ctxt.FixedFrameSize() // 0 on 386, just to be consistent with other architectures
+ p.From.Offset = -base.Ctxt.Arch.FixedFrameSize // 0 on 386, just to be consistent with other architectures
p.From.Name = obj.NAME_PARAM
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
diff --git a/src/cmd/cover/cover.go b/src/cmd/cover/cover.go
index 9c8529f7eb6..86ef128f2cc 100644
--- a/src/cmd/cover/cover.go
+++ b/src/cmd/cover/cover.go
@@ -377,7 +377,7 @@ func (f *File) newCounter(start, end token.Pos, numStmt int) string {
// S1
// if cond {
// S2
-// }
+// }
// S3
//
// counters will be added before S1 and before S3. The block containing S2
diff --git a/src/cmd/cover/cover_test.go b/src/cmd/cover/cover_test.go
index 8bd31514a04..28be2311217 100644
--- a/src/cmd/cover/cover_test.go
+++ b/src/cmd/cover/cover_test.go
@@ -162,8 +162,8 @@ func buildCover(t *testing.T) {
// Run this shell script, but do it in Go so it can be run by "go test".
//
// replace the word LINE with the line number < testdata/test.go > testdata/test_line.go
-// go build -o testcover
-// testcover -mode=count -var=CoverTest -o ./testdata/test_cover.go testdata/test_line.go
+// go build -o testcover
+// testcover -mode=count -var=CoverTest -o ./testdata/test_cover.go testdata/test_line.go
// go run ./testdata/main.go ./testdata/test.go
func TestCover(t *testing.T) {
t.Parallel()
diff --git a/src/cmd/cover/doc.go b/src/cmd/cover/doc.go
index e2c849419ab..e091ce9e300 100644
--- a/src/cmd/cover/doc.go
+++ b/src/cmd/cover/doc.go
@@ -19,6 +19,7 @@ must be applied to the output of cgo preprocessing, not the input,
because cover deletes comments that are significant to cgo.
For usage information, please see:
+
go help testflag
go tool cover -help
*/
diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go
index db2ac1f2a6e..bbaf595421d 100644
--- a/src/cmd/dist/build.go
+++ b/src/cmd/dist/build.go
@@ -26,7 +26,7 @@ import (
// The usual variables.
var (
goarch string
- gobin string
+ gorootBin string
gohostarch string
gohostos string
goos string
@@ -112,6 +112,7 @@ func xinit() {
fatalf("$GOROOT must be set")
}
goroot = filepath.Clean(b)
+ gorootBin = pathf("%s/bin", goroot)
b = os.Getenv("GOROOT_FINAL")
if b == "" {
@@ -119,12 +120,6 @@ func xinit() {
}
goroot_final = b
- b = os.Getenv("GOBIN")
- if b == "" {
- b = pathf("%s/bin", goroot)
- }
- gobin = b
-
b = os.Getenv("GOOS")
if b == "" {
b = gohostos
@@ -241,9 +236,19 @@ func xinit() {
// make.bash really does start from a clean slate.
os.Setenv("GOCACHE", pathf("%s/pkg/obj/go-build", goroot))
+ // Set GOBIN to GOROOT/bin. The meaning of GOBIN has drifted over time
+ // (see https://go.dev/issue/3269, https://go.dev/cl/183058,
+ // https://go.dev/issue/31576). Since we want binaries installed by 'dist' to
+ // always go to GOROOT/bin anyway.
+ os.Setenv("GOBIN", gorootBin)
+
// Make the environment more predictable.
os.Setenv("LANG", "C")
os.Setenv("LANGUAGE", "en_US.UTF8")
+ os.Unsetenv("GO111MODULE")
+ os.Setenv("GOENV", "off")
+ os.Unsetenv("GOFLAGS")
+ os.Setenv("GOWORK", "off")
workdir = xworkdir()
if err := ioutil.WriteFile(pathf("%s/go.mod", workdir), []byte("module bootstrap"), 0666); err != nil {
@@ -490,16 +495,6 @@ func setup() {
xremove(pathf("%s/bin/%s", goroot, old))
}
- // If $GOBIN is set and has a Go compiler, it must be cleaned.
- for _, char := range "56789" {
- if isfile(pathf("%s/%c%s", gobin, char, "g")) {
- for _, old := range oldtool {
- xremove(pathf("%s/%s", gobin, old))
- }
- break
- }
- }
-
// For release, make sure excluded things are excluded.
goversion := findgoversion()
if strings.HasPrefix(goversion, "release.") || (strings.HasPrefix(goversion, "go") && !strings.Contains(goversion, "beta")) {
@@ -1126,8 +1121,8 @@ func clean() {
// The env command prints the default environment.
func cmdenv() {
path := flag.Bool("p", false, "emit updated PATH")
- plan9 := flag.Bool("9", false, "emit plan 9 syntax")
- windows := flag.Bool("w", false, "emit windows syntax")
+ plan9 := flag.Bool("9", gohostos == "plan9", "emit plan 9 syntax")
+ windows := flag.Bool("w", gohostos == "windows", "emit windows syntax")
xflagparse(0)
format := "%s=\"%s\"\n"
@@ -1138,10 +1133,13 @@ func cmdenv() {
format = "set %s=%s\r\n"
}
+ xprintf(format, "GO111MODULE", "")
xprintf(format, "GOARCH", goarch)
- xprintf(format, "GOBIN", gobin)
+ xprintf(format, "GOBIN", gorootBin)
xprintf(format, "GOCACHE", os.Getenv("GOCACHE"))
xprintf(format, "GODEBUG", os.Getenv("GODEBUG"))
+ xprintf(format, "GOENV", "off")
+ xprintf(format, "GOFLAGS", "")
xprintf(format, "GOHOSTARCH", gohostarch)
xprintf(format, "GOHOSTOS", gohostos)
xprintf(format, "GOOS", goos)
@@ -1167,13 +1165,14 @@ func cmdenv() {
if goarch == "ppc64" || goarch == "ppc64le" {
xprintf(format, "GOPPC64", goppc64)
}
+ xprintf(format, "GOWORK", "off")
if *path {
sep := ":"
if gohostos == "windows" {
sep = ";"
}
- xprintf(format, "PATH", fmt.Sprintf("%s%s%s", gobin, sep, os.Getenv("PATH")))
+ xprintf(format, "PATH", fmt.Sprintf("%s%s%s", gorootBin, sep, os.Getenv("PATH")))
}
}
@@ -1226,7 +1225,9 @@ var toolchain = []string{"cmd/asm", "cmd/cgo", "cmd/compile", "cmd/link"}
// commands (like "go tool dist test" in run.bash) can rely on bug fixes
// made since Go 1.4, but this function cannot. In particular, the uses
// of os/exec in this function cannot assume that
+//
// cmd.Env = append(os.Environ(), "X=Y")
+//
// sets $X to Y in the command's environment. That guarantee was
// added after Go 1.4, and in fact in Go 1.4 it was typically the opposite:
// if $X was already present in os.Environ(), most systems preferred
@@ -1318,7 +1319,7 @@ func cmdbootstrap() {
gogcflags = os.Getenv("GO_GCFLAGS") // we were using $BOOT_GO_GCFLAGS until now
goldflags = os.Getenv("GO_LDFLAGS") // we were using $BOOT_GO_LDFLAGS until now
goBootstrap := pathf("%s/go_bootstrap", tooldir)
- cmdGo := pathf("%s/go", gobin)
+ cmdGo := pathf("%s/go", gorootBin)
if debug {
run("", ShowOutput|CheckExit, pathf("%s/compile", tooldir), "-V=full")
copyfile(pathf("%s/compile1", tooldir), pathf("%s/compile", tooldir), writeExec)
@@ -1457,7 +1458,7 @@ func cmdbootstrap() {
os.Setenv("GOOS", gohostos)
os.Setenv("GOARCH", gohostarch)
os.Setenv("CC", compilerEnvLookup(defaultcc, gohostos, gohostarch))
- goCmd(cmdGo, "build", "-o", pathf("%s/go_%s_%s_exec%s", gobin, goos, goarch, exe), wrapperPath)
+ goCmd(cmdGo, "build", "-o", pathf("%s/go_%s_%s_exec%s", gorootBin, goos, goarch, exe), wrapperPath)
// Restore environment.
// TODO(elias.naur): support environment variables in goCmd?
os.Setenv("GOOS", goos)
@@ -1681,26 +1682,26 @@ func banner() {
}
xprintf("---\n")
xprintf("Installed Go for %s/%s in %s\n", goos, goarch, goroot)
- xprintf("Installed commands in %s\n", gobin)
+ xprintf("Installed commands in %s\n", gorootBin)
if !xsamefile(goroot_final, goroot) {
// If the files are to be moved, don't check that gobin
// is on PATH; assume they know what they are doing.
} else if gohostos == "plan9" {
- // Check that gobin is bound before /bin.
+ // Check that GOROOT/bin is bound before /bin.
pid := strings.Replace(readfile("#c/pid"), " ", "", -1)
ns := fmt.Sprintf("/proc/%s/ns", pid)
- if !strings.Contains(readfile(ns), fmt.Sprintf("bind -b %s /bin", gobin)) {
- xprintf("*** You need to bind %s before /bin.\n", gobin)
+ if !strings.Contains(readfile(ns), fmt.Sprintf("bind -b %s /bin", gorootBin)) {
+ xprintf("*** You need to bind %s before /bin.\n", gorootBin)
}
} else {
- // Check that gobin appears in $PATH.
+ // Check that GOROOT/bin appears in $PATH.
pathsep := ":"
if gohostos == "windows" {
pathsep = ";"
}
- if !strings.Contains(pathsep+os.Getenv("PATH")+pathsep, pathsep+gobin+pathsep) {
- xprintf("*** You need to add %s to your PATH.\n", gobin)
+ if !strings.Contains(pathsep+os.Getenv("PATH")+pathsep, pathsep+gorootBin+pathsep) {
+ xprintf("*** You need to add %s to your PATH.\n", gorootBin)
}
}
diff --git a/src/cmd/dist/doc.go b/src/cmd/dist/doc.go
index a4e6aa5cbfd..ad26aa2dc06 100644
--- a/src/cmd/dist/doc.go
+++ b/src/cmd/dist/doc.go
@@ -5,15 +5,17 @@
// Dist helps bootstrap, build, and test the Go distribution.
//
// Usage:
-// go tool dist [command]
+//
+// go tool dist [command]
//
// The commands are:
-// banner print installation banner
-// bootstrap rebuild everything
-// clean deletes all built files
-// env [-p] print environment (-p: include $PATH)
-// install [dir] install individual directory
-// list [-json] list all supported platforms
-// test [-h] run Go test(s)
-// version print Go version
+//
+// banner print installation banner
+// bootstrap rebuild everything
+// clean deletes all built files
+// env [-p] print environment (-p: include $PATH)
+// install [dir] install individual directory
+// list [-json] list all supported platforms
+// test [-h] run Go test(s)
+// version print Go version
package main
diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go
index 9118c133e5e..ee521f81bad 100644
--- a/src/cmd/dist/test.go
+++ b/src/cmd/dist/test.go
@@ -100,8 +100,8 @@ func (t *tester) run() {
if goos == "windows" {
exeSuffix = ".exe"
}
- if _, err := os.Stat(filepath.Join(gobin, "go"+exeSuffix)); err == nil {
- os.Setenv("PATH", fmt.Sprintf("%s%c%s", gobin, os.PathListSeparator, os.Getenv("PATH")))
+ if _, err := os.Stat(filepath.Join(gorootBin, "go"+exeSuffix)); err == nil {
+ os.Setenv("PATH", fmt.Sprintf("%s%c%s", gorootBin, os.PathListSeparator, os.Getenv("PATH")))
}
cmd := exec.Command("go", "env", "CGO_ENABLED")
diff --git a/src/cmd/doc/doc_test.go b/src/cmd/doc/doc_test.go
index ead4f722f60..5887ad3395b 100644
--- a/src/cmd/doc/doc_test.go
+++ b/src/cmd/doc/doc_test.go
@@ -882,7 +882,9 @@ func TestDoc(t *testing.T) {
}
// Test the code to try multiple packages. Our test case is
+//
// go doc rand.Float64
+//
// This needs to find math/rand.Float64; however crypto/rand, which doesn't
// have the symbol, usually appears first in the directory listing.
func TestMultiplePackages(t *testing.T) {
@@ -939,11 +941,15 @@ func TestMultiplePackages(t *testing.T) {
}
// Test the code to look up packages when given two args. First test case is
+//
// go doc binary BigEndian
+//
// This needs to find encoding/binary.BigEndian, which means
// finding the package encoding/binary given only "binary".
// Second case is
+//
// go doc rand Float64
+//
// which again needs to find math/rand and not give up after crypto/rand,
// which has no such function.
func TestTwoArgLookup(t *testing.T) {
diff --git a/src/cmd/doc/main.go b/src/cmd/doc/main.go
index dee5d7bbcd7..3c45dd76dfa 100644
--- a/src/cmd/doc/main.go
+++ b/src/cmd/doc/main.go
@@ -5,20 +5,25 @@
// Doc (usually run as go doc) accepts zero, one or two arguments.
//
// Zero arguments:
+//
// go doc
+//
// Show the documentation for the package in the current directory.
//
// One argument:
+//
// go doc <pkg>
// go doc <sym>[.<methodOrField>]
// go doc [<pkg>.]<sym>[.<methodOrField>]
// go doc [<pkg>.][<sym>.]<methodOrField>
+//
// The first item in this list that succeeds is the one whose documentation
// is printed. If there is a symbol but no package, the package in the current
// directory is chosen. However, if the argument begins with a capital
// letter it is always assumed to be a symbol in the current directory.
//
// Two arguments:
+//
// go doc <pkg> <sym>[.<methodOrField>]
//
// Show the documentation for the package, symbol, and method or field. The
diff --git a/src/cmd/doc/pkg.go b/src/cmd/doc/pkg.go
index 49b68873b6f..35f2eb24bf2 100644
--- a/src/cmd/doc/pkg.go
+++ b/src/cmd/doc/pkg.go
@@ -25,8 +25,7 @@ import (
)
const (
- punchedCardWidth = 80 // These things just won't leave us alone.
- indentedWidth = punchedCardWidth - len(indent)
+ punchedCardWidth = 80
indent = " "
)
@@ -44,6 +43,14 @@ type Package struct {
buf pkgBuffer
}
+func (p *Package) ToText(w io.Writer, text, prefix, codePrefix string) {
+ d := p.doc.Parser().Parse(text)
+ pr := p.doc.Printer()
+ pr.TextPrefix = prefix
+ pr.TextCodePrefix = codePrefix
+ w.Write(pr.Text(d))
+}
+
// pkgBuffer is a wrapper for bytes.Buffer that prints a package clause the
// first time Write is called.
type pkgBuffer struct {
@@ -251,7 +258,7 @@ func (pkg *Package) emit(comment string, node ast.Node) {
}
if comment != "" && !showSrc {
pkg.newlines(1)
- doc.ToText(&pkg.buf, comment, indent, indent+indent, indentedWidth)
+ pkg.ToText(&pkg.buf, comment, indent, indent+indent)
pkg.newlines(2) // Blank line after comment to separate from next item.
} else {
pkg.newlines(1)
@@ -463,7 +470,7 @@ func joinStrings(ss []string) string {
// allDoc prints all the docs for the package.
func (pkg *Package) allDoc() {
pkg.Printf("") // Trigger the package clause; we know the package exists.
- doc.ToText(&pkg.buf, pkg.doc.Doc, "", indent, indentedWidth)
+ pkg.ToText(&pkg.buf, pkg.doc.Doc, "", indent)
pkg.newlines(1)
printed := make(map[*ast.GenDecl]bool)
@@ -523,7 +530,7 @@ func (pkg *Package) allDoc() {
func (pkg *Package) packageDoc() {
pkg.Printf("") // Trigger the package clause; we know the package exists.
if !short {
- doc.ToText(&pkg.buf, pkg.doc.Doc, "", indent, indentedWidth)
+ pkg.ToText(&pkg.buf, pkg.doc.Doc, "", indent)
pkg.newlines(1)
}
@@ -1033,9 +1040,9 @@ func (pkg *Package) printFieldDoc(symbol, fieldName string) bool {
if field.Doc != nil {
// To present indented blocks in comments correctly, process the comment as
// a unit before adding the leading // to each line.
- docBuf := bytes.Buffer{}
- doc.ToText(&docBuf, field.Doc.Text(), "", indent, indentedWidth)
- scanner := bufio.NewScanner(&docBuf)
+ docBuf := new(bytes.Buffer)
+ pkg.ToText(docBuf, field.Doc.Text(), "", indent)
+ scanner := bufio.NewScanner(docBuf)
for scanner.Scan() {
fmt.Fprintf(&pkg.buf, "%s// %s\n", indent, scanner.Bytes())
}
diff --git a/src/cmd/fix/cftype.go b/src/cmd/fix/cftype.go
index 27e4088aa9f..e4988b1c625 100644
--- a/src/cmd/fix/cftype.go
+++ b/src/cmd/fix/cftype.go
@@ -24,9 +24,13 @@ var cftypeFix = fix{
}
// Old state:
-// type CFTypeRef unsafe.Pointer
+//
+// type CFTypeRef unsafe.Pointer
+//
// New state:
-// type CFTypeRef uintptr
+//
+// type CFTypeRef uintptr
+//
// and similar for other *Ref types.
// This fix finds nils initializing these types and replaces the nils with 0s.
func cftypefix(f *ast.File) bool {
diff --git a/src/cmd/fix/doc.go b/src/cmd/fix/doc.go
index 0570169576b..062eb792856 100644
--- a/src/cmd/fix/doc.go
+++ b/src/cmd/fix/doc.go
@@ -8,6 +8,7 @@ newer ones. After you update to a new Go release, fix helps make
the necessary changes to your programs.
Usage:
+
go tool fix [-r name,...] [path ...]
Without an explicit path, fix reads standard input and writes the
@@ -30,7 +31,7 @@ Fix prints the full list of fixes it can apply in its help output;
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
+Instead, use a version control system's “diff” functionality to inspect
the changes that fix makes before committing them.
*/
package main
diff --git a/src/cmd/fix/egltype.go b/src/cmd/fix/egltype.go
index cb0f7a73de1..a096db6665a 100644
--- a/src/cmd/fix/egltype.go
+++ b/src/cmd/fix/egltype.go
@@ -22,9 +22,13 @@ var eglFixDisplay = fix{
}
// Old state:
-// type EGLDisplay unsafe.Pointer
+//
+// type EGLDisplay unsafe.Pointer
+//
// New state:
-// type EGLDisplay uintptr
+//
+// type EGLDisplay uintptr
+//
// This fix finds nils initializing these types and replaces the nils with 0s.
func eglfixDisp(f *ast.File) bool {
return typefix(f, func(s string) bool {
@@ -41,9 +45,13 @@ var eglFixConfig = fix{
}
// Old state:
-// type EGLConfig unsafe.Pointer
+//
+// type EGLConfig unsafe.Pointer
+//
// New state:
-// type EGLConfig uintptr
+//
+// type EGLConfig uintptr
+//
// This fix finds nils initializing these types and replaces the nils with 0s.
func eglfixConfig(f *ast.File) bool {
return typefix(f, func(s string) bool {
diff --git a/src/cmd/fix/jnitype.go b/src/cmd/fix/jnitype.go
index 29abe0f0078..111be8e70c6 100644
--- a/src/cmd/fix/jnitype.go
+++ b/src/cmd/fix/jnitype.go
@@ -21,9 +21,13 @@ var jniFix = fix{
}
// Old state:
-// type jobject *_jobject
+//
+// type jobject *_jobject
+//
// New state:
-// type jobject uintptr
+//
+// type jobject uintptr
+//
// and similar for subtypes of jobject.
// This fix finds nils initializing these types and replaces the nils with 0s.
func jnifix(f *ast.File) bool {
diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go
index f9d78b59e32..6fdb4f93a33 100644
--- a/src/cmd/go/alldocs.go
+++ b/src/cmd/go/alldocs.go
@@ -9,71 +9,69 @@
//
// Usage:
//
-// go <command> [arguments]
+// go <command> [arguments]
//
// The commands are:
//
-// bug start a bug report
-// build compile packages and dependencies
-// clean remove object files and cached files
-// doc show documentation for package or symbol
-// env print Go environment information
-// fix update packages to use new APIs
-// fmt gofmt (reformat) package sources
-// generate generate Go files by processing source
-// get add dependencies to current module and install them
-// install compile and install packages and dependencies
-// list list packages or modules
-// mod module maintenance
-// work workspace maintenance
-// run compile and run Go program
-// test test packages
-// tool run specified go tool
-// version print Go version
-// vet report likely mistakes in packages
+// bug start a bug report
+// build compile packages and dependencies
+// clean remove object files and cached files
+// doc show documentation for package or symbol
+// env print Go environment information
+// fix update packages to use new APIs
+// fmt gofmt (reformat) package sources
+// generate generate Go files by processing source
+// get add dependencies to current module and install them
+// install compile and install packages and dependencies
+// list list packages or modules
+// mod module maintenance
+// work workspace maintenance
+// run compile and run Go program
+// test test packages
+// tool run specified go tool
+// version print Go version
+// vet report likely mistakes in packages
//
// Use "go help <command>" for more information about a command.
//
// Additional help topics:
//
-// buildconstraint build constraints
-// buildmode build modes
-// c calling between Go and C
-// cache build and test caching
-// environment environment variables
-// filetype file types
-// go.mod the go.mod file
-// gopath GOPATH environment variable
-// gopath-get legacy GOPATH go get
-// goproxy module proxy protocol
-// importpath import path syntax
-// modules modules, module versions, and more
-// module-get module-aware go get
-// module-auth module authentication using go.sum
-// packages package lists and patterns
-// private configuration for downloading non-public code
-// testflag testing flags
-// testfunc testing functions
-// vcs controlling version control with GOVCS
+// buildconstraint build constraints
+// buildmode build modes
+// c calling between Go and C
+// cache build and test caching
+// environment environment variables
+// filetype file types
+// go.mod the go.mod file
+// gopath GOPATH environment variable
+// gopath-get legacy GOPATH go get
+// goproxy module proxy protocol
+// importpath import path syntax
+// modules modules, module versions, and more
+// module-get module-aware go get
+// module-auth module authentication using go.sum
+// packages package lists and patterns
+// private configuration for downloading non-public code
+// testflag testing flags
+// testfunc testing functions
+// vcs controlling version control with GOVCS
//
// Use "go help <topic>" for more information about that topic.
//
-//
-// Start a bug report
+// # Start a bug report
//
// Usage:
//
-// go bug
+// go bug
//
// Bug opens the default browser and starts a new bug report.
// The report includes useful system information.
//
-//
-// Compile packages and dependencies
+// # Compile packages and dependencies
//
// Usage:
//
-// go build [-o output] [build flags] [packages]
+// go build [-o output] [build flags] [packages]
//
// Build compiles the packages named by the import paths,
// along with their dependencies, but it does not install the results.
@@ -105,110 +103,112 @@
// The build flags are shared by the build, clean, get, install, list, run,
// and test commands:
//
-// -a
-// force rebuilding of packages that are already up-to-date.
-// -n
-// print the commands but do not run them.
-// -p n
-// the number of programs, such as build commands or
-// test binaries, that can be run in parallel.
-// The default is GOMAXPROCS, normally the number of CPUs available.
-// -race
-// enable data race detection.
-// Supported only on linux/amd64, freebsd/amd64, darwin/amd64, darwin/arm64, windows/amd64,
-// linux/ppc64le and linux/arm64 (only for 48-bit VMA).
-// -msan
-// enable interoperation with memory sanitizer.
-// Supported only on linux/amd64, linux/arm64
-// and only with Clang/LLVM as the host C compiler.
-// On linux/arm64, pie build mode will be used.
-// -asan
-// enable interoperation with address sanitizer.
-// Supported only on linux/arm64, linux/amd64.
-// -v
-// print the names of packages as they are compiled.
-// -work
-// print the name of the temporary work directory and
-// do not delete it when exiting.
-// -x
-// print the commands.
-//
-// -asmflags '[pattern=]arg list'
-// arguments to pass on each go tool asm invocation.
-// -buildmode mode
-// build mode to use. See 'go help buildmode' for more.
-// -buildvcs
-// Whether to stamp binaries with version control information. By default,
-// version control information is stamped into a binary if the main package
-// and the main module containing it are in the repository containing the
-// current directory (if there is a repository). Use -buildvcs=false to
-// omit version control information.
-// -compiler name
-// name of compiler to use, as in runtime.Compiler (gccgo or gc).
-// -gccgoflags '[pattern=]arg list'
-// arguments to pass on each gccgo compiler/linker invocation.
-// -gcflags '[pattern=]arg list'
-// arguments to pass on each go tool compile invocation.
-// -installsuffix suffix
-// a suffix to use in the name of the package installation directory,
-// in order to keep output separate from default builds.
-// If using the -race flag, the install suffix is automatically set to race
-// or, if set explicitly, has _race appended to it. Likewise for the -msan
-// and -asan flags. Using a -buildmode option that requires non-default compile
-// flags has a similar effect.
-// -ldflags '[pattern=]arg list'
-// arguments to pass on each go tool link invocation.
-// -linkshared
-// build code that will be linked against shared libraries previously
-// created with -buildmode=shared.
-// -mod mode
-// module download mode to use: readonly, vendor, or mod.
-// By default, if a vendor directory is present and the go version in go.mod
-// is 1.14 or higher, the go command acts as if -mod=vendor were set.
-// Otherwise, the go command acts as if -mod=readonly were set.
-// See https://golang.org/ref/mod#build-commands for details.
-// -modcacherw
-// leave newly-created directories in the module cache read-write
-// instead of making them read-only.
-// -modfile file
-// in module aware mode, read (and possibly write) an alternate go.mod
-// file instead of the one in the module root directory. A file named
-// "go.mod" must still be present in order to determine the module root
-// directory, but it is not accessed. When -modfile is specified, an
-// alternate go.sum file is also used: its path is derived from the
-// -modfile flag by trimming the ".mod" extension and appending ".sum".
-// -overlay file
-// read a JSON config file that provides an overlay for build operations.
-// The file is a JSON struct with a single field, named 'Replace', that
-// maps each disk file path (a string) to its backing file path, so that
-// a build will run as if the disk file path exists with the contents
-// given by the backing file paths, or as if the disk file path does not
-// exist if its backing file path is empty. Support for the -overlay flag
-// has some limitations: importantly, cgo files included from outside the
-// include path must be in the same directory as the Go package they are
-// included from, and overlays will not appear when binaries and tests are
-// run through go run and go test respectively.
-// -pkgdir dir
-// install and load all packages from dir instead of the usual locations.
-// For example, when building with a non-standard configuration,
-// use -pkgdir to keep generated packages in a separate location.
-// -tags tag,list
-// a comma-separated list of build tags to consider satisfied during the
-// build. For more information about build tags, see the description of
-// build constraints in the documentation for the go/build package.
-// (Earlier versions of Go used a space-separated list, and that form
-// is deprecated but still recognized.)
-// -trimpath
-// remove all file system paths from the resulting executable.
-// Instead of absolute file system paths, the recorded file names
-// will begin either a module path@version (when using modules),
-// or a plain import path (when using the standard library, or GOPATH).
-// -toolexec 'cmd args'
-// a program to use to invoke toolchain programs like vet and asm.
-// For example, instead of running asm, the go command will run
-// 'cmd args /path/to/asm <arguments for asm>'.
-// The TOOLEXEC_IMPORTPATH environment variable will be set,
-// matching 'go list -f {{.ImportPath}}' for the package being built.
+// -a
+// force rebuilding of packages that are already up-to-date.
+// -n
+// print the commands but do not run them.
+// -p n
+// the number of programs, such as build commands or
+// test binaries, that can be run in parallel.
+// The default is GOMAXPROCS, normally the number of CPUs available.
+// -race
+// enable data race detection.
+// Supported only on linux/amd64, freebsd/amd64, darwin/amd64, darwin/arm64, windows/amd64,
+// linux/ppc64le and linux/arm64 (only for 48-bit VMA).
+// -msan
+// enable interoperation with memory sanitizer.
+// Supported only on linux/amd64, linux/arm64
+// and only with Clang/LLVM as the host C compiler.
+// On linux/arm64, pie build mode will be used.
+// -asan
+// enable interoperation with address sanitizer.
+// Supported only on linux/arm64, linux/amd64.
+// -v
+// print the names of packages as they are compiled.
+// -work
+// print the name of the temporary work directory and
+// do not delete it when exiting.
+// -x
+// print the commands.
+//
+// -asmflags '[pattern=]arg list'
+// arguments to pass on each go tool asm invocation.
+// -buildmode mode
+// build mode to use. See 'go help buildmode' for more.
+// -buildvcs
+// Whether to stamp binaries with version control information
+// ("true", "false", or "auto"). By default ("auto"), version control
+// information is stamped into a binary if the main package, the main module
+// containing it, and the current directory are all in the same repository.
+// Use -buildvcs=false to always omit version control information, or
+// -buildvcs=true to error out if version control information is available but
+// cannot be included due to a missing tool or ambiguous directory structure.
+// -compiler name
+// name of compiler to use, as in runtime.Compiler (gccgo or gc).
+// -gccgoflags '[pattern=]arg list'
+// arguments to pass on each gccgo compiler/linker invocation.
+// -gcflags '[pattern=]arg list'
+// arguments to pass on each go tool compile invocation.
+// -installsuffix suffix
+// a suffix to use in the name of the package installation directory,
+// in order to keep output separate from default builds.
+// If using the -race flag, the install suffix is automatically set to race
+// or, if set explicitly, has _race appended to it. Likewise for the -msan
+// and -asan flags. Using a -buildmode option that requires non-default compile
+// flags has a similar effect.
+// -ldflags '[pattern=]arg list'
+// arguments to pass on each go tool link invocation.
+// -linkshared
+// build code that will be linked against shared libraries previously
+// created with -buildmode=shared.
+// -mod mode
+// module download mode to use: readonly, vendor, or mod.
+// By default, if a vendor directory is present and the go version in go.mod
+// is 1.14 or higher, the go command acts as if -mod=vendor were set.
+// Otherwise, the go command acts as if -mod=readonly were set.
+// See https://golang.org/ref/mod#build-commands for details.
+// -modcacherw
+// leave newly-created directories in the module cache read-write
+// instead of making them read-only.
+// -modfile file
+// in module aware mode, read (and possibly write) an alternate go.mod
+// file instead of the one in the module root directory. A file named
+// "go.mod" must still be present in order to determine the module root
+// directory, but it is not accessed. When -modfile is specified, an
+// alternate go.sum file is also used: its path is derived from the
+// -modfile flag by trimming the ".mod" extension and appending ".sum".
+// -overlay file
+// read a JSON config file that provides an overlay for build operations.
+// The file is a JSON struct with a single field, named 'Replace', that
+// maps each disk file path (a string) to its backing file path, so that
+// a build will run as if the disk file path exists with the contents
+// given by the backing file paths, or as if the disk file path does not
+// exist if its backing file path is empty. Support for the -overlay flag
+// has some limitations: importantly, cgo files included from outside the
+// include path must be in the same directory as the Go package they are
+// included from, and overlays will not appear when binaries and tests are
+// run through go run and go test respectively.
+// -pkgdir dir
+// install and load all packages from dir instead of the usual locations.
+// For example, when building with a non-standard configuration,
+// use -pkgdir to keep generated packages in a separate location.
+// -tags tag,list
+// a comma-separated list of build tags to consider satisfied during the
+// build. For more information about build tags, see the description of
+// build constraints in the documentation for the go/build package.
+// (Earlier versions of Go used a space-separated list, and that form
+// is deprecated but still recognized.)
+// -trimpath
+// remove all file system paths from the resulting executable.
+// Instead of absolute file system paths, the recorded file names
+// will begin either a module path@version (when using modules),
+// or a plain import path (when using the standard library, or GOPATH).
+// -toolexec 'cmd args'
+// a program to use to invoke toolchain programs like vet and asm.
+// For example, instead of running asm, the go command will run
+// 'cmd args /path/to/asm <arguments for asm>'.
+// The TOOLEXEC_IMPORTPATH environment variable will be set,
+// matching 'go list -f {{.ImportPath}}' for the package being built.
//
// The -asmflags, -gccgoflags, -gcflags, and -ldflags flags accept a
// space-separated list of arguments to pass to an underlying tool
@@ -240,12 +240,11 @@
//
// See also: go install, go get, go clean.
//
-//
-// Remove object files and cached files
+// # Remove object files and cached files
//
// Usage:
//
-// go clean [clean flags] [build flags] [packages]
+// go clean [clean flags] [build flags] [packages]
//
// Clean removes object files from package source directories.
// The go command builds most objects in a temporary directory,
@@ -256,17 +255,17 @@
// clean removes the following files from each of the
// source directories corresponding to the import paths:
//
-// _obj/ old object directory, left from Makefiles
-// _test/ old test directory, left from Makefiles
-// _testmain.go old gotest file, left from Makefiles
-// test.out old test log, left from Makefiles
-// build.out old test log, left from Makefiles
-// *.[568ao] object files, left from Makefiles
+// _obj/ old object directory, left from Makefiles
+// _test/ old test directory, left from Makefiles
+// _testmain.go old gotest file, left from Makefiles
+// test.out old test log, left from Makefiles
+// build.out old test log, left from Makefiles
+// *.[568ao] object files, left from Makefiles
//
-// DIR(.exe) from go build
-// DIR.test(.exe) from go test -c
-// MAINFILE(.exe) from go build MAINFILE.go
-// *.so from SWIG
+// DIR(.exe) from go build
+// DIR.test(.exe) from go test -c
+// MAINFILE(.exe) from go build MAINFILE.go
+// *.so from SWIG
//
// In the list, DIR represents the final path element of the
// directory, and MAINFILE is the base name of any Go source
@@ -304,12 +303,11 @@
//
// For more about specifying packages, see 'go help packages'.
//
-//
-// Show documentation for package or symbol
+// # Show documentation for package or symbol
//
// Usage:
//
-// go doc [doc flags] [package|[package.]symbol[.methodOrField]]
+// go doc [doc flags] [package|[package.]symbol[.methodOrField]]
//
// Doc prints the documentation comments associated with the item identified by its
// arguments (a package, const, func, type, var, method, or struct field)
@@ -321,7 +319,7 @@
//
// Given no arguments, that is, when run as
//
-// go doc
+// go doc
//
// it prints the package documentation for the package in the current directory.
// If the package is a command (package main), the exported symbols of the package
@@ -332,10 +330,10 @@
// on what is installed in GOROOT and GOPATH, as well as the form of the argument,
// which is schematically one of these:
//
-// go doc <pkg>
-// go doc <sym>[.<methodOrField>]
-// go doc [<pkg>.]<sym>[.<methodOrField>]
-// go doc [<pkg>.][<sym>.]<methodOrField>
+// go doc <pkg>
+// go doc <sym>[.<methodOrField>]
+// go doc [<pkg>.]<sym>[.<methodOrField>]
+// go doc [<pkg>.][<sym>.]<methodOrField>
//
// The first item in this list matched by the argument is the one whose documentation
// is printed. (See the examples below.) However, if the argument starts with a capital
@@ -357,7 +355,7 @@
// When run with two arguments, the first is a package path (full path or suffix),
// and the second is a symbol, or symbol with method or struct field:
//
-// go doc <pkg> <sym>[.<methodOrField>]
+// go doc <pkg> <sym>[.<methodOrField>]
//
// In all forms, when matching symbols, lower-case letters in the argument match
// either case but upper-case letters match exactly. This means that there may be
@@ -365,68 +363,69 @@
// different cases. If this occurs, documentation for all matches is printed.
//
// Examples:
-// go doc
-// Show documentation for current package.
-// go doc Foo
-// Show documentation for Foo in the current package.
-// (Foo starts with a capital letter so it cannot match
-// a package path.)
-// go doc encoding/json
-// Show documentation for the encoding/json package.
-// go doc json
-// Shorthand for encoding/json.
-// go doc json.Number (or go doc json.number)
-// Show documentation and method summary for json.Number.
-// go doc json.Number.Int64 (or go doc json.number.int64)
-// Show documentation for json.Number's Int64 method.
-// go doc cmd/doc
-// Show package docs for the doc command.
-// go doc -cmd cmd/doc
-// Show package docs and exported symbols within the doc command.
-// go doc template.new
-// Show documentation for html/template's New function.
-// (html/template is lexically before text/template)
-// go doc text/template.new # One argument
-// Show documentation for text/template's New function.
-// go doc text/template new # Two arguments
-// Show documentation for text/template's New function.
-//
-// At least in the current tree, these invocations all print the
-// documentation for json.Decoder's Decode method:
-//
-// go doc json.Decoder.Decode
-// go doc json.decoder.decode
-// go doc json.decode
-// cd go/src/encoding/json; go doc decode
+//
+// go doc
+// Show documentation for current package.
+// go doc Foo
+// Show documentation for Foo in the current package.
+// (Foo starts with a capital letter so it cannot match
+// a package path.)
+// go doc encoding/json
+// Show documentation for the encoding/json package.
+// go doc json
+// Shorthand for encoding/json.
+// go doc json.Number (or go doc json.number)
+// Show documentation and method summary for json.Number.
+// go doc json.Number.Int64 (or go doc json.number.int64)
+// Show documentation for json.Number's Int64 method.
+// go doc cmd/doc
+// Show package docs for the doc command.
+// go doc -cmd cmd/doc
+// Show package docs and exported symbols within the doc command.
+// go doc template.new
+// Show documentation for html/template's New function.
+// (html/template is lexically before text/template)
+// go doc text/template.new # One argument
+// Show documentation for text/template's New function.
+// go doc text/template new # Two arguments
+// Show documentation for text/template's New function.
+//
+// At least in the current tree, these invocations all print the
+// documentation for json.Decoder's Decode method:
+//
+// go doc json.Decoder.Decode
+// go doc json.decoder.decode
+// go doc json.decode
+// cd go/src/encoding/json; go doc decode
//
// Flags:
-// -all
-// Show all the documentation for the package.
-// -c
-// Respect case when matching symbols.
-// -cmd
-// Treat a command (package main) like a regular package.
-// Otherwise package main's exported symbols are hidden
-// when showing the package's top-level documentation.
-// -short
-// One-line representation for each symbol.
-// -src
-// Show the full source code for the symbol. This will
-// display the full Go source of its declaration and
-// definition, such as a function definition (including
-// the body), type declaration or enclosing const
-// block. The output may therefore include unexported
-// details.
-// -u
-// Show documentation for unexported as well as exported
-// symbols, methods, and fields.
-//
-//
-// Print Go environment information
+//
+// -all
+// Show all the documentation for the package.
+// -c
+// Respect case when matching symbols.
+// -cmd
+// Treat a command (package main) like a regular package.
+// Otherwise package main's exported symbols are hidden
+// when showing the package's top-level documentation.
+// -short
+// One-line representation for each symbol.
+// -src
+// Show the full source code for the symbol. This will
+// display the full Go source of its declaration and
+// definition, such as a function definition (including
+// the body), type declaration or enclosing const
+// block. The output may therefore include unexported
+// details.
+// -u
+// Show documentation for unexported as well as exported
+// symbols, methods, and fields.
+//
+// # Print Go environment information
//
// Usage:
//
-// go env [-json] [-u] [-w] [var ...]
+// go env [-json] [-u] [-w] [var ...]
//
// Env prints Go environment information.
//
@@ -448,12 +447,11 @@
//
// For more about environment variables, see 'go help environment'.
//
-//
-// Update packages to use new APIs
+// # Update packages to use new APIs
//
// Usage:
//
-// go fix [-fix list] [packages]
+// go fix [-fix list] [packages]
//
// Fix runs the Go fix command on the packages named by the import paths.
//
@@ -468,12 +466,11 @@
//
// See also: go fmt, go vet.
//
-//
-// Gofmt (reformat) package sources
+// # Gofmt (reformat) package sources
//
// Usage:
//
-// go fmt [-n] [-x] [packages]
+// go fmt [-n] [-x] [packages]
//
// Fmt runs the command 'gofmt -l -w' on the packages named
// by the import paths. It prints the names of the files that are modified.
@@ -491,12 +488,11 @@
//
// See also: go fix, go vet.
//
-//
-// Generate Go files by processing source
+// # Generate Go files by processing source
//
// Usage:
//
-// go generate [-run regexp] [-n] [-v] [-x] [build flags] [file.go... | packages]
+// go generate [-run regexp] [-n] [-v] [-x] [build flags] [file.go... | packages]
//
// Generate runs commands described by directives within existing
// files. Those commands can run any process but the intent is to
@@ -508,7 +504,7 @@
// Go generate scans the file for directives, which are lines of
// the form,
//
-// //go:generate command argument...
+// //go:generate command argument...
//
// (note: no leading spaces and no space in "//go") where command
// is the generator to be run, corresponding to an executable file
@@ -531,25 +527,28 @@
// generated source should have a line that matches the following
// regular expression (in Go syntax):
//
-// ^// Code generated .* DO NOT EDIT\.$
+// ^// Code generated .* DO NOT EDIT\.$
//
// This line must appear before the first non-comment, non-blank
// text in the file.
//
// 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.
-// $GOLINE
-// The line number of the directive in the source file.
-// $GOPACKAGE
-// The name of the package of the file containing the directive.
-// $DOLLAR
-// A dollar sign.
+// $GOARCH
+// The execution architecture (arm, amd64, etc.)
+// $GOOS
+// The execution operating system (linux, windows, etc.)
+// $GOFILE
+// The base name of the file.
+// $GOLINE
+// The line number of the directive in the source file.
+// $GOPACKAGE
+// The name of the package of the file containing the directive.
+// $GOROOT
+// The GOROOT directory for the 'go' command that invoked the
+// generator, containing the Go toolchain and standard library.
+// $DOLLAR
+// A dollar sign.
//
// Other than variable substitution and quoted-string evaluation, no
// special processing such as "globbing" is performed on the command
@@ -565,14 +564,14 @@
//
// A directive of the form,
//
-// //go:generate -command xxx args...
+// //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 foo go tool foo
+// //go:generate -command foo go tool foo
//
// specifies that the command "foo" represents the generator
// "go tool foo".
@@ -596,11 +595,11 @@
//
// Go generate accepts one specific flag:
//
-// -run=""
-// if non-empty, specifies a regular expression to select
-// directives whose full original source text (excluding
-// any trailing spaces and final newline) matches the
-// expression.
+// -run=""
+// if non-empty, specifies a regular expression to select
+// directives whose full original source text (excluding
+// any trailing spaces and final newline) matches the
+// expression.
//
// It also accepts the standard build flags including -v, -n, and -x.
// The -v flag prints the names of packages and files as they are
@@ -612,12 +611,11 @@
//
// For more about specifying packages, see 'go help packages'.
//
-//
-// Add dependencies to current module and install them
+// # Add dependencies to current module and install them
//
// Usage:
//
-// go get [-t] [-u] [-v] [build flags] [packages]
+// go get [-t] [-u] [-v] [build flags] [packages]
//
// Get resolves its command-line arguments to packages at specific module versions,
// updates go.mod to require those versions, and downloads source code into the
@@ -625,15 +623,15 @@
//
// To add a dependency for a package or upgrade it to its latest version:
//
-// go get example.com/pkg
+// go get example.com/pkg
//
// To upgrade or downgrade a package to a specific version:
//
-// go get example.com/pkg@v1.2.3
+// go get example.com/pkg@v1.2.3
//
// To remove a dependency on a module and downgrade modules that require it:
//
-// go get example.com/mod@none
+// go get example.com/mod@none
//
// See https://golang.org/ref/mod#go-get for details.
//
@@ -643,8 +641,8 @@
// 'go install' runs in module-aware mode and ignores the go.mod file in the
// current directory. For example:
//
-// go install example.com/pkg@v1.2.3
-// go install example.com/pkg@latest
+// go install example.com/pkg@v1.2.3
+// go install example.com/pkg@latest
//
// See 'go help install' or https://golang.org/ref/mod#go-install for details.
//
@@ -678,12 +676,11 @@
//
// See also: go build, go install, go clean, go mod.
//
-//
-// Compile and install packages and dependencies
+// # Compile and install packages and dependencies
//
// Usage:
//
-// go install [build flags] [packages]
+// go install [build flags] [packages]
//
// Install compiles and installs the packages named by the import paths.
//
@@ -738,12 +735,11 @@
//
// See also: go build, go get, go clean.
//
-//
-// List packages or modules
+// # List packages or modules
//
// Usage:
//
-// go list [-f format] [-json] [-m] [list flags] [build flags] [packages]
+// go list [-f format] [-json] [-m] [list flags] [build flags] [packages]
//
// List lists the named packages, one per line.
// The most commonly-used flags are -f and -json, which control the form
@@ -752,83 +748,83 @@
//
// The default output shows the package import path:
//
-// bytes
-// encoding/json
-// github.com/gorilla/mux
-// golang.org/x/net/html
+// bytes
+// encoding/json
+// github.com/gorilla/mux
+// golang.org/x/net/html
//
// The -f flag specifies an alternate format for the list, using the
// syntax of package template. The default output is equivalent
// to -f '{{.ImportPath}}'. The struct being passed to the template is:
//
-// type Package struct {
-// Dir string // directory containing package sources
-// ImportPath string // import path of package in dir
-// ImportComment string // path in import comment on package statement
-// Name string // package name
-// Doc string // package documentation string
-// Target string // install path
-// Shlib string // the shared library that contains this package (only set when -linkshared)
-// Goroot bool // is this package in the Go root?
-// Standard bool // is this package part of the standard Go library?
-// Stale bool // would 'go install' do anything for this package?
-// StaleReason string // explanation for Stale==true
-// Root string // Go root or Go path dir containing this package
-// ConflictDir string // this directory shadows Dir in $GOPATH
-// BinaryOnly bool // binary-only package (no longer supported)
-// ForTest string // package is only for use in named test
-// Export string // file containing export data (when using -export)
-// BuildID string // build ID of the compiled package (when using -export)
-// Module *Module // info about package's containing module, if any (can be nil)
-// Match []string // command-line patterns matching this package
-// DepOnly bool // package is only a dependency, not explicitly listed
-//
-// // Source files
-// GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
-// CgoFiles []string // .go source files that import "C"
-// CompiledGoFiles []string // .go files presented to compiler (when using -compiled)
-// IgnoredGoFiles []string // .go source files ignored due to build constraints
-// IgnoredOtherFiles []string // non-.go source files ignored due to build constraints
-// CFiles []string // .c source files
-// CXXFiles []string // .cc, .cxx and .cpp source files
-// MFiles []string // .m source files
-// HFiles []string // .h, .hh, .hpp and .hxx source files
-// FFiles []string // .f, .F, .for and .f90 Fortran source files
-// SFiles []string // .s source files
-// SwigFiles []string // .swig files
-// SwigCXXFiles []string // .swigcxx files
-// SysoFiles []string // .syso object files to add to archive
-// TestGoFiles []string // _test.go files in package
-// XTestGoFiles []string // _test.go files outside package
-//
-// // Embedded files
-// EmbedPatterns []string // //go:embed patterns
-// EmbedFiles []string // files matched by EmbedPatterns
-// TestEmbedPatterns []string // //go:embed patterns in TestGoFiles
-// TestEmbedFiles []string // files matched by TestEmbedPatterns
-// XTestEmbedPatterns []string // //go:embed patterns in XTestGoFiles
-// XTestEmbedFiles []string // files matched by XTestEmbedPatterns
-//
-// // Cgo directives
-// CgoCFLAGS []string // cgo: flags for C compiler
-// CgoCPPFLAGS []string // cgo: flags for C preprocessor
-// CgoCXXFLAGS []string // cgo: flags for C++ compiler
-// CgoFFLAGS []string // cgo: flags for Fortran compiler
-// CgoLDFLAGS []string // cgo: flags for linker
-// CgoPkgConfig []string // cgo: pkg-config names
-//
-// // Dependency information
-// Imports []string // import paths used by this package
-// ImportMap map[string]string // map from source import to ImportPath (identity entries omitted)
-// Deps []string // all (recursively) imported dependencies
-// TestImports []string // imports from TestGoFiles
-// XTestImports []string // imports from XTestGoFiles
-//
-// // Error information
-// Incomplete bool // this package or a dependency has an error
-// Error *PackageError // error loading package
-// DepsErrors []*PackageError // errors loading dependencies
-// }
+// type Package struct {
+// Dir string // directory containing package sources
+// ImportPath string // import path of package in dir
+// ImportComment string // path in import comment on package statement
+// Name string // package name
+// Doc string // package documentation string
+// Target string // install path
+// Shlib string // the shared library that contains this package (only set when -linkshared)
+// Goroot bool // is this package in the Go root?
+// Standard bool // is this package part of the standard Go library?
+// Stale bool // would 'go install' do anything for this package?
+// StaleReason string // explanation for Stale==true
+// Root string // Go root or Go path dir containing this package
+// ConflictDir string // this directory shadows Dir in $GOPATH
+// BinaryOnly bool // binary-only package (no longer supported)
+// ForTest string // package is only for use in named test
+// Export string // file containing export data (when using -export)
+// BuildID string // build ID of the compiled package (when using -export)
+// Module *Module // info about package's containing module, if any (can be nil)
+// Match []string // command-line patterns matching this package
+// DepOnly bool // package is only a dependency, not explicitly listed
+//
+// // Source files
+// GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
+// CgoFiles []string // .go source files that import "C"
+// CompiledGoFiles []string // .go files presented to compiler (when using -compiled)
+// IgnoredGoFiles []string // .go source files ignored due to build constraints
+// IgnoredOtherFiles []string // non-.go source files ignored due to build constraints
+// CFiles []string // .c source files
+// CXXFiles []string // .cc, .cxx and .cpp source files
+// MFiles []string // .m source files
+// HFiles []string // .h, .hh, .hpp and .hxx source files
+// FFiles []string // .f, .F, .for and .f90 Fortran source files
+// SFiles []string // .s source files
+// SwigFiles []string // .swig files
+// SwigCXXFiles []string // .swigcxx files
+// SysoFiles []string // .syso object files to add to archive
+// TestGoFiles []string // _test.go files in package
+// XTestGoFiles []string // _test.go files outside package
+//
+// // Embedded files
+// EmbedPatterns []string // //go:embed patterns
+// EmbedFiles []string // files matched by EmbedPatterns
+// TestEmbedPatterns []string // //go:embed patterns in TestGoFiles
+// TestEmbedFiles []string // files matched by TestEmbedPatterns
+// XTestEmbedPatterns []string // //go:embed patterns in XTestGoFiles
+// XTestEmbedFiles []string // files matched by XTestEmbedPatterns
+//
+// // Cgo directives
+// CgoCFLAGS []string // cgo: flags for C compiler
+// CgoCPPFLAGS []string // cgo: flags for C preprocessor
+// CgoCXXFLAGS []string // cgo: flags for C++ compiler
+// CgoFFLAGS []string // cgo: flags for Fortran compiler
+// CgoLDFLAGS []string // cgo: flags for linker
+// CgoPkgConfig []string // cgo: pkg-config names
+//
+// // Dependency information
+// Imports []string // import paths used by this package
+// ImportMap map[string]string // map from source import to ImportPath (identity entries omitted)
+// Deps []string // all (recursively) imported dependencies
+// TestImports []string // imports from TestGoFiles
+// XTestImports []string // imports from XTestGoFiles
+//
+// // Error information
+// Incomplete bool // this package or a dependency has an error
+// Error *PackageError // error loading package
+// DepsErrors []*PackageError // errors loading dependencies
+// }
//
// Packages stored in vendor directories report an ImportPath that includes the
// path to the vendor directory (for example, "d/vendor/p" instead of "p"),
@@ -838,11 +834,11 @@
//
// The error information, if any, is
//
-// type PackageError struct {
-// ImportStack []string // shortest path from package named on command line to this one
-// Pos string // position of error (if present, file:line:col)
-// Err string // the error itself
-// }
+// type PackageError struct {
+// ImportStack []string // shortest path from package named on command line to this one
+// Pos string // position of error (if present, file:line:col)
+// Err string // the error itself
+// }
//
// The module information is a Module struct, defined in the discussion
// of list -m below.
@@ -851,19 +847,19 @@
//
// The template function "context" returns the build context, defined as:
//
-// type Context struct {
-// GOARCH string // target architecture
-// GOOS string // target operating system
-// GOROOT string // Go root
-// GOPATH string // Go path
-// CgoEnabled bool // whether cgo can be used
-// UseAllFiles bool // use files regardless of +build lines, file names
-// Compiler string // compiler to assume when computing target paths
-// BuildTags []string // build constraints to match in +build lines
-// ToolTags []string // toolchain-specific build constraints
-// ReleaseTags []string // releases the current release is compatible with
-// InstallSuffix string // suffix to use in the name of the install dir
-// }
+// type Context struct {
+// GOARCH string // target architecture
+// GOOS string // target operating system
+// GOROOT string // Go root
+// GOPATH string // Go path
+// CgoEnabled bool // whether cgo can be used
+// UseAllFiles bool // use files regardless of +build lines, file names
+// Compiler string // compiler to assume when computing target paths
+// BuildTags []string // build constraints to match in +build lines
+// ToolTags []string // toolchain-specific build constraints
+// ReleaseTags []string // releases the current release is compatible with
+// InstallSuffix string // suffix to use in the name of the install dir
+// }
//
// For more information about the meaning of these fields see the documentation
// for the go/build package's Context type.
@@ -930,25 +926,25 @@
// When listing modules, the -f flag still specifies a format template
// applied to a Go struct, but now a Module struct:
//
-// type Module struct {
-// Path string // module path
-// Version string // module version
-// Versions []string // available module versions (with -versions)
-// Replace *Module // replaced by this module
-// Time *time.Time // time version was created
-// Update *Module // available update, if any (with -u)
-// Main bool // is this the main module?
-// Indirect bool // is this module only an indirect dependency of main module?
-// Dir string // directory holding files for this module, if any
-// GoMod string // path to go.mod file used when loading this module, if any
-// GoVersion string // go version used in module
-// Retracted string // retraction information, if any (with -retracted or -u)
-// Error *ModuleError // error loading module
-// }
-//
-// type ModuleError struct {
-// Err string // the error itself
-// }
+// type Module struct {
+// Path string // module path
+// Version string // module version
+// Versions []string // available module versions (with -versions)
+// Replace *Module // replaced by this module
+// Time *time.Time // time version was created
+// Update *Module // available update, if any (with -u)
+// Main bool // is this the main module?
+// Indirect bool // is this module only an indirect dependency of main module?
+// Dir string // directory holding files for this module, if any
+// GoMod string // path to go.mod file used when loading this module, if any
+// GoVersion string // go version used in module
+// Retracted string // retraction information, if any (with -retracted or -u)
+// Error *ModuleError // error loading module
+// }
+//
+// type ModuleError struct {
+// Err string // the error itself
+// }
//
// The file GoMod refers to may be outside the module directory if the
// module is in the module cache or if the -modfile flag is used.
@@ -957,9 +953,9 @@
// information about the version and replacement if any.
// For example, 'go list -m all' might print:
//
-// my/main/module
-// golang.org/x/text v0.3.0 => /tmp/text
-// rsc.io/pdf v0.1.1
+// my/main/module
+// golang.org/x/text v0.3.0 => /tmp/text
+// rsc.io/pdf v0.1.1
//
// The Module struct has a String method that formats this
// line of output, so that the default format is equivalent
@@ -981,9 +977,9 @@
// If a version is retracted, the string "(retracted)" will follow it.
// For example, 'go list -m -u all' might print:
//
-// my/main/module
-// golang.org/x/text v0.3.0 [v0.4.0] => /tmp/text
-// rsc.io/pdf v0.1.1 (retracted) [v0.1.2]
+// my/main/module
+// golang.org/x/text v0.3.0 [v0.4.0] => /tmp/text
+// rsc.io/pdf v0.1.1 (retracted) [v0.1.2]
//
// (For tools, 'go list -m -u -json all' may be more convenient to parse.)
//
@@ -1026,8 +1022,7 @@
//
// For more about modules, see https://golang.org/ref/mod.
//
-//
-// Module maintenance
+// # Module maintenance
//
// Go mod provides access to operations on modules.
//
@@ -1038,26 +1033,26 @@
//
// Usage:
//
-// go mod <command> [arguments]
+// go mod <command> [arguments]
//
// The commands are:
//
-// download download modules to local cache
-// edit edit go.mod from tools or scripts
-// graph print module requirement graph
-// init initialize new module in current directory
-// tidy add missing and remove unused modules
-// vendor make vendored copy of dependencies
-// verify verify dependencies have expected content
-// why explain why packages or modules are needed
+// download download modules to local cache
+// edit edit go.mod from tools or scripts
+// graph print module requirement graph
+// init initialize new module in current directory
+// tidy add missing and remove unused modules
+// vendor make vendored copy of dependencies
+// verify verify dependencies have expected content
+// why explain why packages or modules are needed
//
// Use "go help mod <command>" for more information about a command.
//
-// Download modules to local cache
+// # Download modules to local cache
//
// Usage:
//
-// go mod download [-x] [-json] [modules]
+// go mod download [-x] [-json] [modules]
//
// Download downloads the named modules, which can be module patterns selecting
// dependencies of the main module or module queries of the form path@version.
@@ -1078,17 +1073,17 @@
// to standard output, describing each downloaded module (or failure),
// corresponding to this Go struct:
//
-// type Module struct {
-// Path string // module path
-// Version string // module version
-// Error string // error loading module
-// Info string // absolute path to cached .info file
-// GoMod string // absolute path to cached .mod file
-// Zip string // absolute path to cached .zip file
-// Dir string // absolute path to cached source root directory
-// Sum string // checksum for path, version (as in go.sum)
-// GoModSum string // checksum for go.mod (as in go.sum)
-// }
+// type Module struct {
+// Path string // module path
+// Version string // module version
+// Error string // error loading module
+// Info string // absolute path to cached .info file
+// GoMod string // absolute path to cached .mod file
+// Zip string // absolute path to cached .zip file
+// Dir string // absolute path to cached source root directory
+// Sum string // checksum for path, version (as in go.sum)
+// GoModSum string // checksum for go.mod (as in go.sum)
+// }
//
// The -x flag causes download to print the commands download executes.
//
@@ -1096,12 +1091,11 @@
//
// See https://golang.org/ref/mod#version-queries for more about version queries.
//
-//
-// Edit go.mod from tools or scripts
+// # Edit go.mod from tools or scripts
//
// Usage:
//
-// go mod edit [editing flags] [-fmt|-print|-json] [go.mod]
+// go mod edit [editing flags] [-fmt|-print|-json] [go.mod]
//
// Edit provides a command-line interface for editing go.mod,
// for use primarily by tools or scripts. It reads only go.mod;
@@ -1159,41 +1153,41 @@
// The -json flag prints the final go.mod file in JSON format instead of
// writing it back to go.mod. The JSON output corresponds to these Go types:
//
-// type Module struct {
-// Path string
-// Version string
-// }
-//
-// type GoMod struct {
-// Module ModPath
-// Go string
-// Require []Require
-// Exclude []Module
-// Replace []Replace
-// Retract []Retract
-// }
-//
-// type ModPath struct {
-// Path string
-// Deprecated string
-// }
-//
-// type Require struct {
-// Path string
-// Version string
-// Indirect bool
-// }
-//
-// type Replace struct {
-// Old Module
-// New Module
-// }
-//
-// type Retract struct {
-// Low string
-// High string
-// Rationale string
-// }
+// type Module struct {
+// Path string
+// Version string
+// }
+//
+// type GoMod struct {
+// Module ModPath
+// Go string
+// Require []Require
+// Exclude []Module
+// Replace []Replace
+// Retract []Retract
+// }
+//
+// type ModPath struct {
+// Path string
+// Deprecated string
+// }
+//
+// type Require struct {
+// Path string
+// Version string
+// Indirect bool
+// }
+//
+// type Replace struct {
+// Old Module
+// New Module
+// }
+//
+// type Retract struct {
+// Low string
+// High string
+// Rationale string
+// }
//
// Retract entries representing a single version (not an interval) will have
// the "Low" and "High" fields set to the same value.
@@ -1204,12 +1198,11 @@
//
// See https://golang.org/ref/mod#go-mod-edit for more about 'go mod edit'.
//
-//
-// Print module requirement graph
+// # Print module requirement graph
//
// Usage:
//
-// go mod graph [-go=version]
+// go mod graph [-go=version]
//
// Graph prints the module requirement graph (with replacements applied)
// in text form. Each line in the output has two space-separated fields: a module
@@ -1222,12 +1215,11 @@
//
// See https://golang.org/ref/mod#go-mod-graph for more about 'go mod graph'.
//
-//
-// Initialize new module in current directory
+// # Initialize new module in current directory
//
// Usage:
//
-// go mod init [module-path]
+// go mod init [module-path]
//
// Init initializes and writes a new go.mod file in the current directory, in
// effect creating a new module rooted at the current directory. The go.mod file
@@ -1243,12 +1235,11 @@
//
// See https://golang.org/ref/mod#go-mod-init for more about 'go mod init'.
//
-//
-// Add missing and remove unused modules
+// # Add missing and remove unused modules
//
// Usage:
//
-// go mod tidy [-e] [-v] [-go=version] [-compat=version]
+// go mod tidy [-e] [-v] [-go=version] [-compat=version]
//
// Tidy makes sure go.mod matches the source code in the module.
// It adds any missing modules necessary to build the current module's
@@ -1278,12 +1269,11 @@
//
// See https://golang.org/ref/mod#go-mod-tidy for more about 'go mod tidy'.
//
-//
-// Make vendored copy of dependencies
+// # Make vendored copy of dependencies
//
// Usage:
//
-// go mod vendor [-e] [-v] [-o outdir]
+// go mod vendor [-e] [-v] [-o outdir]
//
// Vendor resets the main module's vendor directory to include all packages
// needed to build and test all the main module's packages.
@@ -1302,12 +1292,11 @@
//
// See https://golang.org/ref/mod#go-mod-vendor for more about 'go mod vendor'.
//
-//
-// Verify dependencies have expected content
+// # Verify dependencies have expected content
//
// Usage:
//
-// go mod verify
+// go mod verify
//
// Verify checks that the dependencies of the current module,
// which are stored in a local downloaded source cache, have not been
@@ -1318,12 +1307,11 @@
//
// See https://golang.org/ref/mod#go-mod-verify for more about 'go mod verify'.
//
-//
-// Explain why packages or modules are needed
+// # Explain why packages or modules are needed
//
// Usage:
//
-// go mod why [-m] [-vendor] packages...
+// go mod why [-m] [-vendor] packages...
//
// Why shows a shortest path in the import graph from the main module to
// each of the listed packages. If the -m flag is given, why treats the
@@ -1344,20 +1332,19 @@
//
// For example:
//
-// $ go mod why golang.org/x/text/language golang.org/x/text/encoding
-// # golang.org/x/text/language
-// rsc.io/quote
-// rsc.io/sampler
-// golang.org/x/text/language
+// $ go mod why golang.org/x/text/language golang.org/x/text/encoding
+// # golang.org/x/text/language
+// rsc.io/quote
+// rsc.io/sampler
+// golang.org/x/text/language
//
-// # golang.org/x/text/encoding
-// (main module does not need package golang.org/x/text/encoding)
-// $
+// # golang.org/x/text/encoding
+// (main module does not need package golang.org/x/text/encoding)
+// $
//
// See https://golang.org/ref/mod#go-mod-why for more about 'go mod why'.
//
-//
-// Workspace maintenance
+// # Workspace maintenance
//
// Work provides access to operations on workspaces.
//
@@ -1382,20 +1369,20 @@
// go.work files are line-oriented. Each line holds a single directive,
// made up of a keyword followed by arguments. For example:
//
-// go 1.18
+// go 1.18
//
-// use ../foo/bar
-// use ./baz
+// use ../foo/bar
+// use ./baz
//
-// replace example.com/foo v1.2.3 => example.com/bar v1.4.5
+// replace example.com/foo v1.2.3 => example.com/bar v1.4.5
//
// The leading keyword can be factored out of adjacent lines to create a block,
// like in Go imports.
//
-// use (
-// ../foo/bar
-// ./baz
-// )
+// use (
+// ../foo/bar
+// ./baz
+// )
//
// The use directive specifies a module to be included in the workspace's
// set of main modules. The argument to the use directive is the directory
@@ -1417,22 +1404,22 @@
//
// Usage:
//
-// go work <command> [arguments]
+// go work <command> [arguments]
//
// The commands are:
//
-// edit edit go.work from tools or scripts
-// init initialize workspace file
-// sync sync workspace build list to modules
-// use add modules to workspace file
+// edit edit go.work from tools or scripts
+// init initialize workspace file
+// sync sync workspace build list to modules
+// use add modules to workspace file
//
// Use "go help work <command>" for more information about a command.
//
-// Edit go.work from tools or scripts
+// # Edit go.work from tools or scripts
//
// Usage:
//
-// go work edit [editing flags] [go.work]
+// go work edit [editing flags] [go.work]
//
// Edit provides a command-line interface for editing go.work,
// for use primarily by tools or scripts. It only reads go.work;
@@ -1473,36 +1460,35 @@
// The -json flag prints the final go.work file in JSON format instead of
// writing it back to go.mod. The JSON output corresponds to these Go types:
//
-// type GoWork struct {
-// Go string
-// Use []Use
-// Replace []Replace
-// }
+// type GoWork struct {
+// Go string
+// Use []Use
+// Replace []Replace
+// }
//
-// type Use struct {
-// DiskPath string
-// ModulePath string
-// }
+// type Use struct {
+// DiskPath string
+// ModulePath string
+// }
//
-// type Replace struct {
-// Old Module
-// New Module
-// }
+// type Replace struct {
+// Old Module
+// New Module
+// }
//
-// type Module struct {
-// Path string
-// Version string
-// }
+// type Module struct {
+// Path string
+// Version string
+// }
//
// See the workspaces reference at https://go.dev/ref/mod#workspaces
// for more information.
//
-//
-// Initialize workspace file
+// # Initialize workspace file
//
// Usage:
//
-// go work init [moddirs]
+// go work init [moddirs]
//
// Init initializes and writes a new go.work file in the
// current directory, in effect creating a new workspace at the current
@@ -1518,12 +1504,11 @@
// See the workspaces reference at https://go.dev/ref/mod#workspaces
// for more information.
//
-//
-// Sync workspace build list to modules
+// # Sync workspace build list to modules
//
// Usage:
//
-// go work sync
+// go work sync
//
// Sync syncs the workspace's build list back to the
// workspace's modules
@@ -1544,12 +1529,11 @@
// See the workspaces reference at https://go.dev/ref/mod#workspaces
// for more information.
//
-//
-// Add modules to workspace file
+// # Add modules to workspace file
//
// Usage:
//
-// go work use [-r] moddirs
+// go work use [-r] moddirs
//
// Use provides a command-line interface for adding
// directories, optionally recursively, to a go.work file.
@@ -1566,12 +1550,11 @@
// See the workspaces reference at https://go.dev/ref/mod#workspaces
// for more information.
//
-//
-// Compile and run Go program
+// # Compile and run Go program
//
// Usage:
//
-// go run [build flags] [-exec xprog] package [arguments...]
+// go run [build flags] [-exec xprog] package [arguments...]
//
// Run compiles and runs the named main Go package.
// Typically the package is specified as a list of .go source files from a single
@@ -1591,7 +1574,9 @@
//
// By default, 'go run' runs the compiled binary directly: 'a.out arguments...'.
// If the -exec flag is given, 'go run' invokes the binary using xprog:
-// 'xprog a.out arguments...'.
+//
+// 'xprog a.out arguments...'.
+//
// If the -exec flag is not given, GOOS or GOARCH is different from the system
// default, and a program named go_$GOOS_$GOARCH_exec can be found
// on the current search path, 'go run' invokes the binary using that program,
@@ -1610,20 +1595,19 @@
//
// See also: go build.
//
-//
-// Test packages
+// # Test packages
//
// Usage:
//
-// go test [build/test flags] [packages] [build/test flags & test binary flags]
+// go test [build/test flags] [packages] [build/test flags & test binary flags]
//
// 'Go test' automates testing the packages named by the import paths.
// It prints a summary of the test results in the format:
//
-// ok archive/tar 0.011s
-// FAIL archive/zip 0.022s
-// ok compress/gzip 0.033s
-// ...
+// ok archive/tar 0.011s
+// FAIL archive/zip 0.022s
+// ok compress/gzip 0.033s
+// ...
//
// followed by detailed output for each failed package.
//
@@ -1702,33 +1686,33 @@
//
// In addition to the build flags, the flags handled by 'go test' itself are:
//
-// -args
-// Pass the remainder of the command line (everything after -args)
-// to the test binary, uninterpreted and unchanged.
-// Because this flag consumes the remainder of the command line,
-// the package list (if present) must appear before this flag.
+// -args
+// Pass the remainder of the command line (everything after -args)
+// to the test binary, uninterpreted and unchanged.
+// Because this flag consumes the remainder of the command line,
+// the package list (if present) must appear before this flag.
//
-// -c
-// Compile the test binary to pkg.test but do not run it
-// (where pkg is the last element of the package's import path).
-// The file name can be changed with the -o flag.
+// -c
+// Compile the test binary to pkg.test but do not run it
+// (where pkg is the last element of the package's import path).
+// The file name can be changed with the -o flag.
//
-// -exec xprog
-// Run the test binary using xprog. The behavior is the same as
-// in 'go run'. See 'go help run' for details.
+// -exec xprog
+// Run the test binary using xprog. The behavior is the same as
+// in 'go run'. See 'go help run' for details.
//
-// -i
-// Install packages that are dependencies of the test.
-// Do not run the test.
-// The -i flag is deprecated. Compiled packages are cached automatically.
+// -i
+// Install packages that are dependencies of the test.
+// Do not run the test.
+// The -i flag is deprecated. Compiled packages are cached automatically.
//
-// -json
-// Convert test output to JSON suitable for automated processing.
-// See 'go doc test2json' for the encoding details.
+// -json
+// Convert test output to JSON suitable for automated processing.
+// See 'go doc test2json' for the encoding details.
//
-// -o file
-// Compile the test binary to the named file.
-// The test still runs (unless -c or -i is specified).
+// -o file
+// Compile the test binary to the named file.
+// The test still runs (unless -c or -i is specified).
//
// The test binary also accepts flags that control execution of the test; these
// flags are also accessible by 'go test'. See 'go help testflag' for details.
@@ -1738,12 +1722,11 @@
//
// See also: go build, go vet.
//
-//
-// Run specified go tool
+// # Run specified go tool
//
// Usage:
//
-// go tool [-n] command [args...]
+// go tool [-n] command [args...]
//
// Tool runs the go tool command identified by the arguments.
// With no arguments it prints the list of known tools.
@@ -1753,12 +1736,11 @@
//
// For more about each tool command, see 'go doc cmd/<command>'.
//
-//
-// Print Go version
+// # Print Go version
//
// Usage:
//
-// go version [-m] [-v] [file ...]
+// go version [-m] [-v] [file ...]
//
// Version prints the build information for Go executables.
//
@@ -1780,12 +1762,11 @@
//
// See also: go doc runtime/debug.BuildInfo.
//
-//
-// Report likely mistakes in packages
+// # Report likely mistakes in packages
//
// Usage:
//
-// go vet [-n] [-x] [-vettool prog] [build flags] [vet flags] [packages]
+// go vet [-n] [-x] [-vettool prog] [build flags] [vet flags] [packages]
//
// Vet runs the Go vet command on the packages named by the import paths.
//
@@ -1801,8 +1782,8 @@
// or additional checks.
// For example, the 'shadow' analyzer can be built and run using these commands:
//
-// go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow
-// go vet -vettool=$(which shadow)
+// go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow
+// go vet -vettool=$(which shadow)
//
// The build flags supported by go vet are those that control package resolution
// and execution, such as -n, -x, -v, -tags, and -toolexec.
@@ -1810,12 +1791,11 @@
//
// See also: go fmt, go fix.
//
-//
-// Build constraints
+// # Build constraints
//
// A build constraint, also known as a build tag, is a line comment that begins
//
-// //go:build
+// //go:build
//
// that lists the conditions under which a file should be included in the package.
// Constraints may appear in any kind of source file (not just Go), but
@@ -1834,31 +1814,33 @@
// build when the "linux" and "386" constraints are satisfied, or when
// "darwin" is satisfied and "cgo" is not:
//
-// //go:build (linux && 386) || (darwin && !cgo)
+// //go:build (linux && 386) || (darwin && !cgo)
//
// It is an error for a file to have more than one //go:build line.
//
// During a particular build, the following words are satisfied:
//
-// - the target operating system, as spelled by runtime.GOOS, set with the
-// GOOS environment variable.
-// - the target architecture, as spelled by runtime.GOARCH, set with the
-// GOARCH environment variable.
-// - "unix", if GOOS is a Unix or Unix-like system.
-// - the compiler being used, either "gc" or "gccgo"
-// - "cgo", if the cgo command is supported (see CGO_ENABLED in
-// 'go help environment').
-// - a term for each Go major release, through the current version:
-// "go1.1" from Go version 1.1 onward, "go1.12" from Go 1.12, and so on.
-// - any additional tags given by the -tags flag (see 'go help build').
+// - the target operating system, as spelled by runtime.GOOS, set with the
+// GOOS environment variable.
+// - the target architecture, as spelled by runtime.GOARCH, set with the
+// GOARCH environment variable.
+// - "unix", if GOOS is a Unix or Unix-like system.
+// - the compiler being used, either "gc" or "gccgo"
+// - "cgo", if the cgo command is supported (see CGO_ENABLED in
+// 'go help environment').
+// - a term for each Go major release, through the current version:
+// "go1.1" from Go version 1.1 onward, "go1.12" from Go 1.12, and so on.
+// - any additional tags given by the -tags flag (see 'go help build').
//
// There are no separate build tags for beta or minor releases.
//
// If a file's name, after stripping the extension and a possible _test suffix,
// matches any of the following patterns:
-// *_GOOS
-// *_GOARCH
-// *_GOOS_GOARCH
+//
+// *_GOOS
+// *_GOARCH
+// *_GOOS_GOARCH
+//
// (example: source_windows_amd64.go) where GOOS and GOARCH represent
// any known operating system and architecture values respectively, then
// the file is considered to have an implicit build constraint requiring
@@ -1875,19 +1857,19 @@
//
// To keep a file from being considered for the build:
//
-// //go:build ignore
+// //go:build ignore
//
// (any other unsatisfied word will work as well, but "ignore" is conventional.)
//
// To build a file only when using cgo, and only on Linux and OS X:
//
-// //go:build cgo && (linux || darwin)
+// //go:build cgo && (linux || darwin)
//
// Such a file is usually paired with another file implementing the
// default functionality for other systems, which in this case would
// carry the constraint:
//
-// //go:build !(cgo && (linux || darwin))
+// //go:build !(cgo && (linux || darwin))
//
// Naming a file dns_windows.go will cause it to be included only when
// building the package for Windows; similarly, math_386.s will be included
@@ -1897,57 +1879,55 @@
// with a "// +build" prefix. The gofmt command will add an equivalent //go:build
// constraint when encountering the older syntax.
//
-//
-// Build modes
+// # Build modes
//
// The 'go build' and 'go install' commands take a -buildmode argument which
// indicates which kind of object file is to be built. Currently supported values
// are:
//
-// -buildmode=archive
-// Build the listed non-main packages into .a files. Packages named
-// main are ignored.
-//
-// -buildmode=c-archive
-// Build the listed main package, plus all packages it imports,
-// into a C archive file. The only callable symbols will be those
-// functions exported using a cgo //export comment. Requires
-// exactly one main package to be listed.
-//
-// -buildmode=c-shared
-// Build the listed main package, plus all packages it imports,
-// into a C shared library. The only callable symbols will
-// be those functions exported using a cgo //export comment.
-// Requires exactly one main package to be listed.
-//
-// -buildmode=default
-// Listed main packages are built into executables and listed
-// non-main packages are built into .a files (the default
-// behavior).
-//
-// -buildmode=shared
-// Combine all the listed non-main packages into a single shared
-// library that will be used when building with the -linkshared
-// option. Packages named main are ignored.
-//
-// -buildmode=exe
-// Build the listed main packages and everything they import into
-// executables. Packages not named main are ignored.
-//
-// -buildmode=pie
-// Build the listed main packages and everything they import into
-// position independent executables (PIE). Packages not named
-// main are ignored.
-//
-// -buildmode=plugin
-// Build the listed main packages, plus all packages that they
-// import, into a Go plugin. Packages not named main are ignored.
+// -buildmode=archive
+// Build the listed non-main packages into .a files. Packages named
+// main are ignored.
+//
+// -buildmode=c-archive
+// Build the listed main package, plus all packages it imports,
+// into a C archive file. The only callable symbols will be those
+// functions exported using a cgo //export comment. Requires
+// exactly one main package to be listed.
+//
+// -buildmode=c-shared
+// Build the listed main package, plus all packages it imports,
+// into a C shared library. The only callable symbols will
+// be those functions exported using a cgo //export comment.
+// Requires exactly one main package to be listed.
+//
+// -buildmode=default
+// Listed main packages are built into executables and listed
+// non-main packages are built into .a files (the default
+// behavior).
+//
+// -buildmode=shared
+// Combine all the listed non-main packages into a single shared
+// library that will be used when building with the -linkshared
+// option. Packages named main are ignored.
+//
+// -buildmode=exe
+// Build the listed main packages and everything they import into
+// executables. Packages not named main are ignored.
+//
+// -buildmode=pie
+// Build the listed main packages and everything they import into
+// position independent executables (PIE). Packages not named
+// main are ignored.
+//
+// -buildmode=plugin
+// Build the listed main packages, plus all packages that they
+// import, into a Go plugin. Packages not named main are ignored.
//
// On AIX, when linking a C program that uses a Go archive built with
// -buildmode=c-archive, you must pass -Wl,-bnoobjreorder to the C compiler.
//
-//
-// Calling between Go and C
+// # Calling between Go and C
//
// There are two different ways to call between Go and C/C++ code.
//
@@ -1965,8 +1945,7 @@
// compiler. The CC or CXX environment variables may be set to determine
// the C or C++ compiler, respectively, to use.
//
-//
-// Build and test caching
+// # Build and test caching
//
// The go command caches build outputs for reuse in future builds.
// The default location for cache data is a subdirectory named go-build
@@ -2011,8 +1990,7 @@
// GODEBUG=gocachetest=1 causes the go command to print details of its
// decisions about whether to reuse a cached test result.
//
-//
-// Environment variables
+// # Environment variables
//
// The go command and the tools it invokes consult environment variables
// for configuration. If an environment variable is unset, the go command
@@ -2028,217 +2006,216 @@
//
// General-purpose environment variables:
//
-// GO111MODULE
-// Controls whether the go command runs in module-aware mode or GOPATH mode.
-// May be "off", "on", or "auto".
-// See https://golang.org/ref/mod#mod-commands.
-// GCCGO
-// The gccgo command to run for 'go build -compiler=gccgo'.
-// GOARCH
-// The architecture, or processor, for which to compile code.
-// Examples are amd64, 386, arm, ppc64.
-// GOBIN
-// The directory where 'go install' will install a command.
-// GOCACHE
-// The directory where the go command will store cached
-// information for reuse in future builds.
-// GOMODCACHE
-// The directory where the go command will store downloaded modules.
-// GODEBUG
-// Enable various debugging facilities. See 'go doc runtime'
-// for details.
-// GOENV
-// The location of the Go environment configuration file.
-// Cannot be set using 'go env -w'.
-// Setting GOENV=off in the environment disables the use of the
-// default configuration file.
-// GOFLAGS
-// A space-separated list of -flag=value settings to apply
-// to go commands by default, when the given flag is known by
-// the current command. Each entry must be a standalone flag.
-// Because the entries are space-separated, flag values must
-// not contain spaces. Flags listed on the command line
-// are applied after this list and therefore override it.
-// GOINSECURE
-// Comma-separated list of glob patterns (in the syntax of Go's path.Match)
-// of module path prefixes that should always be fetched in an insecure
-// manner. Only applies to dependencies that are being fetched directly.
-// GOINSECURE does not disable checksum database validation. GOPRIVATE or
-// GONOSUMDB may be used to achieve that.
-// GOOS
-// The operating system for which to compile code.
-// Examples are linux, darwin, windows, netbsd.
-// GOPATH
-// For more details see: 'go help gopath'.
-// GOPROXY
-// URL of Go module proxy. See https://golang.org/ref/mod#environment-variables
-// and https://golang.org/ref/mod#module-proxy for details.
-// GOPRIVATE, GONOPROXY, GONOSUMDB
-// Comma-separated list of glob patterns (in the syntax of Go's path.Match)
-// of module path prefixes that should always be fetched directly
-// or that should not be compared against the checksum database.
-// See https://golang.org/ref/mod#private-modules.
-// GOROOT
-// The root of the go tree.
-// GOSUMDB
-// The name of checksum database to use and optionally its public key and
-// URL. See https://golang.org/ref/mod#authenticating.
-// GOTMPDIR
-// The directory where the go command will write
-// temporary source files, packages, and binaries.
-// GOVCS
-// Lists version control commands that may be used with matching servers.
-// See 'go help vcs'.
-// GOWORK
-// In module aware mode, use the given go.work file as a workspace file.
-// By default or when GOWORK is "auto", the go command searches for a
-// file named go.work in the current directory and then containing directories
-// until one is found. If a valid go.work file is found, the modules
-// specified will collectively be used as the main modules. If GOWORK
-// is "off", or a go.work file is not found in "auto" mode, workspace
-// mode is disabled.
+// GO111MODULE
+// Controls whether the go command runs in module-aware mode or GOPATH mode.
+// May be "off", "on", or "auto".
+// See https://golang.org/ref/mod#mod-commands.
+// GCCGO
+// The gccgo command to run for 'go build -compiler=gccgo'.
+// GOARCH
+// The architecture, or processor, for which to compile code.
+// Examples are amd64, 386, arm, ppc64.
+// GOBIN
+// The directory where 'go install' will install a command.
+// GOCACHE
+// The directory where the go command will store cached
+// information for reuse in future builds.
+// GOMODCACHE
+// The directory where the go command will store downloaded modules.
+// GODEBUG
+// Enable various debugging facilities. See 'go doc runtime'
+// for details.
+// GOENV
+// The location of the Go environment configuration file.
+// Cannot be set using 'go env -w'.
+// Setting GOENV=off in the environment disables the use of the
+// default configuration file.
+// GOFLAGS
+// A space-separated list of -flag=value settings to apply
+// to go commands by default, when the given flag is known by
+// the current command. Each entry must be a standalone flag.
+// Because the entries are space-separated, flag values must
+// not contain spaces. Flags listed on the command line
+// are applied after this list and therefore override it.
+// GOINSECURE
+// Comma-separated list of glob patterns (in the syntax of Go's path.Match)
+// of module path prefixes that should always be fetched in an insecure
+// manner. Only applies to dependencies that are being fetched directly.
+// GOINSECURE does not disable checksum database validation. GOPRIVATE or
+// GONOSUMDB may be used to achieve that.
+// GOOS
+// The operating system for which to compile code.
+// Examples are linux, darwin, windows, netbsd.
+// GOPATH
+// For more details see: 'go help gopath'.
+// GOPROXY
+// URL of Go module proxy. See https://golang.org/ref/mod#environment-variables
+// and https://golang.org/ref/mod#module-proxy for details.
+// GOPRIVATE, GONOPROXY, GONOSUMDB
+// Comma-separated list of glob patterns (in the syntax of Go's path.Match)
+// of module path prefixes that should always be fetched directly
+// or that should not be compared against the checksum database.
+// See https://golang.org/ref/mod#private-modules.
+// GOROOT
+// The root of the go tree.
+// GOSUMDB
+// The name of checksum database to use and optionally its public key and
+// URL. See https://golang.org/ref/mod#authenticating.
+// GOTMPDIR
+// The directory where the go command will write
+// temporary source files, packages, and binaries.
+// GOVCS
+// Lists version control commands that may be used with matching servers.
+// See 'go help vcs'.
+// GOWORK
+// In module aware mode, use the given go.work file as a workspace file.
+// By default or when GOWORK is "auto", the go command searches for a
+// file named go.work in the current directory and then containing directories
+// until one is found. If a valid go.work file is found, the modules
+// specified will collectively be used as the main modules. If GOWORK
+// is "off", or a go.work file is not found in "auto" mode, workspace
+// mode is disabled.
//
// Environment variables for use with cgo:
//
-// AR
-// The command to use to manipulate library archives when
-// building with the gccgo compiler.
-// The default is 'ar'.
-// CC
-// The command to use to compile C code.
-// CGO_ENABLED
-// Whether the cgo command is supported. Either 0 or 1.
-// CGO_CFLAGS
-// Flags that cgo will pass to the compiler when compiling
-// C code.
-// CGO_CFLAGS_ALLOW
-// A regular expression specifying additional flags to allow
-// to appear in #cgo CFLAGS source code directives.
-// Does not apply to the CGO_CFLAGS environment variable.
-// CGO_CFLAGS_DISALLOW
-// A regular expression specifying flags that must be disallowed
-// from appearing in #cgo CFLAGS source code directives.
-// Does not apply to the CGO_CFLAGS environment variable.
-// CGO_CPPFLAGS, CGO_CPPFLAGS_ALLOW, CGO_CPPFLAGS_DISALLOW
-// Like CGO_CFLAGS, CGO_CFLAGS_ALLOW, and CGO_CFLAGS_DISALLOW,
-// but for the C preprocessor.
-// CGO_CXXFLAGS, CGO_CXXFLAGS_ALLOW, CGO_CXXFLAGS_DISALLOW
-// Like CGO_CFLAGS, CGO_CFLAGS_ALLOW, and CGO_CFLAGS_DISALLOW,
-// but for the C++ compiler.
-// CGO_FFLAGS, CGO_FFLAGS_ALLOW, CGO_FFLAGS_DISALLOW
-// Like CGO_CFLAGS, CGO_CFLAGS_ALLOW, and CGO_CFLAGS_DISALLOW,
-// but for the Fortran compiler.
-// CGO_LDFLAGS, CGO_LDFLAGS_ALLOW, CGO_LDFLAGS_DISALLOW
-// Like CGO_CFLAGS, CGO_CFLAGS_ALLOW, and CGO_CFLAGS_DISALLOW,
-// but for the linker.
-// CXX
-// The command to use to compile C++ code.
-// FC
-// The command to use to compile Fortran code.
-// PKG_CONFIG
-// Path to pkg-config tool.
+// AR
+// The command to use to manipulate library archives when
+// building with the gccgo compiler.
+// The default is 'ar'.
+// CC
+// The command to use to compile C code.
+// CGO_ENABLED
+// Whether the cgo command is supported. Either 0 or 1.
+// CGO_CFLAGS
+// Flags that cgo will pass to the compiler when compiling
+// C code.
+// CGO_CFLAGS_ALLOW
+// A regular expression specifying additional flags to allow
+// to appear in #cgo CFLAGS source code directives.
+// Does not apply to the CGO_CFLAGS environment variable.
+// CGO_CFLAGS_DISALLOW
+// A regular expression specifying flags that must be disallowed
+// from appearing in #cgo CFLAGS source code directives.
+// Does not apply to the CGO_CFLAGS environment variable.
+// CGO_CPPFLAGS, CGO_CPPFLAGS_ALLOW, CGO_CPPFLAGS_DISALLOW
+// Like CGO_CFLAGS, CGO_CFLAGS_ALLOW, and CGO_CFLAGS_DISALLOW,
+// but for the C preprocessor.
+// CGO_CXXFLAGS, CGO_CXXFLAGS_ALLOW, CGO_CXXFLAGS_DISALLOW
+// Like CGO_CFLAGS, CGO_CFLAGS_ALLOW, and CGO_CFLAGS_DISALLOW,
+// but for the C++ compiler.
+// CGO_FFLAGS, CGO_FFLAGS_ALLOW, CGO_FFLAGS_DISALLOW
+// Like CGO_CFLAGS, CGO_CFLAGS_ALLOW, and CGO_CFLAGS_DISALLOW,
+// but for the Fortran compiler.
+// CGO_LDFLAGS, CGO_LDFLAGS_ALLOW, CGO_LDFLAGS_DISALLOW
+// Like CGO_CFLAGS, CGO_CFLAGS_ALLOW, and CGO_CFLAGS_DISALLOW,
+// but for the linker.
+// CXX
+// The command to use to compile C++ code.
+// FC
+// The command to use to compile Fortran code.
+// PKG_CONFIG
+// Path to pkg-config tool.
//
// Architecture-specific environment variables:
//
-// GOARM
-// For GOARCH=arm, the ARM architecture for which to compile.
-// Valid values are 5, 6, 7.
-// GO386
-// For GOARCH=386, how to implement floating point instructions.
-// Valid values are sse2 (default), softfloat.
-// GOAMD64
-// For GOARCH=amd64, the microarchitecture level for which to compile.
-// Valid values are v1 (default), v2, v3, v4.
-// See https://golang.org/wiki/MinimumRequirements#amd64
-// GOMIPS
-// For GOARCH=mips{,le}, whether to use floating point instructions.
-// Valid values are hardfloat (default), softfloat.
-// GOMIPS64
-// For GOARCH=mips64{,le}, whether to use floating point instructions.
-// Valid values are hardfloat (default), softfloat.
-// GOPPC64
-// For GOARCH=ppc64{,le}, the target ISA (Instruction Set Architecture).
-// Valid values are power8 (default), power9.
-// GOWASM
-// For GOARCH=wasm, comma-separated list of experimental WebAssembly features to use.
-// Valid values are satconv, signext.
+// GOARM
+// For GOARCH=arm, the ARM architecture for which to compile.
+// Valid values are 5, 6, 7.
+// GO386
+// For GOARCH=386, how to implement floating point instructions.
+// Valid values are sse2 (default), softfloat.
+// GOAMD64
+// For GOARCH=amd64, the microarchitecture level for which to compile.
+// Valid values are v1 (default), v2, v3, v4.
+// See https://golang.org/wiki/MinimumRequirements#amd64
+// GOMIPS
+// For GOARCH=mips{,le}, whether to use floating point instructions.
+// Valid values are hardfloat (default), softfloat.
+// GOMIPS64
+// For GOARCH=mips64{,le}, whether to use floating point instructions.
+// Valid values are hardfloat (default), softfloat.
+// GOPPC64
+// For GOARCH=ppc64{,le}, the target ISA (Instruction Set Architecture).
+// Valid values are power8 (default), power9.
+// GOWASM
+// For GOARCH=wasm, comma-separated list of experimental WebAssembly features to use.
+// Valid values are satconv, signext.
//
// Special-purpose environment variables:
//
-// GCCGOTOOLDIR
-// If set, where to find gccgo tools, such as cgo.
-// The default is based on how gccgo was configured.
-// GOEXPERIMENT
-// Comma-separated list of toolchain experiments to enable or disable.
-// The list of available experiments may change arbitrarily over time.
-// See src/internal/goexperiment/flags.go for currently valid values.
-// Warning: This variable is provided for the development and testing
-// of the Go toolchain itself. Use beyond that purpose is unsupported.
-// GOROOT_FINAL
-// The root of the installed Go tree, when it is
-// installed in a location other than where it is built.
-// File names in stack traces are rewritten from GOROOT to
-// GOROOT_FINAL.
-// GO_EXTLINK_ENABLED
-// Whether the linker should use external linking mode
-// when using -linkmode=auto with code that uses cgo.
-// Set to 0 to disable external linking mode, 1 to enable it.
-// GIT_ALLOW_PROTOCOL
-// Defined by Git. A colon-separated list of schemes that are allowed
-// to be used with git fetch/clone. If set, any scheme not explicitly
-// mentioned will be considered insecure by 'go get'.
-// Because the variable is defined by Git, the default value cannot
-// be set using 'go env -w'.
+// GCCGOTOOLDIR
+// If set, where to find gccgo tools, such as cgo.
+// The default is based on how gccgo was configured.
+// GOEXPERIMENT
+// Comma-separated list of toolchain experiments to enable or disable.
+// The list of available experiments may change arbitrarily over time.
+// See src/internal/goexperiment/flags.go for currently valid values.
+// Warning: This variable is provided for the development and testing
+// of the Go toolchain itself. Use beyond that purpose is unsupported.
+// GOROOT_FINAL
+// The root of the installed Go tree, when it is
+// installed in a location other than where it is built.
+// File names in stack traces are rewritten from GOROOT to
+// GOROOT_FINAL.
+// GO_EXTLINK_ENABLED
+// Whether the linker should use external linking mode
+// when using -linkmode=auto with code that uses cgo.
+// Set to 0 to disable external linking mode, 1 to enable it.
+// GIT_ALLOW_PROTOCOL
+// Defined by Git. A colon-separated list of schemes that are allowed
+// to be used with git fetch/clone. If set, any scheme not explicitly
+// mentioned will be considered insecure by 'go get'.
+// Because the variable is defined by Git, the default value cannot
+// be set using 'go env -w'.
//
// Additional information available from 'go env' but not read from the environment:
//
-// GOEXE
-// The executable file name suffix (".exe" on Windows, "" on other systems).
-// GOGCCFLAGS
-// A space-separated list of arguments supplied to the CC command.
-// GOHOSTARCH
-// The architecture (GOARCH) of the Go toolchain binaries.
-// GOHOSTOS
-// The operating system (GOOS) of the Go toolchain binaries.
-// GOMOD
-// The absolute path to the go.mod of the main module.
-// If module-aware mode is enabled, but there is no go.mod, GOMOD will be
-// os.DevNull ("/dev/null" on Unix-like systems, "NUL" on Windows).
-// If module-aware mode is disabled, GOMOD will be the empty string.
-// GOTOOLDIR
-// The directory where the go tools (compile, cover, doc, etc...) are installed.
-// GOVERSION
-// The version of the installed Go tree, as reported by runtime.Version.
-//
-//
-// File types
+// GOEXE
+// The executable file name suffix (".exe" on Windows, "" on other systems).
+// GOGCCFLAGS
+// A space-separated list of arguments supplied to the CC command.
+// GOHOSTARCH
+// The architecture (GOARCH) of the Go toolchain binaries.
+// GOHOSTOS
+// The operating system (GOOS) of the Go toolchain binaries.
+// GOMOD
+// The absolute path to the go.mod of the main module.
+// If module-aware mode is enabled, but there is no go.mod, GOMOD will be
+// os.DevNull ("/dev/null" on Unix-like systems, "NUL" on Windows).
+// If module-aware mode is disabled, GOMOD will be the empty string.
+// GOTOOLDIR
+// The directory where the go tools (compile, cover, doc, etc...) are installed.
+// GOVERSION
+// The version of the installed Go tree, as reported by runtime.Version.
+//
+// # File types
//
// The go command examines the contents of a restricted set of files
// in each directory. It identifies which files to examine based on
// the extension of the file name. These extensions are:
//
-// .go
-// Go source files.
-// .c, .h
-// C source files.
-// If the package uses cgo or SWIG, these will be compiled with the
-// OS-native compiler (typically gcc); otherwise they will
-// trigger an error.
-// .cc, .cpp, .cxx, .hh, .hpp, .hxx
-// C++ source files. Only useful with cgo or SWIG, and always
-// compiled with the OS-native compiler.
-// .m
-// Objective-C source files. Only useful with cgo, and always
-// compiled with the OS-native compiler.
-// .s, .S, .sx
-// Assembler source files.
-// If the package uses cgo or SWIG, these will be assembled with the
-// OS-native assembler (typically gcc (sic)); otherwise they
-// will be assembled with the Go assembler.
-// .swig, .swigcxx
-// SWIG definition files.
-// .syso
-// System object files.
+// .go
+// Go source files.
+// .c, .h
+// C source files.
+// If the package uses cgo or SWIG, these will be compiled with the
+// OS-native compiler (typically gcc); otherwise they will
+// trigger an error.
+// .cc, .cpp, .cxx, .hh, .hpp, .hxx
+// C++ source files. Only useful with cgo or SWIG, and always
+// compiled with the OS-native compiler.
+// .m
+// Objective-C source files. Only useful with cgo, and always
+// compiled with the OS-native compiler.
+// .s, .S, .sx
+// Assembler source files.
+// If the package uses cgo or SWIG, these will be assembled with the
+// OS-native assembler (typically gcc (sic)); otherwise they
+// will be assembled with the Go assembler.
+// .swig, .swigcxx
+// SWIG definition files.
+// .syso
+// System object files.
//
// Files of each of these types except .syso may contain build
// constraints, but the go command stops scanning for build constraints
@@ -2246,8 +2223,7 @@
// line comment. See the go/build package documentation for
// more details.
//
-//
-// The go.mod file
+// # The go.mod file
//
// A module version is defined by a tree of source files, with a go.mod
// file in its root. When the go command is run, it looks in the current
@@ -2272,8 +2248,7 @@
// use 'go mod edit'. See 'go help mod edit' or
// https://golang.org/ref/mod#go-mod-edit.
//
-//
-// GOPATH environment variable
+// # GOPATH environment variable
//
// The Go path is used to resolve import statements.
// It is implemented by and documented in the go/build package.
@@ -2317,21 +2292,21 @@
//
// Here's an example directory layout:
//
-// GOPATH=/home/user/go
-//
-// /home/user/go/
-// src/
-// foo/
-// bar/ (go code in package bar)
-// x.go
-// quux/ (go code in package main)
-// y.go
-// bin/
-// quux (installed command)
-// pkg/
-// linux_amd64/
-// foo/
-// bar.a (installed package object)
+// GOPATH=/home/user/go
+//
+// /home/user/go/
+// src/
+// foo/
+// bar/ (go code in package bar)
+// x.go
+// quux/ (go code in package main)
+// y.go
+// bin/
+// quux (installed command)
+// pkg/
+// linux_amd64/
+// foo/
+// bar.a (installed package object)
//
// Go searches each directory listed in GOPATH to find source code,
// but new packages are always downloaded into the first directory
@@ -2339,33 +2314,32 @@
//
// See https://golang.org/doc/code.html for an example.
//
-// GOPATH and Modules
+// # GOPATH and Modules
//
// When using modules, GOPATH is no longer used for resolving imports.
// However, it is still used to store downloaded source code (in GOPATH/pkg/mod)
// and compiled commands (in GOPATH/bin).
//
-// Internal Directories
+// # Internal Directories
//
// Code in or below a directory named "internal" is importable only
// by code in the directory tree rooted at the parent of "internal".
// Here's an extended version of the directory layout above:
//
-// /home/user/go/
-// src/
-// crash/
-// bang/ (go code in package bang)
-// b.go
-// foo/ (go code in package foo)
-// f.go
-// bar/ (go code in package bar)
-// x.go
-// internal/
-// baz/ (go code in package baz)
-// z.go
-// quux/ (go code in package main)
-// y.go
-//
+// /home/user/go/
+// src/
+// crash/
+// bang/ (go code in package bang)
+// b.go
+// foo/ (go code in package foo)
+// f.go
+// bar/ (go code in package bar)
+// x.go
+// internal/
+// baz/ (go code in package baz)
+// z.go
+// quux/ (go code in package main)
+// y.go
//
// The code in z.go is imported as "foo/internal/baz", but that
// import statement can only appear in source files in the subtree
@@ -2375,7 +2349,7 @@
//
// See https://golang.org/s/go14internal for details.
//
-// Vendor Directories
+// # Vendor Directories
//
// Go 1.6 includes support for using local copies of external dependencies
// to satisfy imports of those dependencies, often referred to as vendoring.
@@ -2389,23 +2363,23 @@
// but with the "internal" directory renamed to "vendor"
// and a new foo/vendor/crash/bang directory added:
//
-// /home/user/go/
-// src/
-// crash/
-// bang/ (go code in package bang)
-// b.go
-// foo/ (go code in package foo)
-// f.go
-// bar/ (go code in package bar)
-// x.go
-// vendor/
-// crash/
-// bang/ (go code in package bang)
-// b.go
-// baz/ (go code in package baz)
-// z.go
-// quux/ (go code in package main)
-// y.go
+// /home/user/go/
+// src/
+// crash/
+// bang/ (go code in package bang)
+// b.go
+// foo/ (go code in package foo)
+// f.go
+// bar/ (go code in package bar)
+// x.go
+// vendor/
+// crash/
+// bang/ (go code in package bang)
+// b.go
+// baz/ (go code in package baz)
+// z.go
+// quux/ (go code in package main)
+// y.go
//
// The same visibility rules apply as for internal, but the code
// in z.go is imported as "baz", not as "foo/vendor/baz".
@@ -2427,8 +2401,7 @@
//
// See https://golang.org/s/go15vendor for details.
//
-//
-// Legacy GOPATH go get
+// # Legacy GOPATH go get
//
// The 'go get' command changes behavior depending on whether the
// go command is running in module-aware mode or legacy GOPATH mode.
@@ -2490,8 +2463,7 @@
//
// See also: go build, go install, go clean.
//
-//
-// Module proxy protocol
+// # Module proxy protocol
//
// A Go module proxy is any web server that can respond to GET requests for
// URLs of a specified form. The requests have no query parameters, so even
@@ -2501,15 +2473,14 @@
// For details on the GOPROXY protocol, see
// https://golang.org/ref/mod#goproxy-protocol.
//
-//
-// Import path syntax
+// # Import path syntax
//
// An import path (see 'go help packages') denotes a package stored in the local
// file system. In general, an import path denotes either a standard package (such
// as "unicode/utf8") or a package found in one of the work spaces (For more
// details see: 'go help gopath').
//
-// Relative import paths
+// # Relative import paths
//
// An import path beginning with ./ or ../ is called a relative path.
// The toolchain supports relative import paths as a shortcut in two ways.
@@ -2533,7 +2504,7 @@
// To avoid ambiguity, Go programs cannot use relative import paths
// within a work space.
//
-// Remote import paths
+// # Remote import paths
//
// Certain import paths also
// describe how to obtain the source code for the package using
@@ -2541,29 +2512,29 @@
//
// A few common code hosting sites have special syntax:
//
-// Bitbucket (Git, Mercurial)
+// Bitbucket (Git, Mercurial)
//
-// import "bitbucket.org/user/project"
-// import "bitbucket.org/user/project/sub/directory"
+// import "bitbucket.org/user/project"
+// import "bitbucket.org/user/project/sub/directory"
//
-// GitHub (Git)
+// GitHub (Git)
//
-// import "github.com/user/project"
-// import "github.com/user/project/sub/directory"
+// import "github.com/user/project"
+// import "github.com/user/project/sub/directory"
//
-// Launchpad (Bazaar)
+// Launchpad (Bazaar)
//
-// import "launchpad.net/project"
-// import "launchpad.net/project/series"
-// import "launchpad.net/project/series/sub/directory"
+// import "launchpad.net/project"
+// import "launchpad.net/project/series"
+// import "launchpad.net/project/series/sub/directory"
//
-// import "launchpad.net/~user/project/branch"
-// import "launchpad.net/~user/project/branch/sub/directory"
+// import "launchpad.net/~user/project/branch"
+// import "launchpad.net/~user/project/branch/sub/directory"
//
-// IBM DevOps Services (Git)
+// IBM DevOps Services (Git)
//
-// import "hub.jazz.net/git/user/project"
-// import "hub.jazz.net/git/user/project/sub/directory"
+// import "hub.jazz.net/git/user/project"
+// import "hub.jazz.net/git/user/project/sub/directory"
//
// For code hosted on other servers, import paths may either be qualified
// with the version control type, or the go tool can dynamically fetch
@@ -2572,26 +2543,26 @@
//
// To declare the code location, an import path of the form
//
-// repository.vcs/path
+// repository.vcs/path
//
// specifies the given repository, with or without the .vcs suffix,
// using the named version control system, and then the path inside
// that repository. The supported version control systems are:
//
-// Bazaar .bzr
-// Fossil .fossil
-// Git .git
-// Mercurial .hg
-// Subversion .svn
+// Bazaar .bzr
+// Fossil .fossil
+// Git .git
+// Mercurial .hg
+// Subversion .svn
//
// For example,
//
-// import "example.org/user/foo.hg"
+// import "example.org/user/foo.hg"
//
// denotes the root directory of the Mercurial repository at
// example.org/user/foo or foo.hg, and
//
-// import "example.org/repo.git/foo/bar"
+// import "example.org/repo.git/foo/bar"
//
// denotes the foo/bar directory of the Git repository at
// example.org/repo or repo.git.
@@ -2612,7 +2583,7 @@
//
// The meta tag has the form:
//
-// <meta name="go-import" content="import-prefix vcs repo-root">
+// <meta name="go-import" content="import-prefix vcs repo-root">
//
// The import-prefix is the import path corresponding to the repository
// root. It must be a prefix or an exact match of the package being
@@ -2630,16 +2601,16 @@
//
// For example,
//
-// import "example.org/pkg/foo"
+// import "example.org/pkg/foo"
//
// will result in the following requests:
//
-// https://example.org/pkg/foo?go-get=1 (preferred)
-// http://example.org/pkg/foo?go-get=1 (fallback, only with use of correctly set GOINSECURE)
+// https://example.org/pkg/foo?go-get=1 (preferred)
+// http://example.org/pkg/foo?go-get=1 (fallback, only with use of correctly set GOINSECURE)
//
// If that page contains the meta tag
//
-// <meta name="go-import" content="example.org git https://code.org/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.org/r/p/exproj into
@@ -2656,14 +2627,14 @@
// recognized and is preferred over those listing version control systems.
// That variant uses "mod" as the vcs in the content value, as in:
//
-// <meta name="go-import" content="example.org mod https://code.org/moduleproxy">
+// <meta name="go-import" content="example.org mod https://code.org/moduleproxy">
//
// This tag means to fetch modules with paths beginning with example.org
// from the module proxy available at the URL https://code.org/moduleproxy.
// See https://golang.org/ref/mod#goproxy-protocol for details about the
// proxy protocol.
//
-// Import path checking
+// # Import path checking
//
// When the custom import path feature described above redirects to a
// known code hosting site, each of the resulting packages has two possible
@@ -2672,8 +2643,8 @@
// A package statement is said to have an "import comment" if it is immediately
// followed (before the next newline) by a comment of one of these two forms:
//
-// package math // import "path"
-// package math /* import "path" */
+// package math // import "path"
+// package math /* import "path" */
//
// The go command will refuse to install a package with an import comment
// unless it is being referred to by that import path. In this way, import comments
@@ -2689,8 +2660,7 @@
//
// See https://golang.org/s/go14customimport for details.
//
-//
-// Modules, module versions, and more
+// # Modules, module versions, and more
//
// Modules are how Go manages dependencies.
//
@@ -2714,8 +2684,7 @@
// GOPRIVATE, and other environment variables. See 'go help environment'
// and https://golang.org/ref/mod#private-module-privacy for more information.
//
-//
-// Module authentication using go.sum
+// # Module authentication using go.sum
//
// When the go command downloads a module zip file or go.mod file into the
// module cache, it computes a cryptographic hash and compares it with a known
@@ -2726,12 +2695,11 @@
//
// For details, see https://golang.org/ref/mod#authenticating.
//
-//
-// Package lists and patterns
+// # Package lists and patterns
//
// Many commands apply to a set of packages:
//
-// go action [packages]
+// go action [packages]
//
// Usually, [packages] is a list of import paths.
//
@@ -2810,8 +2778,7 @@
// Directory and file names that begin with "." or "_" are ignored
// by the go tool, as are directories named "testdata".
//
-//
-// Configuration for downloading non-public code
+// # Configuration for downloading non-public code
//
// The go command defaults to downloading modules from the public Go module
// mirror at proxy.golang.org. It also defaults to validating downloaded modules,
@@ -2824,7 +2791,7 @@
// glob patterns (in the syntax of Go's path.Match) of module path prefixes.
// For example,
//
-// GOPRIVATE=*.corp.example.com,rsc.io/private
+// GOPRIVATE=*.corp.example.com,rsc.io/private
//
// causes the go command to treat as private any module with a path prefix
// matching either pattern, including git.corp.example.com/xyzzy, rsc.io/private,
@@ -2838,9 +2805,9 @@
// For example, if a company ran a module proxy serving private modules,
// users would configure go using:
//
-// GOPRIVATE=*.corp.example.com
-// GOPROXY=proxy.example.com
-// GONOPROXY=none
+// GOPRIVATE=*.corp.example.com
+// GOPROXY=proxy.example.com
+// GONOPROXY=none
//
// The GOPRIVATE variable is also used to define the "public" and "private"
// patterns for the GOVCS variable; see 'go help vcs'. For that usage,
@@ -2852,8 +2819,7 @@
//
// For more details, see https://golang.org/ref/mod#private-modules.
//
-//
-// Testing flags
+// # Testing flags
//
// The 'go test' command takes both flags that apply to 'go test' itself
// and flags that apply to the resulting test binary.
@@ -2866,204 +2832,204 @@
// The following flags are recognized by the 'go test' command and
// control the execution of any test:
//
-// -bench regexp
-// Run only those benchmarks matching a regular expression.
-// By default, no benchmarks are run.
-// To run all benchmarks, use '-bench .' or '-bench=.'.
-// The regular expression is split by unbracketed slash (/)
-// characters into a sequence of regular expressions, and each
-// part of a benchmark's identifier must match the corresponding
-// element in the sequence, if any. Possible parents of matches
-// are run with b.N=1 to identify sub-benchmarks. For example,
-// given -bench=X/Y, top-level benchmarks matching X are run
-// with b.N=1 to find any sub-benchmarks matching Y, which are
-// then run in full.
-//
-// -benchtime t
-// Run enough iterations of each benchmark to take t, specified
-// as a time.Duration (for example, -benchtime 1h30s).
-// The default is 1 second (1s).
-// The special syntax Nx means to run the benchmark N times
-// (for example, -benchtime 100x).
-//
-// -count n
-// Run each test, benchmark, and fuzz seed n times (default 1).
-// If -cpu is set, run n times for each GOMAXPROCS value.
-// Examples are always run once. -count does not apply to
-// fuzz tests matched by -fuzz.
-//
-// -cover
-// Enable coverage analysis.
-// Note that because coverage works by annotating the source
-// code before compilation, compilation and test failures with
-// coverage enabled may report line numbers that don't correspond
-// to the original sources.
-//
-// -covermode set,count,atomic
-// Set the mode for coverage analysis for the package[s]
-// being tested. The default is "set" unless -race is enabled,
-// in which case it is "atomic".
-// The values:
-// set: bool: does this statement run?
-// count: int: how many times does this statement run?
-// atomic: int: count, but correct in multithreaded tests;
-// significantly more expensive.
-// Sets -cover.
-//
-// -coverpkg pattern1,pattern2,pattern3
-// Apply coverage analysis in each test to packages matching the patterns.
-// The default is for each test to analyze only the package being tested.
-// See 'go help packages' for a description of package patterns.
-// Sets -cover.
-//
-// -cpu 1,2,4
-// Specify a list of GOMAXPROCS values for which the tests, benchmarks or
-// fuzz tests should be executed. The default is the current value
-// of GOMAXPROCS. -cpu does not apply to fuzz tests matched by -fuzz.
-//
-// -failfast
-// Do not start new tests after the first test failure.
-//
-// -fuzz regexp
-// Run the fuzz test matching the regular expression. When specified,
-// the command line argument must match exactly one package within the
-// main module, and regexp must match exactly one fuzz test within
-// that package. Fuzzing will occur after tests, benchmarks, seed corpora
-// of other fuzz tests, and examples have completed. See the Fuzzing
-// section of the testing package documentation for details.
-//
-// -fuzztime t
-// Run enough iterations of the fuzz target during fuzzing to take t,
-// specified as a time.Duration (for example, -fuzztime 1h30s).
-// The default is to run forever.
-// The special syntax Nx means to run the fuzz target N times
-// (for example, -fuzztime 1000x).
-//
-// -fuzzminimizetime t
-// Run enough iterations of the fuzz target during each minimization
-// attempt to take t, as specified as a time.Duration (for example,
-// -fuzzminimizetime 30s).
-// The default is 60s.
-// The special syntax Nx means to run the fuzz target N times
-// (for example, -fuzzminimizetime 100x).
-//
-// -json
-// Log verbose output and test results in JSON. This presents the
-// same information as the -v flag in a machine-readable format.
-//
-// -list regexp
-// List tests, benchmarks, fuzz tests, or examples matching the regular
-// expression. No tests, benchmarks, fuzz tests, or examples will be run.
-// This will only list top-level tests. No subtest or subbenchmarks will be
-// shown.
-//
-// -parallel n
-// Allow parallel execution of test functions that call t.Parallel, and
-// fuzz targets that call t.Parallel when running the seed corpus.
-// The value of this flag is the maximum number of tests to run
-// simultaneously.
-// While fuzzing, the value of this flag is the maximum number of
-// subprocesses that may call the fuzz function simultaneously, regardless of
-// whether T.Parallel is called.
-// By default, -parallel is set to the value of GOMAXPROCS.
-// Setting -parallel to values higher than GOMAXPROCS may cause degraded
-// performance due to CPU contention, especially when fuzzing.
-// Note that -parallel only applies within a single test binary.
-// The 'go test' command may run tests for different packages
-// in parallel as well, according to the setting of the -p flag
-// (see 'go help build').
-//
-// -run regexp
-// Run only those tests, examples, and fuzz tests matching the regular
-// expression. For tests, the regular expression is split by unbracketed
-// slash (/) characters into a sequence of regular expressions, and each
-// part of a test's identifier must match the corresponding element in
-// the sequence, if any. Note that possible parents of matches are
-// run too, so that -run=X/Y matches and runs and reports the result
-// of all tests matching X, even those without sub-tests matching Y,
-// because it must run them to look for those sub-tests.
-//
-// -short
-// Tell long-running tests to shorten their run time.
-// It is off by default but set during all.bash so that installing
-// the Go tree can run a sanity check but not spend time running
-// exhaustive tests.
-//
-// -shuffle off,on,N
-// Randomize the execution order of tests and benchmarks.
-// It is off by default. If -shuffle is set to on, then it will seed
-// the randomizer using the system clock. If -shuffle is set to an
-// integer N, then N will be used as the seed value. In both cases,
-// the seed will be reported for reproducibility.
-//
-// -timeout d
-// If a test binary runs longer than duration d, panic.
-// If d is 0, the timeout is disabled.
-// The default is 10 minutes (10m).
-//
-// -v
-// Verbose output: log all tests as they are run. Also print all
-// text from Log and Logf calls even if the test succeeds.
-//
-// -vet list
-// Configure the invocation of "go vet" during "go test"
-// to use the comma-separated list of vet checks.
-// If list is empty, "go test" runs "go vet" with a curated list of
-// checks believed to be always worth addressing.
-// If list is "off", "go test" does not run "go vet" at all.
+// -bench regexp
+// Run only those benchmarks matching a regular expression.
+// By default, no benchmarks are run.
+// To run all benchmarks, use '-bench .' or '-bench=.'.
+// The regular expression is split by unbracketed slash (/)
+// characters into a sequence of regular expressions, and each
+// part of a benchmark's identifier must match the corresponding
+// element in the sequence, if any. Possible parents of matches
+// are run with b.N=1 to identify sub-benchmarks. For example,
+// given -bench=X/Y, top-level benchmarks matching X are run
+// with b.N=1 to find any sub-benchmarks matching Y, which are
+// then run in full.
+//
+// -benchtime t
+// Run enough iterations of each benchmark to take t, specified
+// as a time.Duration (for example, -benchtime 1h30s).
+// The default is 1 second (1s).
+// The special syntax Nx means to run the benchmark N times
+// (for example, -benchtime 100x).
+//
+// -count n
+// Run each test, benchmark, and fuzz seed n times (default 1).
+// If -cpu is set, run n times for each GOMAXPROCS value.
+// Examples are always run once. -count does not apply to
+// fuzz tests matched by -fuzz.
+//
+// -cover
+// Enable coverage analysis.
+// Note that because coverage works by annotating the source
+// code before compilation, compilation and test failures with
+// coverage enabled may report line numbers that don't correspond
+// to the original sources.
+//
+// -covermode set,count,atomic
+// Set the mode for coverage analysis for the package[s]
+// being tested. The default is "set" unless -race is enabled,
+// in which case it is "atomic".
+// The values:
+// set: bool: does this statement run?
+// count: int: how many times does this statement run?
+// atomic: int: count, but correct in multithreaded tests;
+// significantly more expensive.
+// Sets -cover.
+//
+// -coverpkg pattern1,pattern2,pattern3
+// Apply coverage analysis in each test to packages matching the patterns.
+// The default is for each test to analyze only the package being tested.
+// See 'go help packages' for a description of package patterns.
+// Sets -cover.
+//
+// -cpu 1,2,4
+// Specify a list of GOMAXPROCS values for which the tests, benchmarks or
+// fuzz tests should be executed. The default is the current value
+// of GOMAXPROCS. -cpu does not apply to fuzz tests matched by -fuzz.
+//
+// -failfast
+// Do not start new tests after the first test failure.
+//
+// -fuzz regexp
+// Run the fuzz test matching the regular expression. When specified,
+// the command line argument must match exactly one package within the
+// main module, and regexp must match exactly one fuzz test within
+// that package. Fuzzing will occur after tests, benchmarks, seed corpora
+// of other fuzz tests, and examples have completed. See the Fuzzing
+// section of the testing package documentation for details.
+//
+// -fuzztime t
+// Run enough iterations of the fuzz target during fuzzing to take t,
+// specified as a time.Duration (for example, -fuzztime 1h30s).
+// The default is to run forever.
+// The special syntax Nx means to run the fuzz target N times
+// (for example, -fuzztime 1000x).
+//
+// -fuzzminimizetime t
+// Run enough iterations of the fuzz target during each minimization
+// attempt to take t, as specified as a time.Duration (for example,
+// -fuzzminimizetime 30s).
+// The default is 60s.
+// The special syntax Nx means to run the fuzz target N times
+// (for example, -fuzzminimizetime 100x).
+//
+// -json
+// Log verbose output and test results in JSON. This presents the
+// same information as the -v flag in a machine-readable format.
+//
+// -list regexp
+// List tests, benchmarks, fuzz tests, or examples matching the regular
+// expression. No tests, benchmarks, fuzz tests, or examples will be run.
+// This will only list top-level tests. No subtest or subbenchmarks will be
+// shown.
+//
+// -parallel n
+// Allow parallel execution of test functions that call t.Parallel, and
+// fuzz targets that call t.Parallel when running the seed corpus.
+// The value of this flag is the maximum number of tests to run
+// simultaneously.
+// While fuzzing, the value of this flag is the maximum number of
+// subprocesses that may call the fuzz function simultaneously, regardless of
+// whether T.Parallel is called.
+// By default, -parallel is set to the value of GOMAXPROCS.
+// Setting -parallel to values higher than GOMAXPROCS may cause degraded
+// performance due to CPU contention, especially when fuzzing.
+// Note that -parallel only applies within a single test binary.
+// The 'go test' command may run tests for different packages
+// in parallel as well, according to the setting of the -p flag
+// (see 'go help build').
+//
+// -run regexp
+// Run only those tests, examples, and fuzz tests matching the regular
+// expression. For tests, the regular expression is split by unbracketed
+// slash (/) characters into a sequence of regular expressions, and each
+// part of a test's identifier must match the corresponding element in
+// the sequence, if any. Note that possible parents of matches are
+// run too, so that -run=X/Y matches and runs and reports the result
+// of all tests matching X, even those without sub-tests matching Y,
+// because it must run them to look for those sub-tests.
+//
+// -short
+// Tell long-running tests to shorten their run time.
+// It is off by default but set during all.bash so that installing
+// the Go tree can run a sanity check but not spend time running
+// exhaustive tests.
+//
+// -shuffle off,on,N
+// Randomize the execution order of tests and benchmarks.
+// It is off by default. If -shuffle is set to on, then it will seed
+// the randomizer using the system clock. If -shuffle is set to an
+// integer N, then N will be used as the seed value. In both cases,
+// the seed will be reported for reproducibility.
+//
+// -timeout d
+// If a test binary runs longer than duration d, panic.
+// If d is 0, the timeout is disabled.
+// The default is 10 minutes (10m).
+//
+// -v
+// Verbose output: log all tests as they are run. Also print all
+// text from Log and Logf calls even if the test succeeds.
+//
+// -vet list
+// Configure the invocation of "go vet" during "go test"
+// to use the comma-separated list of vet checks.
+// If list is empty, "go test" runs "go vet" with a curated list of
+// checks believed to be always worth addressing.
+// If list is "off", "go test" does not run "go vet" at all.
//
// The following flags are also recognized by 'go test' and can be used to
// profile the tests during execution:
//
-// -benchmem
-// Print memory allocation statistics for benchmarks.
+// -benchmem
+// Print memory allocation statistics for benchmarks.
//
-// -blockprofile block.out
-// Write a goroutine blocking profile to the specified file
-// when all tests are complete.
-// Writes test binary as -c would.
+// -blockprofile block.out
+// Write a goroutine blocking profile to the specified file
+// when all tests are complete.
+// Writes test binary as -c would.
//
-// -blockprofilerate n
-// Control the detail provided in goroutine blocking profiles by
-// calling runtime.SetBlockProfileRate with n.
-// See 'go doc runtime.SetBlockProfileRate'.
-// The profiler aims to sample, on average, one blocking event every
-// n nanoseconds the program spends blocked. By default,
-// if -test.blockprofile is set without this flag, all blocking events
-// are recorded, equivalent to -test.blockprofilerate=1.
+// -blockprofilerate n
+// Control the detail provided in goroutine blocking profiles by
+// calling runtime.SetBlockProfileRate with n.
+// See 'go doc runtime.SetBlockProfileRate'.
+// The profiler aims to sample, on average, one blocking event every
+// n nanoseconds the program spends blocked. By default,
+// if -test.blockprofile is set without this flag, all blocking events
+// are recorded, equivalent to -test.blockprofilerate=1.
//
-// -coverprofile cover.out
-// Write a coverage profile to the file after all tests have passed.
-// Sets -cover.
+// -coverprofile cover.out
+// Write a coverage profile to the file after all tests have passed.
+// Sets -cover.
//
-// -cpuprofile cpu.out
-// Write a CPU profile to the specified file before exiting.
-// Writes test binary as -c would.
+// -cpuprofile cpu.out
+// Write a CPU profile to the specified file before exiting.
+// Writes test binary as -c would.
//
-// -memprofile mem.out
-// Write an allocation profile to the file after all tests have passed.
-// Writes test binary as -c would.
+// -memprofile mem.out
+// Write an allocation profile to the file after all tests have passed.
+// Writes test binary as -c would.
//
-// -memprofilerate n
-// Enable more precise (and expensive) memory allocation profiles by
-// setting runtime.MemProfileRate. See 'go doc runtime.MemProfileRate'.
-// To profile all memory allocations, use -test.memprofilerate=1.
+// -memprofilerate n
+// Enable more precise (and expensive) memory allocation profiles by
+// setting runtime.MemProfileRate. See 'go doc runtime.MemProfileRate'.
+// To profile all memory allocations, use -test.memprofilerate=1.
//
-// -mutexprofile mutex.out
-// Write a mutex contention profile to the specified file
-// when all tests are complete.
-// Writes test binary as -c would.
+// -mutexprofile mutex.out
+// Write a mutex contention profile to the specified file
+// when all tests are complete.
+// Writes test binary as -c would.
//
-// -mutexprofilefraction n
-// Sample 1 in n stack traces of goroutines holding a
-// contended mutex.
+// -mutexprofilefraction n
+// Sample 1 in n stack traces of goroutines holding a
+// contended mutex.
//
-// -outputdir directory
-// Place output files from profiling in the specified directory,
-// by default the directory in which "go test" is running.
+// -outputdir directory
+// Place output files from profiling in the specified directory,
+// by default the directory in which "go test" is running.
//
-// -trace trace.out
-// Write an execution trace to the specified file before exiting.
+// -trace trace.out
+// Write an execution trace to the specified file before exiting.
//
// Each of these flags is also recognized with an optional 'test.' prefix,
// as in -test.v. When invoking the generated test binary (the result of
@@ -3075,11 +3041,11 @@
//
// For instance, the command
//
-// go test -v -myflag testdata -cpuprofile=prof.out -x
+// go test -v -myflag testdata -cpuprofile=prof.out -x
//
// will compile the test binary and then run it as
//
-// pkg.test -test.v -myflag testdata -test.cpuprofile=prof.out
+// pkg.test -test.v -myflag testdata -test.cpuprofile=prof.out
//
// (The -x flag is removed because it applies only to the go command's
// execution, not to the test itself.)
@@ -3114,27 +3080,26 @@
//
// For instance, the command
//
-// go test -v -args -x -v
+// go test -v -args -x -v
//
// will compile the test binary and then run it as
//
-// pkg.test -test.v -x -v
+// pkg.test -test.v -x -v
//
// Similarly,
//
-// go test -args math
+// go test -args math
//
// will compile the test binary and then run it as
//
-// pkg.test math
+// pkg.test math
//
// In the first example, the -x and the second -v are passed through to the
// test binary unchanged and with no effect on the go command itself.
// In the second example, the argument math is passed through to the test
// binary, instead of being interpreted as the package list.
//
-//
-// Testing functions
+// # Testing functions
//
// The 'go test' command expects to find test, benchmark, and example functions
// in the "*_test.go" files corresponding to the package under test.
@@ -3142,15 +3107,15 @@
// A test function is one named TestXxx (where Xxx does not start with a
// lower case letter) and should have the signature,
//
-// func TestXxx(t *testing.T) { ... }
+// func TestXxx(t *testing.T) { ... }
//
// A benchmark function is one named BenchmarkXxx and should have the signature,
//
-// func BenchmarkXxx(b *testing.B) { ... }
+// func BenchmarkXxx(b *testing.B) { ... }
//
// A fuzz test is one named FuzzXxx and should have the signature,
//
-// func FuzzXxx(f *testing.F) { ... }
+// func FuzzXxx(f *testing.F) { ... }
//
// An example function is similar to a test function but, instead of using
// *testing.T to report success or failure, prints output to os.Stdout.
@@ -3169,25 +3134,25 @@
//
// Here is an example of an example:
//
-// func ExamplePrintln() {
-// Println("The output of\nthis example.")
-// // Output: The output of
-// // this example.
-// }
+// func ExamplePrintln() {
+// Println("The output of\nthis example.")
+// // Output: The output of
+// // this example.
+// }
//
// Here is another example where the ordering of the output is ignored:
//
-// func ExamplePerm() {
-// for _, value := range Perm(4) {
-// fmt.Println(value)
-// }
+// func ExamplePerm() {
+// for _, value := range Perm(4) {
+// fmt.Println(value)
+// }
//
-// // Unordered output: 4
-// // 2
-// // 1
-// // 3
-// // 0
-// }
+// // Unordered output: 4
+// // 2
+// // 1
+// // 3
+// // 0
+// }
//
// The entire test file is presented as the example when it contains a single
// example function, at least one other function, type, variable, or constant
@@ -3195,8 +3160,7 @@
//
// See the documentation of the testing package for more information.
//
-//
-// Controlling version control with GOVCS
+// # Controlling version control with GOVCS
//
// The 'go get' command can run version control commands like git
// to download imported code. This functionality is critical to the decentralized
@@ -3245,7 +3209,7 @@
//
// For example, consider:
//
-// GOVCS=github.com:git,evil.com:off,*:git|hg
+// GOVCS=github.com:git,evil.com:off,*:git|hg
//
// With this setting, code with a module or import path beginning with
// github.com/ can only use git; paths on evil.com cannot use any version
@@ -3262,14 +3226,12 @@
//
// To allow unfettered use of any version control system for any package, use:
//
-// GOVCS=*:all
+// GOVCS=*:all
//
// To disable all use of version control, use:
//
-// GOVCS=*:off
+// GOVCS=*:off
//
// The 'go env -w' command (see 'go help env') can be used to set the GOVCS
// variable for future go command invocations.
-//
-//
package main
diff --git a/src/cmd/go/go_test.go b/src/cmd/go/go_test.go
index b4387dc0784..905dd682743 100644
--- a/src/cmd/go/go_test.go
+++ b/src/cmd/go/go_test.go
@@ -78,6 +78,10 @@ func tooSlow(t *testing.T) {
// (temp) directory.
var testGOROOT string
+// testGOROOT_FINAL is the GOROOT_FINAL with which the test binary is assumed to
+// have been built.
+var testGOROOT_FINAL = os.Getenv("GOROOT_FINAL")
+
var testGOCACHE string
var testGo string
diff --git a/src/cmd/go/internal/bug/bug.go b/src/cmd/go/internal/bug/bug.go
index 702dc2a14ac..b4181b1e44a 100644
--- a/src/cmd/go/internal/bug/bug.go
+++ b/src/cmd/go/internal/bug/bug.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Package bug implements the ``go bug'' command.
+// Package bug implements the “go bug” command.
package bug
import (
diff --git a/src/cmd/go/internal/cfg/cfg.go b/src/cmd/go/internal/cfg/cfg.go
index a11a1a76555..c6ddfe55d58 100644
--- a/src/cmd/go/internal/cfg/cfg.go
+++ b/src/cmd/go/internal/cfg/cfg.go
@@ -44,9 +44,9 @@ func exeSuffix() string {
// These are general "build flags" used by build and other commands.
var (
- BuildA bool // -a flag
- BuildBuildmode string // -buildmode flag
- BuildBuildvcs bool // -buildvcs flag
+ BuildA bool // -a flag
+ BuildBuildmode string // -buildmode flag
+ BuildBuildvcs = "auto" // -buildvcs flag: "true", "false", or "auto"
BuildContext = defaultContext()
BuildMod string // -mod flag
BuildModExplicit bool // whether -mod was set explicitly
diff --git a/src/cmd/go/internal/clean/clean.go b/src/cmd/go/internal/clean/clean.go
index dc93cdf5983..8564411fb6c 100644
--- a/src/cmd/go/internal/clean/clean.go
+++ b/src/cmd/go/internal/clean/clean.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Package clean implements the ``go clean'' command.
+// Package clean implements the “go clean” command.
package clean
import (
diff --git a/src/cmd/go/internal/doc/doc.go b/src/cmd/go/internal/doc/doc.go
index 7741a9022c9..3b6cd94799a 100644
--- a/src/cmd/go/internal/doc/doc.go
+++ b/src/cmd/go/internal/doc/doc.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Package doc implements the ``go doc'' command.
+// Package doc implements the “go doc” command.
package doc
import (
diff --git a/src/cmd/go/internal/envcmd/env.go b/src/cmd/go/internal/envcmd/env.go
index fcabc8d1c71..529351dfbd8 100644
--- a/src/cmd/go/internal/envcmd/env.go
+++ b/src/cmd/go/internal/envcmd/env.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Package envcmd implements the ``go env'' command.
+// Package envcmd implements the “go env” command.
package envcmd
import (
@@ -184,15 +184,23 @@ func ExtraEnvVarsCostly() []cfg.EnvVar {
}
cmd := b.GccCmd(".", "")
+ join := func(s []string) string {
+ q, err := quoted.Join(s)
+ if err != nil {
+ return strings.Join(s, " ")
+ }
+ return q
+ }
+
return []cfg.EnvVar{
// Note: Update the switch in runEnv below when adding to this list.
- {Name: "CGO_CFLAGS", Value: strings.Join(cflags, " ")},
- {Name: "CGO_CPPFLAGS", Value: strings.Join(cppflags, " ")},
- {Name: "CGO_CXXFLAGS", Value: strings.Join(cxxflags, " ")},
- {Name: "CGO_FFLAGS", Value: strings.Join(fflags, " ")},
- {Name: "CGO_LDFLAGS", Value: strings.Join(ldflags, " ")},
+ {Name: "CGO_CFLAGS", Value: join(cflags)},
+ {Name: "CGO_CPPFLAGS", Value: join(cppflags)},
+ {Name: "CGO_CXXFLAGS", Value: join(cxxflags)},
+ {Name: "CGO_FFLAGS", Value: join(fflags)},
+ {Name: "CGO_LDFLAGS", Value: join(ldflags)},
{Name: "PKG_CONFIG", Value: b.PkgconfigCmd()},
- {Name: "GOGCCFLAGS", Value: strings.Join(cmd[3:], " ")},
+ {Name: "GOGCCFLAGS", Value: join(cmd[3:])},
}
}
diff --git a/src/cmd/go/internal/fix/fix.go b/src/cmd/go/internal/fix/fix.go
index d8ba353de65..3705b30ef95 100644
--- a/src/cmd/go/internal/fix/fix.go
+++ b/src/cmd/go/internal/fix/fix.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Package fix implements the ``go fix'' command.
+// Package fix implements the “go fix” command.
package fix
import (
diff --git a/src/cmd/go/internal/fmtcmd/fmt.go b/src/cmd/go/internal/fmtcmd/fmt.go
index 19656eab7fc..3dc29d40b2f 100644
--- a/src/cmd/go/internal/fmtcmd/fmt.go
+++ b/src/cmd/go/internal/fmtcmd/fmt.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Package fmtcmd implements the ``go fmt'' command.
+// Package fmtcmd implements the “go fmt” command.
package fmtcmd
import (
diff --git a/src/cmd/go/internal/generate/generate.go b/src/cmd/go/internal/generate/generate.go
index 54ccfe78f24..a46f4f8908c 100644
--- a/src/cmd/go/internal/generate/generate.go
+++ b/src/cmd/go/internal/generate/generate.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Package generate implements the ``go generate'' command.
+// Package generate implements the “go generate” command.
package generate
import (
@@ -84,6 +84,9 @@ Go generate sets several variables when it runs the generator:
The line number of the directive in the source file.
$GOPACKAGE
The name of the package of the file containing the directive.
+ $GOROOT
+ The GOROOT directory for the 'go' command that invoked the
+ generator, containing the Go toolchain and standard library.
$DOLLAR
A dollar sign.
@@ -326,6 +329,7 @@ func isGoGenerate(buf []byte) bool {
// single go:generate command.
func (g *Generator) setEnv() {
g.env = []string{
+ "GOROOT=" + cfg.GOROOT,
"GOARCH=" + cfg.BuildContext.GOARCH,
"GOOS=" + cfg.BuildContext.GOOS,
"GOFILE=" + g.file,
diff --git a/src/cmd/go/internal/generate/generate_test.go b/src/cmd/go/internal/generate/generate_test.go
index b546218a3c5..15b1279f36b 100644
--- a/src/cmd/go/internal/generate/generate_test.go
+++ b/src/cmd/go/internal/generate/generate_test.go
@@ -78,11 +78,11 @@ var defEnvMap = map[string]string{
// TestGenerateCommandShortHand - similar to TestGenerateCommandParse,
// except:
-// 1. if the result starts with -command, record that shorthand
-// before moving on to the next test.
-// 2. If a source line number is specified, set that in the parser
-// before executing the test. i.e., execute the split as if it
-// processing that source line.
+// 1. if the result starts with -command, record that shorthand
+// before moving on to the next test.
+// 2. If a source line number is specified, set that in the parser
+// before executing the test. i.e., execute the split as if it
+// processing that source line.
func TestGenerateCommandShorthand(t *testing.T) {
g := &Generator{
r: nil, // Unused here.
@@ -216,11 +216,11 @@ var splitTestsLines = []splitTestWithLine{
// TestGenerateCommandShortHand - similar to TestGenerateCommandParse,
// except:
-// 1. if the result starts with -command, record that shorthand
-// before moving on to the next test.
-// 2. If a source line number is specified, set that in the parser
-// before executing the test. i.e., execute the split as if it
-// processing that source line.
+// 1. if the result starts with -command, record that shorthand
+// before moving on to the next test.
+// 2. If a source line number is specified, set that in the parser
+// before executing the test. i.e., execute the split as if it
+// processing that source line.
func TestGenerateCommandShortHand2(t *testing.T) {
g := &Generator{
r: nil, // Unused here.
diff --git a/src/cmd/go/internal/get/get.go b/src/cmd/go/internal/get/get.go
index 8cf8fe6645f..1bb67bcf51b 100644
--- a/src/cmd/go/internal/get/get.go
+++ b/src/cmd/go/internal/get/get.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Package get implements the ``go get'' command.
+// Package get implements the “go get” command.
package get
import (
diff --git a/src/cmd/go/internal/help/help.go b/src/cmd/go/internal/help/help.go
index 2a07d2423bd..f73097af845 100644
--- a/src/cmd/go/internal/help/help.go
+++ b/src/cmd/go/internal/help/help.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Package help implements the ``go help'' command.
+// Package help implements the “go help” command.
package help
import (
diff --git a/src/cmd/go/internal/imports/build.go b/src/cmd/go/internal/imports/build.go
index 10e90fc216a..53fa1967f74 100644
--- a/src/cmd/go/internal/imports/build.go
+++ b/src/cmd/go/internal/imports/build.go
@@ -215,7 +215,9 @@ func matchTag(name string, tags map[string]bool, prefer bool) bool {
}
// eval is like
+//
// x.Eval(func(tag string) bool { return matchTag(tag, tags) })
+//
// except that it implements the special case for tags["*"] meaning
// all tags are both true and false at the same time.
func eval(x constraint.Expr, tags map[string]bool, prefer bool) bool {
@@ -236,17 +238,18 @@ func eval(x constraint.Expr, tags map[string]bool, prefer bool) bool {
// suffix which does not match the current system.
// The recognized name formats are:
//
-// name_$(GOOS).*
-// name_$(GOARCH).*
-// name_$(GOOS)_$(GOARCH).*
-// name_$(GOOS)_test.*
-// name_$(GOARCH)_test.*
-// name_$(GOOS)_$(GOARCH)_test.*
+// name_$(GOOS).*
+// name_$(GOARCH).*
+// name_$(GOOS)_$(GOARCH).*
+// name_$(GOOS)_test.*
+// name_$(GOARCH)_test.*
+// name_$(GOOS)_$(GOARCH)_test.*
//
// Exceptions:
-// if GOOS=android, then files with GOOS=linux are also matched.
-// if GOOS=illumos, then files with GOOS=solaris are also matched.
-// if GOOS=ios, then files with GOOS=darwin are also matched.
+//
+// if GOOS=android, then files with GOOS=linux are also matched.
+// if GOOS=illumos, then files with GOOS=solaris are also matched.
+// if GOOS=ios, then files with GOOS=darwin are also matched.
//
// If tags["*"] is true, then MatchFile will consider all possible
// GOOS and GOARCH to be available and will consequently
diff --git a/src/cmd/go/internal/list/list.go b/src/cmd/go/internal/list/list.go
index 5fc33989cd9..17864e1da71 100644
--- a/src/cmd/go/internal/list/list.go
+++ b/src/cmd/go/internal/list/list.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Package list implements the ``go list'' command.
+// Package list implements the “go list” command.
package list
import (
@@ -567,7 +567,7 @@ func runList(ctx context.Context, cmd *base.Command, args []string) {
pkgOpts := load.PackageOpts{
IgnoreImports: *listFind,
ModResolveTests: *listTest,
- LoadVCS: cfg.BuildBuildvcs,
+ LoadVCS: true,
}
pkgs := load.PackagesAndErrors(ctx, pkgOpts, args)
if !*listE {
diff --git a/src/cmd/go/internal/load/pkg.go b/src/cmd/go/internal/load/pkg.go
index a1cfcad8267..10799ad5166 100644
--- a/src/cmd/go/internal/load/pkg.go
+++ b/src/cmd/go/internal/load/pkg.go
@@ -17,6 +17,7 @@ import (
"internal/goroot"
"io/fs"
"os"
+ "os/exec"
"path"
pathpkg "path"
"path/filepath"
@@ -196,9 +197,9 @@ func (p *Package) Desc() string {
// IsTestOnly reports whether p is a test-only package.
//
// A “test-only” package is one that:
-// - is a test-only variant of an ordinary package, or
-// - is a synthesized "main" package for a test binary, or
-// - contains only _test.go files.
+// - is a test-only variant of an ordinary package, or
+// - is a synthesized "main" package for a test binary, or
+// - contains only _test.go files.
func (p *Package) IsTestOnly() bool {
return p.ForTest != "" ||
p.Internal.TestmainGo != nil ||
@@ -2062,7 +2063,8 @@ func resolveEmbed(pkgdir string, patterns []string) (files []string, pmap map[st
// then there may be other things lying around, like symbolic links or .git directories.)
var list []string
for _, file := range match {
- rel := filepath.ToSlash(file[len(pkgdir)+1:]) // file, relative to p.Dir
+ // relative path to p.Dir which begins without prefix slash
+ rel := filepath.ToSlash(str.TrimFilePathPrefix(file, pkgdir))
what := "file"
info, err := fsys.Lstat(file)
@@ -2112,7 +2114,7 @@ func resolveEmbed(pkgdir string, patterns []string) (files []string, pmap map[st
if err != nil {
return err
}
- rel := filepath.ToSlash(path[len(pkgdir)+1:])
+ rel := filepath.ToSlash(str.TrimFilePathPrefix(path, pkgdir))
name := info.Name()
if path != file && (isBadEmbedName(name) || ((name[0] == '.' || name[0] == '_') && !all)) {
// Ignore bad names, assuming they won't go into modules.
@@ -2377,7 +2379,7 @@ func (p *Package) setBuildInfo(includeVCS bool) {
var vcsCmd *vcs.Cmd
var err error
const allowNesting = true
- if includeVCS && p.Module != nil && p.Module.Version == "" && !p.Standard && !p.IsTestOnly() {
+ if includeVCS && cfg.BuildBuildvcs != "false" && p.Module != nil && p.Module.Version == "" && !p.Standard && !p.IsTestOnly() {
repoDir, vcsCmd, err = vcs.FromDir(base.Cwd(), "", allowNesting)
if err != nil && !errors.Is(err, os.ErrNotExist) {
setVCSError(err)
@@ -2389,7 +2391,14 @@ func (p *Package) setBuildInfo(includeVCS bool) {
// repository containing the working directory. Don't include VCS info.
// If the repo contains the module or vice versa, but they are not
// the same directory, it's likely an error (see below).
- repoDir, vcsCmd = "", nil
+ goto omitVCS
+ }
+ if cfg.BuildBuildvcs == "auto" && vcsCmd != nil && vcsCmd.Cmd != "" {
+ if _, err := exec.LookPath(vcsCmd.Cmd); err != nil {
+ // We fould a repository, but the required VCS tool is not present.
+ // "-buildvcs=auto" means that we should silently drop the VCS metadata.
+ goto omitVCS
+ }
}
}
if repoDir != "" && vcsCmd.Status != nil {
@@ -2403,8 +2412,11 @@ func (p *Package) setBuildInfo(includeVCS bool) {
return
}
if pkgRepoDir != repoDir {
- setVCSError(fmt.Errorf("main package is in repository %q but current directory is in repository %q", pkgRepoDir, repoDir))
- return
+ if cfg.BuildBuildvcs != "auto" {
+ setVCSError(fmt.Errorf("main package is in repository %q but current directory is in repository %q", pkgRepoDir, repoDir))
+ return
+ }
+ goto omitVCS
}
modRepoDir, _, err := vcs.FromDir(p.Module.Dir, "", allowNesting)
if err != nil {
@@ -2412,8 +2424,11 @@ func (p *Package) setBuildInfo(includeVCS bool) {
return
}
if modRepoDir != repoDir {
- setVCSError(fmt.Errorf("main module is in repository %q but current directory is in repository %q", modRepoDir, repoDir))
- return
+ if cfg.BuildBuildvcs != "auto" {
+ setVCSError(fmt.Errorf("main module is in repository %q but current directory is in repository %q", modRepoDir, repoDir))
+ return
+ }
+ goto omitVCS
}
type vcsStatusError struct {
@@ -2440,6 +2455,7 @@ func (p *Package) setBuildInfo(includeVCS bool) {
}
appendSetting("vcs.modified", strconv.FormatBool(st.Uncommitted))
}
+omitVCS:
p.Internal.BuildInfo = info.String()
}
diff --git a/src/cmd/go/internal/load/test.go b/src/cmd/go/internal/load/test.go
index 39f1131a437..3780f358f4b 100644
--- a/src/cmd/go/internal/load/test.go
+++ b/src/cmd/go/internal/load/test.go
@@ -76,9 +76,9 @@ func TestPackagesFor(ctx context.Context, opts PackageOpts, p *Package, cover *T
}
// TestPackagesAndErrors returns three packages:
-// - pmain, the package main corresponding to the test binary (running tests in ptest and pxtest).
-// - ptest, the package p compiled with added "package p" test files.
-// - pxtest, the result of compiling any "package p_test" (external) test files.
+// - pmain, the package main corresponding to the test binary (running tests in ptest and pxtest).
+// - ptest, the package p compiled with added "package p" test files.
+// - pxtest, the result of compiling any "package p_test" (external) test files.
//
// If the package has no "package p_test" test files, pxtest will be nil.
// If the non-test compilation of package p can be reused
diff --git a/src/cmd/go/internal/lockedfile/lockedfile_plan9.go b/src/cmd/go/internal/lockedfile/lockedfile_plan9.go
index 35669388e05..a2ce794b967 100644
--- a/src/cmd/go/internal/lockedfile/lockedfile_plan9.go
+++ b/src/cmd/go/internal/lockedfile/lockedfile_plan9.go
@@ -17,9 +17,9 @@ import (
// Opening an exclusive-use file returns an error.
// The expected error strings are:
//
-// - "open/create -- file is locked" (cwfs, kfs)
-// - "exclusive lock" (fossil)
-// - "exclusive use file already open" (ramfs)
+// - "open/create -- file is locked" (cwfs, kfs)
+// - "exclusive lock" (fossil)
+// - "exclusive use file already open" (ramfs)
var lockedErrStrings = [...]string{
"file is locked",
"exclusive lock",
diff --git a/src/cmd/go/internal/lockedfile/lockedfile_test.go b/src/cmd/go/internal/lockedfile/lockedfile_test.go
index c9907db46ce..79352bc8c73 100644
--- a/src/cmd/go/internal/lockedfile/lockedfile_test.go
+++ b/src/cmd/go/internal/lockedfile/lockedfile_test.go
@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file.
// js does not support inter-process file locking.
+//
//go:build !js
package lockedfile_test
diff --git a/src/cmd/go/internal/lockedfile/transform_test.go b/src/cmd/go/internal/lockedfile/transform_test.go
index 3c1caa334eb..833cbf78795 100644
--- a/src/cmd/go/internal/lockedfile/transform_test.go
+++ b/src/cmd/go/internal/lockedfile/transform_test.go
@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file.
// js does not support inter-process file locking.
+//
//go:build !js
package lockedfile_test
diff --git a/src/cmd/go/internal/modcmd/mod.go b/src/cmd/go/internal/modcmd/mod.go
index d72d0cacd68..125ba336a0e 100644
--- a/src/cmd/go/internal/modcmd/mod.go
+++ b/src/cmd/go/internal/modcmd/mod.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Package modcmd implements the ``go mod'' command.
+// Package modcmd implements the “go mod” command.
package modcmd
import (
diff --git a/src/cmd/go/internal/modget/get.go b/src/cmd/go/internal/modget/get.go
index 3d8463e892c..08a474f61b3 100644
--- a/src/cmd/go/internal/modget/get.go
+++ b/src/cmd/go/internal/modget/get.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Package modget implements the module-aware ``go get'' command.
+// Package modget implements the module-aware “go get” command.
package modget
// The arguments to 'go get' are patterns with optional version queries, with
@@ -731,10 +731,10 @@ func (r *resolver) performWildcardQueries(ctx context.Context) {
}
// queryWildcard adds a candidate set to q for each module for which:
-// - some version of the module is already in the build list, and
-// - that module exists at some version matching q.version, and
-// - either the module path itself matches q.pattern, or some package within
-// the module at q.version matches q.pattern.
+// - some version of the module is already in the build list, and
+// - that module exists at some version matching q.version, and
+// - either the module path itself matches q.pattern, or some package within
+// the module at q.version matches q.pattern.
func (r *resolver) queryWildcard(ctx context.Context, q *query) {
// For wildcard patterns, modload.QueryPattern only identifies modules
// matching the prefix of the path before the wildcard. However, the build
diff --git a/src/cmd/go/internal/modload/buildlist.go b/src/cmd/go/internal/modload/buildlist.go
index 6f9072c8c48..5b8d6051f32 100644
--- a/src/cmd/go/internal/modload/buildlist.go
+++ b/src/cmd/go/internal/modload/buildlist.go
@@ -676,11 +676,11 @@ func updateWorkspaceRoots(ctx context.Context, rs *Requirements, add []module.Ve
// invariants of the go.mod file needed to support graph pruning for the given
// packages:
//
-// 1. For each package marked with pkgInAll, the module path that provided that
-// package is included as a root.
-// 2. For all packages, the module that provided that package either remains
-// selected at the same version or is upgraded by the dependencies of a
-// root.
+// 1. For each package marked with pkgInAll, the module path that provided that
+// package is included as a root.
+// 2. For all packages, the module that provided that package either remains
+// selected at the same version or is upgraded by the dependencies of a
+// root.
//
// If any module that provided a package has been upgraded above its previous
// version, the caller may need to reload and recompute the package graph.
@@ -769,17 +769,17 @@ func tidyPrunedRoots(ctx context.Context, mainModule module.Version, direct map[
// updatePrunedRoots returns a set of root requirements that maintains the
// invariants of the go.mod file needed to support graph pruning:
//
-// 1. The selected version of the module providing each package marked with
-// either pkgInAll or pkgIsRoot is included as a root.
-// Note that certain root patterns (such as '...') may explode the root set
-// to contain every module that provides any package imported (or merely
-// required) by any other module.
-// 2. Each root appears only once, at the selected version of its path
-// (if rs.graph is non-nil) or at the highest version otherwise present as a
-// root (otherwise).
-// 3. Every module path that appears as a root in rs remains a root.
-// 4. Every version in add is selected at its given version unless upgraded by
-// (the dependencies of) an existing root or another module in add.
+// 1. The selected version of the module providing each package marked with
+// either pkgInAll or pkgIsRoot is included as a root.
+// Note that certain root patterns (such as '...') may explode the root set
+// to contain every module that provides any package imported (or merely
+// required) by any other module.
+// 2. Each root appears only once, at the selected version of its path
+// (if rs.graph is non-nil) or at the highest version otherwise present as a
+// root (otherwise).
+// 3. Every module path that appears as a root in rs remains a root.
+// 4. Every version in add is selected at its given version unless upgraded by
+// (the dependencies of) an existing root or another module in add.
//
// The packages in pkgs are assumed to have been loaded from either the roots of
// rs or the modules selected in the graph of rs.
@@ -787,26 +787,26 @@ func tidyPrunedRoots(ctx context.Context, mainModule module.Version, direct map[
// The above invariants together imply the graph-pruning invariants for the
// go.mod file:
//
-// 1. (The import invariant.) Every module that provides a package transitively
-// imported by any package or test in the main module is included as a root.
-// This follows by induction from (1) and (3) above. Transitively-imported
-// packages loaded during this invocation are marked with pkgInAll (1),
-// and by hypothesis any transitively-imported packages loaded in previous
-// invocations were already roots in rs (3).
+// 1. (The import invariant.) Every module that provides a package transitively
+// imported by any package or test in the main module is included as a root.
+// This follows by induction from (1) and (3) above. Transitively-imported
+// packages loaded during this invocation are marked with pkgInAll (1),
+// and by hypothesis any transitively-imported packages loaded in previous
+// invocations were already roots in rs (3).
//
-// 2. (The argument invariant.) Every module that provides a package matching
-// an explicit package pattern is included as a root. This follows directly
-// from (1): packages matching explicit package patterns are marked with
-// pkgIsRoot.
+// 2. (The argument invariant.) Every module that provides a package matching
+// an explicit package pattern is included as a root. This follows directly
+// from (1): packages matching explicit package patterns are marked with
+// pkgIsRoot.
//
-// 3. (The completeness invariant.) Every module that contributed any package
-// to the build is required by either the main module or one of the modules
-// it requires explicitly. This invariant is left up to the caller, who must
-// not load packages from outside the module graph but may add roots to the
-// graph, but is facilited by (3). If the caller adds roots to the graph in
-// order to resolve missing packages, then updatePrunedRoots will retain them,
-// the selected versions of those roots cannot regress, and they will
-// eventually be written back to the main module's go.mod file.
+// 3. (The completeness invariant.) Every module that contributed any package
+// to the build is required by either the main module or one of the modules
+// it requires explicitly. This invariant is left up to the caller, who must
+// not load packages from outside the module graph but may add roots to the
+// graph, but is facilited by (3). If the caller adds roots to the graph in
+// order to resolve missing packages, then updatePrunedRoots will retain them,
+// the selected versions of those roots cannot regress, and they will
+// eventually be written back to the main module's go.mod file.
//
// (See https://golang.org/design/36460-lazy-module-loading#invariants for more
// detail.)
@@ -1162,14 +1162,14 @@ func tidyUnprunedRoots(ctx context.Context, mainModule module.Version, direct ma
//
// The roots are updated such that:
//
-// 1. The selected version of every module path in direct is included as a root
-// (if it is not "none").
-// 2. Each root is the selected version of its path. (We say that such a root
-// set is “consistent”.)
-// 3. Every version selected in the graph of rs remains selected unless upgraded
-// by a dependency in add.
-// 4. Every version in add is selected at its given version unless upgraded by
-// (the dependencies of) an existing root or another module in add.
+// 1. The selected version of every module path in direct is included as a root
+// (if it is not "none").
+// 2. Each root is the selected version of its path. (We say that such a root
+// set is “consistent”.)
+// 3. Every version selected in the graph of rs remains selected unless upgraded
+// by a dependency in add.
+// 4. Every version in add is selected at its given version unless upgraded by
+// (the dependencies of) an existing root or another module in add.
func updateUnprunedRoots(ctx context.Context, direct map[string]bool, rs *Requirements, add []module.Version) (*Requirements, error) {
mg, err := rs.Graph(ctx)
if err != nil {
diff --git a/src/cmd/go/internal/modload/edit.go b/src/cmd/go/internal/modload/edit.go
index 0f37e3b2e94..c556664c351 100644
--- a/src/cmd/go/internal/modload/edit.go
+++ b/src/cmd/go/internal/modload/edit.go
@@ -16,20 +16,20 @@ import (
// editRequirements returns an edited version of rs such that:
//
-// 1. Each module version in mustSelect is selected.
+// 1. Each module version in mustSelect is selected.
//
-// 2. Each module version in tryUpgrade is upgraded toward the indicated
-// version as far as can be done without violating (1).
+// 2. Each module version in tryUpgrade is upgraded toward the indicated
+// version as far as can be done without violating (1).
//
-// 3. Each module version in rs.rootModules (or rs.graph, if rs is unpruned)
-// is downgraded from its original version only to the extent needed to
-// satisfy (1), or upgraded only to the extent needed to satisfy (1) and
-// (2).
+// 3. Each module version in rs.rootModules (or rs.graph, if rs is unpruned)
+// is downgraded from its original version only to the extent needed to
+// satisfy (1), or upgraded only to the extent needed to satisfy (1) and
+// (2).
//
-// 4. No module is upgraded above the maximum version of its path found in the
-// dependency graph of rs, the combined dependency graph of the versions in
-// mustSelect, or the dependencies of each individual module version in
-// tryUpgrade.
+// 4. No module is upgraded above the maximum version of its path found in the
+// dependency graph of rs, the combined dependency graph of the versions in
+// mustSelect, or the dependencies of each individual module version in
+// tryUpgrade.
//
// Generally, the module versions in mustSelect are due to the module or a
// package within the module matching an explicit command line argument to 'go
diff --git a/src/cmd/go/internal/modload/load.go b/src/cmd/go/internal/modload/load.go
index c1706995354..e85a33dd506 100644
--- a/src/cmd/go/internal/modload/load.go
+++ b/src/cmd/go/internal/modload/load.go
@@ -1222,16 +1222,16 @@ func loadFromRoots(ctx context.Context, params loaderParams) *loader {
//
// In particular:
//
-// - Modules that provide packages directly imported from the main module are
-// marked as direct, and are promoted to explicit roots. If a needed root
-// cannot be promoted due to -mod=readonly or -mod=vendor, the importing
-// package is marked with an error.
+// - Modules that provide packages directly imported from the main module are
+// marked as direct, and are promoted to explicit roots. If a needed root
+// cannot be promoted due to -mod=readonly or -mod=vendor, the importing
+// package is marked with an error.
//
-// - If ld scanned the "all" pattern independent of build constraints, it is
-// guaranteed to have seen every direct import. Module dependencies that did
-// not provide any directly-imported package are then marked as indirect.
+// - If ld scanned the "all" pattern independent of build constraints, it is
+// guaranteed to have seen every direct import. Module dependencies that did
+// not provide any directly-imported package are then marked as indirect.
//
-// - Root dependencies are updated to their selected versions.
+// - Root dependencies are updated to their selected versions.
//
// The "changed" return value reports whether the update changed the selected
// version of any module that either provided a loaded package or may now
diff --git a/src/cmd/go/internal/modload/query.go b/src/cmd/go/internal/modload/query.go
index 33808ea1097..27af78d99eb 100644
--- a/src/cmd/go/internal/modload/query.go
+++ b/src/cmd/go/internal/modload/query.go
@@ -33,19 +33,27 @@ import (
// The version must take one of the following forms:
//
// - the literal string "latest", denoting the latest available, allowed
-// tagged version, with non-prereleases preferred over prereleases.
-// If there are no tagged versions in the repo, latest returns the most
-// recent commit.
+//
+// tagged version, with non-prereleases preferred over prereleases.
+// If there are no tagged versions in the repo, latest returns the most
+// recent commit.
+//
// - the literal string "upgrade", equivalent to "latest" except that if
-// current is a newer version, current will be returned (see below).
+//
+// current is a newer version, current will be returned (see below).
+//
// - the literal string "patch", denoting the latest available tagged version
-// with the same major and minor number as current (see below).
+//
+// with the same major and minor number as current (see below).
+//
// - v1, denoting the latest available tagged version v1.x.x.
// - v1.2, denoting the latest available tagged version v1.2.x.
// - v1.2.3, a semantic version string denoting that tagged version.
// - <v1.2.3, <=v1.2.3, >v1.2.3, >=v1.2.3,
-// denoting the version closest to the target and satisfying the given operator,
-// with non-prereleases preferred over prereleases.
+//
+// denoting the version closest to the target and satisfying the given operator,
+// with non-prereleases preferred over prereleases.
+//
// - a repository commit identifier or tag, denoting that commit.
//
// current denotes the currently-selected version of the module; it may be
@@ -433,9 +441,9 @@ func (qm *queryMatcher) allowsVersion(ctx context.Context, v string) bool {
// filterVersions classifies versions into releases and pre-releases, filtering
// out:
-// 1. versions that do not satisfy the 'allowed' predicate, and
-// 2. "+incompatible" versions, if a compatible one satisfies the predicate
-// and the incompatible version is not preferred.
+// 1. versions that do not satisfy the 'allowed' predicate, and
+// 2. "+incompatible" versions, if a compatible one satisfies the predicate
+// and the incompatible version is not preferred.
//
// If the allowed predicate returns an error not equivalent to ErrDisallowed,
// filterVersions returns that error.
diff --git a/src/cmd/go/internal/modload/stat_openfile.go b/src/cmd/go/internal/modload/stat_openfile.go
index ff7c124af58..5773073d903 100644
--- a/src/cmd/go/internal/modload/stat_openfile.go
+++ b/src/cmd/go/internal/modload/stat_openfile.go
@@ -8,7 +8,7 @@
// are checked by the server and group information is not known to the client,
// access must open the file to check permissions.”
//
-// aix and js,wasm are similar, in that they do not define syscall.Access.
+// js,wasm is similar, in that it does not define syscall.Access.
package modload
diff --git a/src/cmd/go/internal/robustio/robustio.go b/src/cmd/go/internal/robustio/robustio.go
index ce3dbbde6db..15b33773cf5 100644
--- a/src/cmd/go/internal/robustio/robustio.go
+++ b/src/cmd/go/internal/robustio/robustio.go
@@ -42,9 +42,9 @@ func RemoveAll(path string) error {
// in this package attempt to mitigate.
//
// Errors considered ephemeral include:
-// - syscall.ERROR_ACCESS_DENIED
-// - syscall.ERROR_FILE_NOT_FOUND
-// - internal/syscall/windows.ERROR_SHARING_VIOLATION
+// - syscall.ERROR_ACCESS_DENIED
+// - syscall.ERROR_FILE_NOT_FOUND
+// - internal/syscall/windows.ERROR_SHARING_VIOLATION
//
// This set may be expanded in the future; programs must not rely on the
// non-ephemerality of any given error.
diff --git a/src/cmd/go/internal/run/run.go b/src/cmd/go/internal/run/run.go
index 35c57833730..ebe16118190 100644
--- a/src/cmd/go/internal/run/run.go
+++ b/src/cmd/go/internal/run/run.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Package run implements the ``go run'' command.
+// Package run implements the “go run” command.
package run
import (
diff --git a/src/cmd/go/internal/str/path.go b/src/cmd/go/internal/str/path.go
index 0c8aaeaca1f..a69e171f8ce 100644
--- a/src/cmd/go/internal/str/path.go
+++ b/src/cmd/go/internal/str/path.go
@@ -58,8 +58,11 @@ func TrimFilePathPrefix(s, prefix string) string {
if !HasFilePathPrefix(s, prefix) {
return s
}
- if len(s) == len(prefix) {
- return ""
+ trimmed := s[len(prefix):]
+ if len(trimmed) == 0 || trimmed[0] != filepath.Separator {
+ // Prefix either is equal to s, or ends with a separator
+ // (for example, if it is exactly "/").
+ return trimmed
}
- return s[len(prefix)+1:]
+ return trimmed[1:]
}
diff --git a/src/cmd/go/internal/str/str.go b/src/cmd/go/internal/str/str.go
index 021bfbff779..975869d7601 100644
--- a/src/cmd/go/internal/str/str.go
+++ b/src/cmd/go/internal/str/str.go
@@ -30,7 +30,9 @@ func StringList(args ...any) []string {
}
// ToFold returns a string with the property that
+//
// strings.EqualFold(s, t) iff ToFold(s) == ToFold(t)
+//
// This lets us test a large set of strings for fold-equivalent
// duplicates without making a quadratic number of calls
// to EqualFold. Note that strings.ToUpper and strings.ToLower
diff --git a/src/cmd/go/internal/str/str_test.go b/src/cmd/go/internal/str/str_test.go
index 8ea758e0a8b..158fe65dc16 100644
--- a/src/cmd/go/internal/str/str_test.go
+++ b/src/cmd/go/internal/str/str_test.go
@@ -5,6 +5,8 @@
package str
import (
+ "os"
+ "runtime"
"testing"
)
@@ -27,3 +29,72 @@ func TestFoldDup(t *testing.T) {
}
}
}
+
+type trimFilePathPrefixTest struct {
+ s, prefix, want string
+}
+
+func TestTrimFilePathPrefixSlash(t *testing.T) {
+ if os.PathSeparator != '/' {
+ t.Skipf("test requires slash-separated file paths")
+ }
+ tests := []trimFilePathPrefixTest{
+ {"/foo", "", "foo"},
+ {"/foo", "/", "foo"},
+ {"/foo", "/foo", ""},
+ {"/foo/bar", "/foo", "bar"},
+ {"/foo/bar", "/foo/", "bar"},
+ // if prefix is not s's prefix, return s
+ {"/foo", "/bar", "/foo"},
+ {"/foo", "/foo/bar", "/foo"},
+ }
+
+ for _, tt := range tests {
+ if got := TrimFilePathPrefix(tt.s, tt.prefix); got != tt.want {
+ t.Errorf("TrimFilePathPrefix(%q, %q) = %q, want %q", tt.s, tt.prefix, got, tt.want)
+ }
+ }
+}
+
+func TestTrimFilePathPrefixWindows(t *testing.T) {
+ if runtime.GOOS != "windows" {
+ t.Skipf("test requires Windows file paths")
+ }
+ tests := []trimFilePathPrefixTest{
+ {`C:\foo`, `C:`, `foo`},
+ {`C:\foo`, `C:\`, `foo`},
+ {`C:\foo`, `C:\foo`, ``},
+ {`C:\foo\bar`, `C:\foo`, `bar`},
+ {`C:\foo\bar`, `C:\foo\`, `bar`},
+ // if prefix is not s's prefix, return s
+ {`C:\foo`, `C:\bar`, `C:\foo`},
+ {`C:\foo`, `C:\foo\bar`, `C:\foo`},
+ // if volumes are different, return s
+ {`C:\foo`, ``, `C:\foo`},
+ {`C:\foo`, `\foo`, `C:\foo`},
+ {`C:\foo`, `D:\foo`, `C:\foo`},
+
+ //UNC path
+ {`\\host\share\foo`, `\\host\share`, `foo`},
+ {`\\host\share\foo`, `\\host\share\`, `foo`},
+ {`\\host\share\foo`, `\\host\share\foo`, ``},
+ {`\\host\share\foo\bar`, `\\host\share\foo`, `bar`},
+ {`\\host\share\foo\bar`, `\\host\share\foo\`, `bar`},
+ // if prefix is not s's prefix, return s
+ {`\\host\share\foo`, `\\host\share\bar`, `\\host\share\foo`},
+ {`\\host\share\foo`, `\\host\share\foo\bar`, `\\host\share\foo`},
+ // if either host or share name is different, return s
+ {`\\host\share\foo`, ``, `\\host\share\foo`},
+ {`\\host\share\foo`, `\foo`, `\\host\share\foo`},
+ {`\\host\share\foo`, `\\host\other\`, `\\host\share\foo`},
+ {`\\host\share\foo`, `\\other\share\`, `\\host\share\foo`},
+ {`\\host\share\foo`, `\\host\`, `\\host\share\foo`},
+ {`\\host\share\foo`, `\share\`, `\\host\share\foo`},
+ }
+
+ for _, tt := range tests {
+ if got := TrimFilePathPrefix(tt.s, tt.prefix); got != tt.want {
+ t.Errorf("TrimFilePathPrefix(%q, %q) = %q, want %q", tt.s, tt.prefix, got, tt.want)
+ }
+ }
+}
diff --git a/src/cmd/go/internal/test/testflag.go b/src/cmd/go/internal/test/testflag.go
index c046caca25d..f3cd0b1392f 100644
--- a/src/cmd/go/internal/test/testflag.go
+++ b/src/cmd/go/internal/test/testflag.go
@@ -270,6 +270,7 @@ func (f *shuffleFlag) Set(value string) error {
// pkg.test's arguments.
// We allow known flags both before and after the package name list,
// to allow both
+//
// go test fmt -custom-flag-for-fmt-test
// go test -x math
func testFlags(args []string) (packageNames, passToTest []string) {
diff --git a/src/cmd/go/internal/tool/tool.go b/src/cmd/go/internal/tool/tool.go
index 4fe4c2baeda..e8b55092d83 100644
--- a/src/cmd/go/internal/tool/tool.go
+++ b/src/cmd/go/internal/tool/tool.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Package tool implements the ``go tool'' command.
+// Package tool implements the “go tool” command.
package tool
import (
diff --git a/src/cmd/go/internal/version/version.go b/src/cmd/go/internal/version/version.go
index 1c0eb5407d9..5de7b83efae 100644
--- a/src/cmd/go/internal/version/version.go
+++ b/src/cmd/go/internal/version/version.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Package version implements the ``go version'' command.
+// Package version implements the “go version” command.
package version
import (
diff --git a/src/cmd/go/internal/vet/vet.go b/src/cmd/go/internal/vet/vet.go
index d3e0dd8116f..a0b11fdd3dc 100644
--- a/src/cmd/go/internal/vet/vet.go
+++ b/src/cmd/go/internal/vet/vet.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Package vet implements the ``go vet'' command.
+// Package vet implements the “go vet” command.
package vet
import (
diff --git a/src/cmd/go/internal/work/build.go b/src/cmd/go/internal/work/build.go
index 42f052d3410..e9a8ee6cb3a 100644
--- a/src/cmd/go/internal/work/build.go
+++ b/src/cmd/go/internal/work/build.go
@@ -13,6 +13,7 @@ import (
"os"
"path/filepath"
"runtime"
+ "strconv"
"strings"
"cmd/go/internal/base"
@@ -91,11 +92,13 @@ and test commands:
-buildmode mode
build mode to use. See 'go help buildmode' for more.
-buildvcs
- Whether to stamp binaries with version control information. By default,
- version control information is stamped into a binary if the main package
- and the main module containing it are in the repository containing the
- current directory (if there is a repository). Use -buildvcs=false to
- omit version control information.
+ Whether to stamp binaries with version control information
+ ("true", "false", or "auto"). By default ("auto"), version control
+ information is stamped into a binary if the main package, the main module
+ containing it, and the current directory are all in the same repository.
+ Use -buildvcs=false to always omit version control information, or
+ -buildvcs=true to error out if version control information is available but
+ cannot be included due to a missing tool or ambiguous directory structure.
-compiler name
name of compiler to use, as in runtime.Compiler (gccgo or gc).
-gccgoflags '[pattern=]arg list'
@@ -302,7 +305,7 @@ func AddBuildFlags(cmd *base.Command, mask BuildFlagMask) {
cmd.Flag.Var((*base.StringsFlag)(&cfg.BuildToolexec), "toolexec", "")
cmd.Flag.BoolVar(&cfg.BuildTrimpath, "trimpath", false, "")
cmd.Flag.BoolVar(&cfg.BuildWork, "work", false, "")
- cmd.Flag.BoolVar(&cfg.BuildBuildvcs, "buildvcs", true, "")
+ cmd.Flag.Var((*buildvcsFlag)(&cfg.BuildBuildvcs), "buildvcs", "")
// Undocumented, unstable debugging flags.
cmd.Flag.StringVar(&cfg.DebugActiongraph, "debug-actiongraph", "", "")
@@ -332,6 +335,29 @@ func (v *tagsFlag) String() string {
return "<TagsFlag>"
}
+// buildvcsFlag is the implementation of the -buildvcs flag.
+type buildvcsFlag string
+
+func (f *buildvcsFlag) IsBoolFlag() bool { return true } // allow -buildvcs (without arguments)
+
+func (f *buildvcsFlag) Set(s string) error {
+ // https://go.dev/issue/51748: allow "-buildvcs=auto",
+ // in addition to the usual "true" and "false".
+ if s == "" || s == "auto" {
+ *f = "auto"
+ return nil
+ }
+
+ b, err := strconv.ParseBool(s)
+ if err != nil {
+ return errors.New("value is neither 'auto' nor a valid bool")
+ }
+ *f = (buildvcsFlag)(strconv.FormatBool(b)) // convert to canonical "true" or "false"
+ return nil
+}
+
+func (f *buildvcsFlag) String() string { return string(*f) }
+
// fileExtSplit expects a filename and returns the name
// and ext (without the dot). If the file has no
// extension, ext will be empty.
@@ -379,7 +405,7 @@ func runBuild(ctx context.Context, cmd *base.Command, args []string) {
var b Builder
b.Init()
- pkgs := load.PackagesAndErrors(ctx, load.PackageOpts{LoadVCS: cfg.BuildBuildvcs}, args)
+ pkgs := load.PackagesAndErrors(ctx, load.PackageOpts{LoadVCS: true}, args)
load.CheckPackageErrors(pkgs)
explicitO := len(cfg.BuildO) > 0
@@ -532,16 +558,22 @@ See also: go build, go get, go clean.
// libname returns the filename to use for the shared library when using
// -buildmode=shared. The rules we use are:
// Use arguments for special 'meta' packages:
+//
// std --> libstd.so
// std cmd --> libstd,cmd.so
+//
// A single non-meta argument with trailing "/..." is special cased:
+//
// foo/... --> libfoo.so
// (A relative path like "./..." expands the "." first)
+//
// Use import paths for other cases, changing '/' to '-':
+//
// somelib --> libsubdir-somelib.so
// ./ or ../ --> libsubdir-somelib.so
// gopkg.in/tomb.v2 -> libgopkg.in-tomb.v2.so
// a/... b/... ---> liba/c,b/d.so - all matching import paths
+//
// Name parts are joined with ','.
func libname(args []string, pkgs []*load.Package) (string, error) {
var libname string
@@ -603,7 +635,7 @@ func runInstall(ctx context.Context, cmd *base.Command, args []string) {
modload.InitWorkfile()
BuildInit()
- pkgs := load.PackagesAndErrors(ctx, load.PackageOpts{LoadVCS: cfg.BuildBuildvcs}, args)
+ pkgs := load.PackagesAndErrors(ctx, load.PackageOpts{LoadVCS: true}, args)
if cfg.ModulesEnabled && !modload.HasModRoot() {
haveErrors := false
allMissingErrors := true
diff --git a/src/cmd/go/internal/work/exec.go b/src/cmd/go/internal/work/exec.go
index 4252209f100..9c9d58b2a17 100644
--- a/src/cmd/go/internal/work/exec.go
+++ b/src/cmd/go/internal/work/exec.go
@@ -303,7 +303,9 @@ func (b *Builder) buildActionID(a *Action) cache.ActionID {
fmt.Fprintf(h, "fuzz %q\n", fuzzFlags)
}
}
- fmt.Fprintf(h, "modinfo %q\n", p.Internal.BuildInfo)
+ if p.Internal.BuildInfo != "" {
+ fmt.Fprintf(h, "modinfo %q\n", p.Internal.BuildInfo)
+ }
// Configuration specific to compiler toolchain.
switch cfg.BuildToolchainName {
@@ -1882,6 +1884,7 @@ func (b *Builder) installHeader(ctx context.Context, a *Action) error {
}
// cover runs, in effect,
+//
// go tool cover -mode=b.coverMode -var="varName" -o dst.go src.go
func (b *Builder) cover(a *Action, dst, src string, varName string) error {
return b.run(a, a.Objdir, "cover "+a.Package.ImportPath, nil,
diff --git a/src/cmd/go/internal/work/security.go b/src/cmd/go/internal/work/security.go
index d1e2c673fad..0bf8763543b 100644
--- a/src/cmd/go/internal/work/security.go
+++ b/src/cmd/go/internal/work/security.go
@@ -171,7 +171,7 @@ var validLinkerFlags = []*lazyregexp.Regexp{
// Note that any wildcards in -Wl need to exclude comma,
// since -Wl splits its argument at commas and passes
// them all to the linker uninterpreted. Allowing comma
- // in a wildcard would allow tunnelling arbitrary additional
+ // in a wildcard would allow tunneling arbitrary additional
// linker arguments through one of these.
re(`-Wl,--(no-)?allow-multiple-definition`),
re(`-Wl,--(no-)?allow-shlib-undefined`),
diff --git a/src/cmd/go/internal/workcmd/work.go b/src/cmd/go/internal/workcmd/work.go
index 39c81e8f5dc..c99cc2a3fa9 100644
--- a/src/cmd/go/internal/workcmd/work.go
+++ b/src/cmd/go/internal/workcmd/work.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Package workcmd implements the ``go work'' command.
+// Package workcmd implements the “go work” command.
package workcmd
import (
diff --git a/src/cmd/go/script_test.go b/src/cmd/go/script_test.go
index 76c542f32a2..6254cf97c1c 100644
--- a/src/cmd/go/script_test.go
+++ b/src/cmd/go/script_test.go
@@ -175,7 +175,7 @@ func (ts *testScript) setup() {
"GOPROXY=" + proxyURL,
"GOPRIVATE=",
"GOROOT=" + testGOROOT,
- "GOROOT_FINAL=" + os.Getenv("GOROOT_FINAL"), // causes spurious rebuilds and breaks the "stale" built-in if not propagated
+ "GOROOT_FINAL=" + testGOROOT_FINAL, // causes spurious rebuilds and breaks the "stale" built-in if not propagated
"GOTRACEBACK=system",
"TESTGO_GOROOT=" + testGOROOT,
"GOSUMDB=" + testSumDBVerifierKey,
@@ -385,6 +385,8 @@ Script:
}
}
}
+ case "mismatched-goroot":
+ ok = testGOROOT_FINAL != "" && testGOROOT_FINAL != testGOROOT
default:
if strings.HasPrefix(cond.tag, "exec:") {
prog := cond.tag[len("exec:"):]
diff --git a/src/cmd/go/testdata/script/README b/src/cmd/go/testdata/script/README
index 17b582d6627..85e575d56ee 100644
--- a/src/cmd/go/testdata/script/README
+++ b/src/cmd/go/testdata/script/README
@@ -90,6 +90,8 @@ should only run when the condition is satisfied. The available conditions are:
- [exec:prog] for whether prog is available for execution (found by exec.LookPath)
- [GODEBUG:value] for whether value is one of the comma-separated entries in the GODEBUG variable
- [buildmode:value] for whether -buildmode=value is supported
+ - [trimpath] for whether the 'go' binary was built with -trimpath
+ - [mismatched-goroot] for whether the test's GOROOT_FINAL does not match the real GOROOT
A condition can be negated: [!short] means to run the rest of the line
when testing.Short() is false. Multiple conditions may be given for a single
diff --git a/src/cmd/go/testdata/script/build_buildvcs_auto.txt b/src/cmd/go/testdata/script/build_buildvcs_auto.txt
new file mode 100644
index 00000000000..9eac5680459
--- /dev/null
+++ b/src/cmd/go/testdata/script/build_buildvcs_auto.txt
@@ -0,0 +1,87 @@
+# Regression test for https://go.dev/issue/51748: by default, 'go build' should
+# not attempt to stamp VCS information when the VCS tool is not present.
+
+[short] skip
+[!exec:git] skip
+
+cd sub
+exec git init .
+exec git add sub.go
+exec git commit -m 'initial state'
+cd ..
+
+exec git init
+exec git submodule add ./sub
+exec git add go.mod example.go
+exec git commit -m 'initial state'
+
+
+# Control case: with a git binary in $PATH,
+# 'go build' on a package in the same git repo
+# succeeds and stamps VCS metadata by default.
+
+go build -o example.exe .
+go version -m example.exe
+stdout '^\tbuild\tvcs=git$'
+stdout '^\tbuild\tvcs.modified=false$'
+
+
+# Building a binary from a different (nested) VCS repo should not stamp VCS
+# info. It should be an error if VCS stamps are requested explicitly with
+# '-buildvcs' (since we know the VCS metadata exists), but not an error
+# with '-buildvcs=auto'.
+
+go build -o sub.exe ./sub
+go version -m sub.exe
+! stdout '^\tbuild\tvcs'
+
+! go build -buildvcs -o sub.exe ./sub
+stderr '\Aerror obtaining VCS status: main package is in repository ".*" but current directory is in repository ".*"\n\tUse -buildvcs=false to disable VCS stamping.\n\z'
+
+cd ./sub
+go build -o sub.exe .
+go version -m sub.exe
+! stdout '^\tbuild\tvcs'
+
+! go build -buildvcs -o sub.exe .
+stderr '\Aerror obtaining VCS status: main module is in repository ".*" but current directory is in repository ".*"\n\tUse -buildvcs=false to disable VCS stamping.\n\z'
+cd ..
+
+
+# After removing 'git' from $PATH, 'go build -buildvcs' should fail...
+
+env PATH=
+env path=
+! go build -buildvcs -o example.exe .
+stderr 'go: missing Git command\. See https://golang\.org/s/gogetcmd$'
+
+# ...but by default we should omit VCS metadata when the tool is missing.
+
+go build -o example.exe .
+go version -m example.exe
+! stdout '^\tbuild\tvcs'
+
+# The default behavior can be explicitly set with '-buildvcs=auto'.
+
+go build -buildvcs=auto -o example.exe .
+go version -m example.exe
+! stdout '^\tbuild\tvcs'
+
+# Other flag values should be rejected with a useful error message.
+
+! go build -buildvcs=hg -o example.exe .
+stderr '\Ainvalid boolean value "hg" for -buildvcs: value is neither ''auto'' nor a valid bool\nusage: go build .*\nRun ''go help build'' for details.\n\z'
+
+
+-- go.mod --
+module example
+
+go 1.18
+-- example.go --
+package main
+
+func main() {}
+-- sub/sub.go --
+package main
+
+func main() {}
diff --git a/src/cmd/go/testdata/script/build_trimpath_goroot.txt b/src/cmd/go/testdata/script/build_trimpath_goroot.txt
index 7b870ab739d..a26cfd23be4 100644
--- a/src/cmd/go/testdata/script/build_trimpath_goroot.txt
+++ b/src/cmd/go/testdata/script/build_trimpath_goroot.txt
@@ -8,24 +8,52 @@
# TODO(#51483): when runtime.GOROOT() returns the empty string,
# go/build should default to 'go env GOROOT' instead.
-env GOROOT=
env GOROOT_FINAL=
+[trimpath] env GOROOT=
[trimpath] ! go env GOROOT
[trimpath] stderr '^go: cannot find GOROOT directory: ''go'' binary is trimmed and GOROOT is not set$'
-[trimpath] stop
+[trimpath] env GOROOT=$TESTGO_GOROOT
+
+[short] stop
+
+# With GOROOT still set but GOROOT_FINAL unset, 'go build' and 'go test -c'
+# should cause runtime.GOROOT() to report either the correct GOROOT
+# (without -trimpath) or no GOROOT at all (with -trimpath).
+go build -o example.exe .
+go build -trimpath -o example-trimpath.exe .
+go test -c -o example.test.exe .
+go test -trimpath -c -o example.test-trimpath.exe .
-[short] skip
+env GOROOT=
-go run .
+exec ./example.exe
stdout '^GOROOT '$TESTGO_GOROOT'$'
stdout '^runtime '$TESTGO_GOROOT${/}src${/}runtime'$'
-go test -v .
+! exec ./example-trimpath.exe
+stdout '^GOROOT $'
+stderr 'cannot find package "runtime" in any of:\n\t\(\$GOROOT not set\)\n\t'$WORK${/}gopath${/}src${/}runtime' \(from \$GOPATH\)\n\z'
+
+exec ./example.test.exe -test.v
stdout '^GOROOT '$TESTGO_GOROOT'$'
stdout '^runtime '$TESTGO_GOROOT${/}src${/}runtime'$'
+! exec ./example.test-trimpath.exe -test.v
+stdout '^GOROOT $'
+stderr 'cannot find package "runtime" in any of:\n\t\(\$GOROOT not set\)\n\t'$WORK${/}gopath${/}src${/}runtime' \(from \$GOPATH\)$'
+
+# If a correct GOROOT is baked in to the 'go' command itself, 'go run' and
+# 'go test' should not implicitly set GOROOT in the process environment
+# (because that could mask an unexpected production dependency on the GOROOT
+# environment variable), but 'go generate' should (because the generator may
+# reasonably expect to be able to locate the GOROOT for which it is generating
+# code).
+
+[trimpath] stop
+[mismatched-goroot] stop
+
! go run -trimpath .
stdout '^GOROOT $'
stderr 'cannot find package "runtime" in any of:\n\t\(\$GOROOT not set\)\n\t'$WORK${/}gopath${/}src${/}runtime' \(from \$GOPATH\)\nexit status 1\n\z'
@@ -34,6 +62,11 @@ stderr 'cannot find package "runtime" in any of:\n\t\(\$GOROOT not set\)\n\t'$WO
stdout '^GOROOT $'
stdout 'cannot find package "runtime" in any of:\n\t\(\$GOROOT not set\)\n\t'$WORK${/}gopath${/}src${/}runtime' \(from \$GOPATH\)$'
+env GOFLAGS=-trimpath
+go generate .
+stdout '^GOROOT '$TESTGO_GOROOT'$'
+stdout '^runtime '$TESTGO_GOROOT${/}src${/}runtime'$'
+
-- go.mod --
module example
@@ -41,6 +74,8 @@ go 1.19
-- main.go --
package main
+//go:generate go run .
+
import (
"fmt"
"go/build"
diff --git a/src/cmd/go/testdata/script/test_buildvcs.txt b/src/cmd/go/testdata/script/test_buildvcs.txt
index a0689195e81..a6699660364 100644
--- a/src/cmd/go/testdata/script/test_buildvcs.txt
+++ b/src/cmd/go/testdata/script/test_buildvcs.txt
@@ -5,6 +5,8 @@
[short] skip
[!exec:git] skip
+env GOFLAGS=-buildvcs # override default -buildvcs=auto in GOFLAGS, as a user might
+
exec git init
# The test binaries should not have VCS settings stamped.
diff --git a/src/cmd/go/testdata/script/test_fuzz_minimize_interesting.txt b/src/cmd/go/testdata/script/test_fuzz_minimize_interesting.txt
index a09e85b972f..e61c4f9d04e 100644
--- a/src/cmd/go/testdata/script/test_fuzz_minimize_interesting.txt
+++ b/src/cmd/go/testdata/script/test_fuzz_minimize_interesting.txt
@@ -18,30 +18,27 @@ env GOCACHE=$WORK/gocache
exec ./fuzz.test$GOEXE -test.fuzzcachedir=$GOCACHE/fuzz -test.fuzz=FuzzMinCache -test.fuzztime=1000x
go run check_cache/check_cache.go $GOCACHE/fuzz/FuzzMinCache
-go test -c -fuzz=. # Build using shared build cache for speed.
-env GOCACHE=$WORK/gocache
-
# Test that minimization occurs for a crash that appears while minimizing a
# newly found interesting input. There must be only one worker for this test to
# be flaky like we want.
-! exec ./fuzz.test$GOEXE -test.fuzzcachedir=$GOCACHE/fuzz -test.fuzz=FuzzMinimizerCrashInMinimization -test.run=FuzzMinimizerCrashInMinimization -test.fuzztime=10000x -test.parallel=1
+! exec ./fuzz.test$GOEXE -test.fuzzcachedir=$GOCACHE/fuzz -test.fuzz=FuzzMinimizerCrashInMinimization -test.run=XXX -test.fuzztime=10000x -test.parallel=1
! stdout '^ok'
stdout -count=1 'got the minimum size!'
-stdout -count=1 'flaky failure'
+stdout -count=1 'bad input'
stdout FAIL
# Check that the input written to testdata will reproduce the error, and is the
# smallest possible.
-go run check_testdata/check_testdata.go FuzzMinimizerCrashInMinimization 50
+go run check_testdata/check_testdata.go FuzzMinimizerCrashInMinimization 1
# Test that a nonrecoverable error that occurs while minimizing an interesting
# input is reported correctly.
-! exec ./fuzz.test$GOEXE -test.fuzzcachedir=$GOCACHE/fuzz -test.fuzz=FuzzMinimizerNonrecoverableCrashInMinimization -test.run=FuzzMinimizerNonrecoverableCrashInMinimization -test.fuzztime=10000x -test.parallel=1
+! exec ./fuzz.test$GOEXE -test.fuzzcachedir=$GOCACHE/fuzz -test.fuzz=FuzzMinimizerNonrecoverableCrashInMinimization -test.run=XXX -test.fuzztime=10000x -test.parallel=1
! stdout '^ok'
stdout -count=1 'fuzzing process hung or terminated unexpectedly while minimizing'
stdout -count=1 'EOF'
stdout FAIL
# Check that the input written to testdata will reproduce the error.
-go run check_testdata/check_testdata.go FuzzMinimizerNonrecoverableCrashInMinimization 100
+go run check_testdata/check_testdata.go FuzzMinimizerNonrecoverableCrashInMinimization 1
-- go.mod --
module fuzz
@@ -65,57 +62,34 @@ package fuzz
import (
"bytes"
- "io"
"os"
- "strings"
"testing"
- "unicode/utf8"
)
func FuzzMinimizerCrashInMinimization(f *testing.F) {
- seed := strings.Repeat("A", 1000)
+ seed := bytes.Repeat([]byte{255}, 100)
f.Add(seed)
- i := 3
- f.Fuzz(func(t *testing.T, s string) {
- if len(s) < 50 || len(s) > 1100 {
- // Make sure that b is large enough that it can be minimized
+ f.Fuzz(func(t *testing.T, b []byte) {
+ if bytes.Equal(seed, b) {
return
}
- if s != seed {
- // This should hit a new edge, and the interesting input
- // should attempt minimization
- Y(io.Discard, s)
- }
- if i > 0 {
- // Don't let it fail right away.
- i--
- } else if utf8.RuneCountInString(s) == len(s) && len(s) <= 100 {
- // Make sure this only fails if the number of bytes in the
- // marshaled string is the same as the unmarshaled string,
- // so that we can check the length of the testdata file.
- t.Error("flaky failure")
- if len(s) == 50 {
- t.Error("got the minimum size!")
- }
+ t.Error("bad input")
+ if len(b) == 1 {
+ t.Error("got the minimum size!")
}
})
}
+var fuzzing bool
+
func FuzzMinimizerNonrecoverableCrashInMinimization(f *testing.F) {
- seed := strings.Repeat("A", 1000)
+ seed := bytes.Repeat([]byte{255}, 100)
f.Add(seed)
- i := 3
- f.Fuzz(func(t *testing.T, s string) {
- if len(s) < 50 || len(s) > 1100 {
+ f.Fuzz(func(t *testing.T, b []byte) {
+ if bytes.Equal(seed, b) {
return
- }
- if s != seed {
- Y(io.Discard, s)
- }
- if i > 0 {
- i--
- } else if utf8.RuneCountInString(s) == len(s) && len(s) <= 100 {
- os.Exit(19)
+ } else if len(b) == 1 {
+ os.Exit(1)
}
})
}
@@ -138,10 +112,12 @@ func FuzzMinCache(f *testing.F) {
package main
import (
+ "bytes"
"fmt"
"io/ioutil"
"os"
"path/filepath"
+ "regexp"
"strconv"
)
@@ -165,22 +141,36 @@ func main() {
os.Exit(1)
}
- fname := files[0].Name()
- contents, err := ioutil.ReadFile(filepath.Join(dir, fname))
- if err != nil {
- fmt.Fprintln(os.Stderr, err)
- os.Exit(1)
- }
- contentsLen := len(contents) - len(`go test fuzz v1
-string("")
-`)
- if got, want := contentsLen, wantLen; got > want {
- fmt.Fprintf(os.Stderr, "expect length <= %d, got %d\n", want, got)
- os.Exit(1)
+ for _, f := range files {
+ data, err := ioutil.ReadFile(filepath.Join(dir, f.Name()))
+ if err != nil {
+ panic(err)
+ }
+ var containsVal bool
+ for _, line := range bytes.Split(data, []byte("\n")) {
+ m := valRe.FindSubmatch(line)
+ if m == nil {
+ continue
+ }
+ containsVal = true
+ s, err := strconv.Unquote(string(m[1]))
+ if err != nil {
+ panic(err)
+ }
+ if len(s) != wantLen {
+ fmt.Fprintf(os.Stderr, "expect length %d, got %d (%q)\n", wantLen, len(s), line)
+ os.Exit(1)
+ }
+ }
+ if !containsVal {
+ fmt.Fprintln(os.Stderr, "corpus file contained no values")
+ os.Exit(1)
+ }
}
- fmt.Fprintf(os.Stderr, "%s\n", contents)
}
+var valRe = regexp.MustCompile(`^\[\]byte\(([^)]+)\)$`)
+
-- check_cache/check_cache.go --
//go:build ignore
// +build ignore
diff --git a/src/cmd/go/testdata/script/version_buildvcs_nested.txt b/src/cmd/go/testdata/script/version_buildvcs_nested.txt
index 08d4c92bafb..a0c69f9c12a 100644
--- a/src/cmd/go/testdata/script/version_buildvcs_nested.txt
+++ b/src/cmd/go/testdata/script/version_buildvcs_nested.txt
@@ -1,7 +1,7 @@
[!exec:git] skip
[!exec:hg] skip
[short] skip
-env GOFLAGS=-n
+env GOFLAGS='-n -buildvcs'
# Create a root module in a root Git repository.
mkdir root
diff --git a/src/cmd/gofmt/doc.go b/src/cmd/gofmt/doc.go
index e3406655941..8ac9c6a9317 100644
--- a/src/cmd/gofmt/doc.go
+++ b/src/cmd/gofmt/doc.go
@@ -13,9 +13,11 @@ that directory, recursively. (Files starting with a period are ignored.)
By default, gofmt prints the reformatted sources to standard output.
Usage:
+
gofmt [flags] [path ...]
The flags are:
+
-d
Do not print reformatted sources to standard output.
If a file's formatting is different than gofmt's, print diffs
@@ -37,10 +39,10 @@ The flags are:
the original file is restored from an automatic backup.
Debugging support:
+
-cpuprofile filename
Write cpu profile to the specified file.
-
The rewrite rule specified with the -r flag must be a string of the form:
pattern -> replacement
@@ -57,7 +59,7 @@ such a fragment, gofmt preserves leading indentation as well as leading
and trailing spaces, so that individual sections of a Go program can be
formatted by piping them through gofmt.
-Examples
+# Examples
To check files for unnecessary parentheses:
@@ -71,7 +73,7 @@ To convert the package tree from explicit slice upper bounds to implicit ones:
gofmt -r 'α[β:len(α)] -> α[β:]' -w $GOROOT/src
-The simplify command
+# The simplify command
When invoked with -s gofmt will make the following source transformations where possible.
diff --git a/src/cmd/internal/bio/buf_mmap.go b/src/cmd/internal/bio/buf_mmap.go
index b9755c7e50e..89ae39f736a 100644
--- a/src/cmd/internal/bio/buf_mmap.go
+++ b/src/cmd/internal/bio/buf_mmap.go
@@ -18,12 +18,12 @@ import (
// because some operating systems place a limit on the number of
// distinct mapped regions per process. As of this writing:
//
-// Darwin unlimited
-// DragonFly 1000000 (vm.max_proc_mmap)
-// FreeBSD unlimited
-// Linux 65530 (vm.max_map_count) // TODO: query /proc/sys/vm/max_map_count?
-// NetBSD unlimited
-// OpenBSD unlimited
+// Darwin unlimited
+// DragonFly 1000000 (vm.max_proc_mmap)
+// FreeBSD unlimited
+// Linux 65530 (vm.max_map_count) // TODO: query /proc/sys/vm/max_map_count?
+// NetBSD unlimited
+// OpenBSD unlimited
var mmapLimit int32 = 1<<31 - 1
func init() {
diff --git a/src/cmd/internal/gcprog/gcprog.go b/src/cmd/internal/gcprog/gcprog.go
index c8bf2064687..eeea53daf4a 100644
--- a/src/cmd/internal/gcprog/gcprog.go
+++ b/src/cmd/internal/gcprog/gcprog.go
@@ -5,7 +5,7 @@
// Package gcprog implements an encoder for packed GC pointer bitmaps,
// known as GC programs.
//
-// Program Format
+// # Program Format
//
// The GC program encodes a sequence of 0 and 1 bits indicating scalar or pointer words in an object.
// The encoding is a simple Lempel-Ziv program, with codes to emit literal bits and to repeat the
@@ -20,7 +20,6 @@
//
// The numbers n and c, when they follow a code, are encoded as varints
// using the same encoding as encoding/binary's Uvarint.
-//
package gcprog
import (
diff --git a/src/cmd/internal/goobj/objfile.go b/src/cmd/internal/goobj/objfile.go
index af2a0df338b..3e36c461fa4 100644
--- a/src/cmd/internal/goobj/objfile.go
+++ b/src/cmd/internal/goobj/objfile.go
@@ -264,15 +264,16 @@ func (p *ImportedPkg) Write(w *Writer) {
// Symbol definition.
//
// Serialized format:
-// Sym struct {
-// Name string
-// ABI uint16
-// Type uint8
-// Flag uint8
-// Flag2 uint8
-// Siz uint32
-// Align uint32
-// }
+//
+// Sym struct {
+// Name string
+// ABI uint16
+// Type uint8
+// Flag uint8
+// Flag2 uint8
+// Siz uint32
+// Align uint32
+// }
type Sym [SymSize]byte
const SymSize = stringRefSize + 2 + 1 + 1 + 1 + 4 + 4
@@ -371,13 +372,14 @@ const HashSize = sha1.Size
// Relocation.
//
// Serialized format:
-// Reloc struct {
-// Off int32
-// Siz uint8
-// Type uint16
-// Add int64
-// Sym SymRef
-// }
+//
+// Reloc struct {
+// Off int32
+// Siz uint8
+// Type uint16
+// Add int64
+// Sym SymRef
+// }
type Reloc [RelocSize]byte
const RelocSize = 4 + 1 + 2 + 8 + 8
@@ -415,10 +417,11 @@ func (r *Reloc) fromBytes(b []byte) { copy(r[:], b) }
// Aux symbol info.
//
// Serialized format:
-// Aux struct {
-// Type uint8
-// Sym SymRef
-// }
+//
+// Aux struct {
+// Type uint8
+// Sym SymRef
+// }
type Aux [AuxSize]byte
const AuxSize = 1 + 8
@@ -458,11 +461,12 @@ func (a *Aux) fromBytes(b []byte) { copy(a[:], b) }
// Referenced symbol flags.
//
// Serialized format:
-// RefFlags struct {
-// Sym symRef
-// Flag uint8
-// Flag2 uint8
-// }
+//
+// RefFlags struct {
+// Sym symRef
+// Flag uint8
+// Flag2 uint8
+// }
type RefFlags [RefFlagsSize]byte
const RefFlagsSize = 8 + 1 + 1
@@ -490,10 +494,11 @@ const huge = (1<<31 - 1) / RelocSize
// Referenced symbol name.
//
// Serialized format:
-// RefName struct {
-// Sym symRef
-// Name string
-// }
+//
+// RefName struct {
+// Sym symRef
+// Name string
+// }
type RefName [RefNameSize]byte
const RefNameSize = 8 + stringRefSize
diff --git a/src/cmd/internal/obj/arm64/asm7.go b/src/cmd/internal/obj/arm64/asm7.go
index 72c4cd48ed0..57d4e7a8d32 100644
--- a/src/cmd/internal/obj/arm64/asm7.go
+++ b/src/cmd/internal/obj/arm64/asm7.go
@@ -3977,7 +3977,7 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) {
if (o1&S64) == 0 && s >= 2 {
c.ctxt.Diag("illegal bit position\n%v", p)
}
- if ((d >> uint(s*16)) >> 16) != 0 {
+ if ((uint64(d) >> uint(s*16)) >> 16) != 0 {
c.ctxt.Diag("requires uimm16\n%v", p)
}
rt := int(p.To.Reg)
diff --git a/src/cmd/internal/obj/arm64/asm_arm64_test.go b/src/cmd/internal/obj/arm64/asm_arm64_test.go
index c6a00f5b941..f468b6b0fe9 100644
--- a/src/cmd/internal/obj/arm64/asm_arm64_test.go
+++ b/src/cmd/internal/obj/arm64/asm_arm64_test.go
@@ -160,3 +160,14 @@ func TestVMOVQ(t *testing.T) {
t.Errorf("TestVMOVQ got: a=0x%x, b=0x%x, want: a=0x7040201008040201, b=0x3040201008040201", a, b)
}
}
+
+func testmovk() uint64
+
+// TestMOVK makes sure MOVK with a very large constant works. See issue 52261.
+func TestMOVK(t *testing.T) {
+ x := testmovk()
+ want := uint64(40000 << 48)
+ if x != want {
+ t.Errorf("TestMOVK got %x want %x\n", x, want)
+ }
+}
diff --git a/src/cmd/internal/obj/arm64/asm_arm64_test.s b/src/cmd/internal/obj/arm64/asm_arm64_test.s
index 9d337a4fd1a..f85433c6e3c 100644
--- a/src/cmd/internal/obj/arm64/asm_arm64_test.s
+++ b/src/cmd/internal/obj/arm64/asm_arm64_test.s
@@ -12,3 +12,10 @@ TEXT ·testvmovq(SB), NOSPLIT, $0-16
MOVD R0, r1+0(FP)
MOVD R1, r2+8(FP)
RET
+
+// testmovk() uint64
+TEXT ·testmovk(SB), NOSPLIT, $0-8
+ MOVD $0, R0
+ MOVK $(40000<<48), R0
+ MOVD R0, ret+0(FP)
+ RET
diff --git a/src/cmd/internal/obj/arm64/doc.go b/src/cmd/internal/obj/arm64/doc.go
index 2763cf4139a..c12f618e939 100644
--- a/src/cmd/internal/obj/arm64/doc.go
+++ b/src/cmd/internal/obj/arm64/doc.go
@@ -6,24 +6,26 @@
Package arm64 implements an ARM64 assembler. Go assembly syntax is different from GNU ARM64
syntax, but we can still follow the general rules to map between them.
-Instructions mnemonics mapping rules
+# Instructions mnemonics mapping rules
1. Most instructions use width suffixes of instruction names to indicate operand width rather than
using different register names.
Examples:
- ADC R24, R14, R12 <=> adc x12, x24
- ADDW R26->24, R21, R15 <=> add w15, w21, w26, asr #24
- FCMPS F2, F3 <=> fcmp s3, s2
- FCMPD F2, F3 <=> fcmp d3, d2
- FCVTDH F2, F3 <=> fcvt h3, d2
+
+ ADC R24, R14, R12 <=> adc x12, x24
+ ADDW R26->24, R21, R15 <=> add w15, w21, w26, asr #24
+ FCMPS F2, F3 <=> fcmp s3, s2
+ FCMPD F2, F3 <=> fcmp d3, d2
+ FCVTDH F2, F3 <=> fcvt h3, d2
2. Go uses .P and .W suffixes to indicate post-increment and pre-increment.
Examples:
- MOVD.P -8(R10), R8 <=> ldr x8, [x10],#-8
- MOVB.W 16(R16), R10 <=> ldrsb x10, [x16,#16]!
- MOVBU.W 16(R16), R10 <=> ldrb x10, [x16,#16]!
+
+ MOVD.P -8(R10), R8 <=> ldr x8, [x10],#-8
+ MOVB.W 16(R16), R10 <=> ldrsb x10, [x16,#16]!
+ MOVBU.W 16(R16), R10 <=> ldrb x10, [x16,#16]!
3. Go uses a series of MOV instructions as load and store.
@@ -40,11 +42,12 @@ ldrsh, sturh, strh => MOVH.
instructions and floating-point(scalar) instructions.
Examples:
- VADD V5.H8, V18.H8, V9.H8 <=> add v9.8h, v18.8h, v5.8h
- VLD1.P (R6)(R11), [V31.D1] <=> ld1 {v31.1d}, [x6], x11
- VFMLA V29.S2, V20.S2, V14.S2 <=> fmla v14.2s, v20.2s, v29.2s
- AESD V22.B16, V19.B16 <=> aesd v19.16b, v22.16b
- SCVTFWS R3, F16 <=> scvtf s17, w6
+
+ VADD V5.H8, V18.H8, V9.H8 <=> add v9.8h, v18.8h, v5.8h
+ VLD1.P (R6)(R11), [V31.D1] <=> ld1 {v31.1d}, [x6], x11
+ VFMLA V29.S2, V20.S2, V14.S2 <=> fmla v14.2s, v20.2s, v29.2s
+ AESD V22.B16, V19.B16 <=> aesd v19.16b, v22.16b
+ SCVTFWS R3, F16 <=> scvtf s17, w6
6. Align directive
@@ -53,10 +56,11 @@ to a specified boundary by padding with NOOP instruction. The alignment value su
must be a power of 2 and in the range of [8, 2048].
Examples:
- PCALIGN $16
- MOVD $2, R0 // This instruction is aligned with 16 bytes.
- PCALIGN $1024
- MOVD $3, R1 // This instruction is aligned with 1024 bytes.
+
+ PCALIGN $16
+ MOVD $2, R0 // This instruction is aligned with 16 bytes.
+ PCALIGN $1024
+ MOVD $3, R1 // This instruction is aligned with 1024 bytes.
PCALIGN also changes the function alignment. If a function has one or more PCALIGN directives,
its address will be aligned to the same or coarser boundary, which is the maximum of all the
@@ -65,13 +69,14 @@ alignment values.
In the following example, the function Add is aligned with 128 bytes.
Examples:
- TEXT ·Add(SB),$40-16
- MOVD $2, R0
- PCALIGN $32
- MOVD $4, R1
- PCALIGN $128
- MOVD $8, R2
- RET
+
+ TEXT ·Add(SB),$40-16
+ MOVD $2, R0
+ PCALIGN $32
+ MOVD $4, R1
+ PCALIGN $128
+ MOVD $8, R2
+ RET
On arm64, functions in Go are aligned to 16 bytes by default, we can also use PCALGIN to set the
function alignment. The functions that need to be aligned are preferably using NOFRAME and NOSPLIT
@@ -81,11 +86,12 @@ have the same alignment as the first hand-written instruction.
In the following example, PCALIGN at the entry of the function Add will align its address to 2048 bytes.
Examples:
- TEXT ·Add(SB),NOSPLIT|NOFRAME,$0
- PCALIGN $2048
- MOVD $1, R0
- MOVD $1, R1
- RET
+
+ TEXT ·Add(SB),NOSPLIT|NOFRAME,$0
+ PCALIGN $2048
+ MOVD $1, R0
+ MOVD $1, R1
+ RET
7. Move large constants to vector registers.
@@ -93,9 +99,10 @@ Go asm uses VMOVQ/VMOVD/VMOVS to move 128-bit, 64-bit and 32-bit constants into
And for a 128-bit interger, it take two 64-bit operands, for the low and high parts separately.
Examples:
- VMOVS $0x11223344, V0
- VMOVD $0x1122334455667788, V1
- VMOVQ $0x1122334455667788, $0x99aabbccddeeff00, V2 // V2=0x99aabbccddeeff001122334455667788
+
+ VMOVS $0x11223344, V0
+ VMOVD $0x1122334455667788, V1
+ VMOVQ $0x1122334455667788, $0x99aabbccddeeff00, V2 // V2=0x99aabbccddeeff001122334455667788
8. Move an optionally-shifted 16-bit immediate value to a register.
@@ -106,9 +113,10 @@ is the 16-bit unsigned immediate, in the range 0 to 65535; For the 32-bit varian
The current Go assembler does not accept zero shifts, such as "op $0, Rd" and "op $(0<<(16|32|48)), Rd" instructions.
Examples:
- MOVK $(10<<32), R20 <=> movk x20, #10, lsl #32
- MOVZW $(20<<16), R8 <=> movz w8, #20, lsl #16
- MOVK $(0<<16), R10 will be reported as an error by the assembler.
+
+ MOVK $(10<<32), R20 <=> movk x20, #10, lsl #32
+ MOVZW $(20<<16), R8 <=> movz w8, #20, lsl #16
+ MOVK $(0<<16), R10 will be reported as an error by the assembler.
Special Cases.
@@ -123,15 +131,15 @@ related to real ARM64 instruction. NOOP serves for the hardware nop instruction.
HINT $0.
Examples:
- VMOV V13.B[1], R20 <=> mov x20, v13.b[1]
- VMOV V13.H[1], R20 <=> mov w20, v13.h[1]
- JMP (R3) <=> br x3
- CALL (R17) <=> blr x17
- LDAXRB (R19), R16 <=> ldaxrb w16, [x19]
- NOOP <=> nop
+ VMOV V13.B[1], R20 <=> mov x20, v13.b[1]
+ VMOV V13.H[1], R20 <=> mov w20, v13.h[1]
+ JMP (R3) <=> br x3
+ CALL (R17) <=> blr x17
+ LDAXRB (R19), R16 <=> ldaxrb w16, [x19]
+ NOOP <=> nop
-Register mapping rules
+# Register mapping rules
1. All basic register names are written as Rn.
@@ -140,16 +148,16 @@ Register mapping rules
3. Bn, Hn, Dn, Sn and Qn instructions are written as Fn in floating-point instructions and as Vn
in SIMD instructions.
-
-Argument mapping rules
+# Argument mapping rules
1. The operands appear in left-to-right assignment order.
Go reverses the arguments of most instructions.
Examples:
- ADD R11.SXTB<<1, RSP, R25 <=> add x25, sp, w11, sxtb #1
- VADD V16, V19, V14 <=> add d14, d19, d16
+
+ ADD R11.SXTB<<1, RSP, R25 <=> add x25, sp, w11, sxtb #1
+ VADD V16, V19, V14 <=> add d14, d19, d16
Special Cases.
@@ -157,70 +165,79 @@ Special Cases.
such as str, stur, strb, sturb, strh, sturh stlr, stlrb. stlrh, st1.
Examples:
- MOVD R29, 384(R19) <=> str x29, [x19,#384]
- MOVB.P R30, 30(R4) <=> strb w30, [x4],#30
- STLRH R21, (R19) <=> stlrh w21, [x19]
+
+ MOVD R29, 384(R19) <=> str x29, [x19,#384]
+ MOVB.P R30, 30(R4) <=> strb w30, [x4],#30
+ STLRH R21, (R19) <=> stlrh w21, [x19]
(2) MADD, MADDW, MSUB, MSUBW, SMADDL, SMSUBL, UMADDL, UMSUBL <Rm>, <Ra>, <Rn>, <Rd>
Examples:
- MADD R2, R30, R22, R6 <=> madd x6, x22, x2, x30
- SMSUBL R10, R3, R17, R27 <=> smsubl x27, w17, w10, x3
+
+ MADD R2, R30, R22, R6 <=> madd x6, x22, x2, x30
+ SMSUBL R10, R3, R17, R27 <=> smsubl x27, w17, w10, x3
(3) FMADDD, FMADDS, FMSUBD, FMSUBS, FNMADDD, FNMADDS, FNMSUBD, FNMSUBS <Fm>, <Fa>, <Fn>, <Fd>
Examples:
- FMADDD F30, F20, F3, F29 <=> fmadd d29, d3, d30, d20
- FNMSUBS F7, F25, F7, F22 <=> fnmsub s22, s7, s7, s25
+
+ FMADDD F30, F20, F3, F29 <=> fmadd d29, d3, d30, d20
+ FNMSUBS F7, F25, F7, F22 <=> fnmsub s22, s7, s7, s25
(4) BFI, BFXIL, SBFIZ, SBFX, UBFIZ, UBFX $<lsb>, <Rn>, $<width>, <Rd>
Examples:
- BFIW $16, R20, $6, R0 <=> bfi w0, w20, #16, #6
- UBFIZ $34, R26, $5, R20 <=> ubfiz x20, x26, #34, #5
+
+ BFIW $16, R20, $6, R0 <=> bfi w0, w20, #16, #6
+ UBFIZ $34, R26, $5, R20 <=> ubfiz x20, x26, #34, #5
(5) FCCMPD, FCCMPS, FCCMPED, FCCMPES <cond>, Fm. Fn, $<nzcv>
Examples:
- FCCMPD AL, F8, F26, $0 <=> fccmp d26, d8, #0x0, al
- FCCMPS VS, F29, F4, $4 <=> fccmp s4, s29, #0x4, vs
- FCCMPED LE, F20, F5, $13 <=> fccmpe d5, d20, #0xd, le
- FCCMPES NE, F26, F10, $0 <=> fccmpe s10, s26, #0x0, ne
+
+ FCCMPD AL, F8, F26, $0 <=> fccmp d26, d8, #0x0, al
+ FCCMPS VS, F29, F4, $4 <=> fccmp s4, s29, #0x4, vs
+ FCCMPED LE, F20, F5, $13 <=> fccmpe d5, d20, #0xd, le
+ FCCMPES NE, F26, F10, $0 <=> fccmpe s10, s26, #0x0, ne
(6) CCMN, CCMNW, CCMP, CCMPW <cond>, <Rn>, $<imm>, $<nzcv>
Examples:
- CCMP MI, R22, $12, $13 <=> ccmp x22, #0xc, #0xd, mi
- CCMNW AL, R1, $11, $8 <=> ccmn w1, #0xb, #0x8, al
+
+ CCMP MI, R22, $12, $13 <=> ccmp x22, #0xc, #0xd, mi
+ CCMNW AL, R1, $11, $8 <=> ccmn w1, #0xb, #0x8, al
(7) CCMN, CCMNW, CCMP, CCMPW <cond>, <Rn>, <Rm>, $<nzcv>
Examples:
- CCMN VS, R13, R22, $10 <=> ccmn x13, x22, #0xa, vs
- CCMPW HS, R19, R14, $11 <=> ccmp w19, w14, #0xb, cs
+
+ CCMN VS, R13, R22, $10 <=> ccmn x13, x22, #0xa, vs
+ CCMPW HS, R19, R14, $11 <=> ccmp w19, w14, #0xb, cs
(9) CSEL, CSELW, CSNEG, CSNEGW, CSINC, CSINCW <cond>, <Rn>, <Rm>, <Rd> ;
FCSELD, FCSELS <cond>, <Fn>, <Fm>, <Fd>
Examples:
- CSEL GT, R0, R19, R1 <=> csel x1, x0, x19, gt
- CSNEGW GT, R7, R17, R8 <=> csneg w8, w7, w17, gt
- FCSELD EQ, F15, F18, F16 <=> fcsel d16, d15, d18, eq
-(10) TBNZ, TBZ $<imm>, <Rt>, <label>
+ CSEL GT, R0, R19, R1 <=> csel x1, x0, x19, gt
+ CSNEGW GT, R7, R17, R8 <=> csneg w8, w7, w17, gt
+ FCSELD EQ, F15, F18, F16 <=> fcsel d16, d15, d18, eq
+(10) TBNZ, TBZ $<imm>, <Rt>, <label>
(11) STLXR, STLXRW, STXR, STXRW, STLXRB, STLXRH, STXRB, STXRH <Rf>, (<Rn|RSP>), <Rs>
Examples:
- STLXR ZR, (R15), R16 <=> stlxr w16, xzr, [x15]
- STXRB R9, (R21), R19 <=> stxrb w19, w9, [x21]
+
+ STLXR ZR, (R15), R16 <=> stlxr w16, xzr, [x15]
+ STXRB R9, (R21), R19 <=> stxrb w19, w9, [x21]
(12) STLXP, STLXPW, STXP, STXPW (<Rf1>, <Rf2>), (<Rn|RSP>), <Rs>
Examples:
- STLXP (R17, R19), (R4), R5 <=> stlxp w5, x17, x19, [x4]
- STXPW (R30, R25), (R22), R13 <=> stxp w13, w30, w25, [x22]
+
+ STLXP (R17, R19), (R4), R5 <=> stlxp w5, x17, x19, [x4]
+ STXPW (R30, R25), (R22), R13 <=> stxp w13, w30, w25, [x22]
2. Expressions for special arguments.
@@ -229,45 +246,51 @@ Examples:
Optionally-shifted immediate.
Examples:
- ADD $(3151<<12), R14, R20 <=> add x20, x14, #0xc4f, lsl #12
- ADDW $1864, R25, R6 <=> add w6, w25, #0x748
+
+ ADD $(3151<<12), R14, R20 <=> add x20, x14, #0xc4f, lsl #12
+ ADDW $1864, R25, R6 <=> add w6, w25, #0x748
Optionally-shifted registers are written as <Rm>{<shift><amount>}.
The <shift> can be <<(lsl), >>(lsr), ->(asr), @>(ror).
Examples:
- ADD R19>>30, R10, R24 <=> add x24, x10, x19, lsr #30
- ADDW R26->24, R21, R15 <=> add w15, w21, w26, asr #24
+
+ ADD R19>>30, R10, R24 <=> add x24, x10, x19, lsr #30
+ ADDW R26->24, R21, R15 <=> add w15, w21, w26, asr #24
Extended registers are written as <Rm>{.<extend>{<<<amount>}}.
<extend> can be UXTB, UXTH, UXTW, UXTX, SXTB, SXTH, SXTW or SXTX.
Examples:
- ADDS R19.UXTB<<4, R9, R26 <=> adds x26, x9, w19, uxtb #4
- ADDSW R14.SXTX, R14, R6 <=> adds w6, w14, w14, sxtx
+
+ ADDS R19.UXTB<<4, R9, R26 <=> adds x26, x9, w19, uxtb #4
+ ADDSW R14.SXTX, R14, R6 <=> adds w6, w14, w14, sxtx
Memory references: [<Xn|SP>{,#0}] is written as (Rn|RSP), a base register and an immediate
offset is written as imm(Rn|RSP), a base register and an offset register is written as (Rn|RSP)(Rm).
Examples:
- LDAR (R22), R9 <=> ldar x9, [x22]
- LDP 28(R17), (R15, R23) <=> ldp x15, x23, [x17,#28]
- MOVWU (R4)(R12<<2), R8 <=> ldr w8, [x4, x12, lsl #2]
- MOVD (R7)(R11.UXTW<<3), R25 <=> ldr x25, [x7,w11,uxtw #3]
- MOVBU (R27)(R23), R14 <=> ldrb w14, [x27,x23]
+
+ LDAR (R22), R9 <=> ldar x9, [x22]
+ LDP 28(R17), (R15, R23) <=> ldp x15, x23, [x17,#28]
+ MOVWU (R4)(R12<<2), R8 <=> ldr w8, [x4, x12, lsl #2]
+ MOVD (R7)(R11.UXTW<<3), R25 <=> ldr x25, [x7,w11,uxtw #3]
+ MOVBU (R27)(R23), R14 <=> ldrb w14, [x27,x23]
Register pairs are written as (Rt1, Rt2).
Examples:
- LDP.P -240(R11), (R12, R26) <=> ldp x12, x26, [x11],#-240
+
+ LDP.P -240(R11), (R12, R26) <=> ldp x12, x26, [x11],#-240
Register with arrangement and register with arrangement and index.
Examples:
- VADD V5.H8, V18.H8, V9.H8 <=> add v9.8h, v18.8h, v5.8h
- VLD1 (R2), [V21.B16] <=> ld1 {v21.16b}, [x2]
- VST1.P V9.S[1], (R16)(R21) <=> st1 {v9.s}[1], [x16], x28
- VST1.P [V13.H8, V14.H8, V15.H8], (R3)(R14) <=> st1 {v13.8h-v15.8h}, [x3], x14
- VST1.P [V14.D1, V15.D1], (R7)(R23) <=> st1 {v14.1d, v15.1d}, [x7], x23
+
+ VADD V5.H8, V18.H8, V9.H8 <=> add v9.8h, v18.8h, v5.8h
+ VLD1 (R2), [V21.B16] <=> ld1 {v21.16b}, [x2]
+ VST1.P V9.S[1], (R16)(R21) <=> st1 {v9.s}[1], [x16], x28
+ VST1.P [V13.H8, V14.H8, V15.H8], (R3)(R14) <=> st1 {v13.8h-v15.8h}, [x3], x14
+ VST1.P [V14.D1, V15.D1], (R7)(R23) <=> st1 {v14.1d, v15.1d}, [x7], x23
*/
package arm64
diff --git a/src/cmd/internal/obj/inl.go b/src/cmd/internal/obj/inl.go
index d65a7357a6f..de3c73d693f 100644
--- a/src/cmd/internal/obj/inl.go
+++ b/src/cmd/internal/obj/inl.go
@@ -13,29 +13,29 @@ import "cmd/internal/src"
// every time a function is inlined. For example, suppose f() calls g()
// and g has two calls to h(), and that f, g, and h are inlineable:
//
-// 1 func main() {
-// 2 f()
-// 3 }
-// 4 func f() {
-// 5 g()
-// 6 }
-// 7 func g() {
-// 8 h()
-// 9 h()
-// 10 }
-// 11 func h() {
-// 12 println("H")
-// 13 }
+// 1 func main() {
+// 2 f()
+// 3 }
+// 4 func f() {
+// 5 g()
+// 6 }
+// 7 func g() {
+// 8 h()
+// 9 h()
+// 10 }
+// 11 func h() {
+// 12 println("H")
+// 13 }
//
// Assuming the global tree starts empty, inlining will produce the
// following tree:
//
-// []InlinedCall{
-// {Parent: -1, Func: "f", Pos: <line 2>},
-// {Parent: 0, Func: "g", Pos: <line 5>},
-// {Parent: 1, Func: "h", Pos: <line 8>},
-// {Parent: 1, Func: "h", Pos: <line 9>},
-// }
+// []InlinedCall{
+// {Parent: -1, Func: "f", Pos: <line 2>},
+// {Parent: 0, Func: "g", Pos: <line 5>},
+// {Parent: 1, Func: "h", Pos: <line 8>},
+// {Parent: 1, Func: "h", Pos: <line 9>},
+// }
//
// The nodes of h inlined into main will have inlining indexes 2 and 3.
//
diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go
index a3eba73906f..5f6c135f8c4 100644
--- a/src/cmd/internal/obj/link.go
+++ b/src/cmd/internal/obj/link.go
@@ -495,10 +495,20 @@ type FuncInfo struct {
ArgInfo *LSym // argument info for traceback
ArgLiveInfo *LSym // argument liveness info for traceback
WrapInfo *LSym // for wrapper, info of wrapped function
+ JumpTables []JumpTable
FuncInfoSym *LSym
}
+// JumpTable represents a table used for implementing multi-way
+// computed branching, used typically for implementing switches.
+// Sym is the table itself, and Targets is a list of target
+// instructions to go to for the computed branch index.
+type JumpTable struct {
+ Sym *LSym
+ Targets []*Prog
+}
+
// NewFuncInfo allocates and returns a FuncInfo for LSym.
func (s *LSym) NewFuncInfo() *FuncInfo {
if s.Extra != nil {
@@ -974,23 +984,6 @@ func (fi *FuncInfo) UnspillRegisterArgs(last *Prog, pa ProgAlloc) *Prog {
return last
}
-// The smallest possible offset from the hardware stack pointer to a local
-// variable on the stack. Architectures that use a link register save its value
-// on the stack in the function prologue and so always have a pointer between
-// the hardware stack pointer and the local variable area.
-func (ctxt *Link) FixedFrameSize() int64 {
- switch ctxt.Arch.Family {
- case sys.AMD64, sys.I386, sys.Wasm:
- return 0
- case sys.PPC64:
- // PIC code on ppc64le requires 32 bytes of stack, and it's easier to
- // just use that much stack always on ppc64x.
- return int64(4 * ctxt.Arch.PtrSize)
- default:
- return int64(ctxt.Arch.PtrSize)
- }
-}
-
// LinkArch is the definition of a single architecture.
type LinkArch struct {
*sys.Arch
diff --git a/src/cmd/internal/obj/mips/asm0.go b/src/cmd/internal/obj/mips/asm0.go
index e475ffdc146..ab8d37b1bee 100644
--- a/src/cmd/internal/obj/mips/asm0.go
+++ b/src/cmd/internal/obj/mips/asm0.go
@@ -415,7 +415,7 @@ func span0(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
return
}
- c := ctxt0{ctxt: ctxt, newprog: newprog, cursym: cursym, autosize: int32(p.To.Offset + ctxt.FixedFrameSize())}
+ c := ctxt0{ctxt: ctxt, newprog: newprog, cursym: cursym, autosize: int32(p.To.Offset + ctxt.Arch.FixedFrameSize)}
if oprange[AOR&obj.AMask] == nil {
c.ctxt.Diag("mips ops not initialized, call mips.buildop first")
@@ -627,7 +627,7 @@ func (c *ctxt0) aclass(a *obj.Addr) int {
// a.Offset is still relative to pseudo-FP.
a.Reg = obj.REG_NONE
}
- c.instoffset = int64(c.autosize) + a.Offset + c.ctxt.FixedFrameSize()
+ c.instoffset = int64(c.autosize) + a.Offset + c.ctxt.Arch.FixedFrameSize
if c.instoffset >= -BIG && c.instoffset < BIG {
return C_SAUTO
}
@@ -695,7 +695,7 @@ func (c *ctxt0) aclass(a *obj.Addr) int {
// a.Offset is still relative to pseudo-FP.
a.Reg = obj.REG_NONE
}
- c.instoffset = int64(c.autosize) + a.Offset + c.ctxt.FixedFrameSize()
+ c.instoffset = int64(c.autosize) + a.Offset + c.ctxt.Arch.FixedFrameSize
if c.instoffset >= -BIG && c.instoffset < BIG {
return C_SACON
}
diff --git a/src/cmd/internal/obj/mips/obj0.go b/src/cmd/internal/obj/mips/obj0.go
index b96a28a9442..2a2c8ecb75c 100644
--- a/src/cmd/internal/obj/mips/obj0.go
+++ b/src/cmd/internal/obj/mips/obj0.go
@@ -140,7 +140,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
p := c.cursym.Func().Text
textstksiz := p.To.Offset
- if textstksiz == -ctxt.FixedFrameSize() {
+ if textstksiz == -ctxt.Arch.FixedFrameSize {
// Historical way to mark NOFRAME.
p.From.Sym.Set(obj.AttrNoFrame, true)
textstksiz = 0
@@ -282,7 +282,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
if !p.From.Sym.NoFrame() {
// If there is a stack frame at all, it includes
// space to save the LR.
- autosize += int32(c.ctxt.FixedFrameSize())
+ autosize += int32(c.ctxt.Arch.FixedFrameSize)
}
if autosize&4 != 0 && c.ctxt.Arch.Family == sys.MIPS64 {
@@ -299,7 +299,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
}
}
- p.To.Offset = int64(autosize) - ctxt.FixedFrameSize()
+ p.To.Offset = int64(autosize) - ctxt.Arch.FixedFrameSize
if c.cursym.Func().Text.Mark&LEAF != 0 {
c.cursym.Set(obj.AttrLeaf, true)
@@ -392,7 +392,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
q = obj.Appendp(q, newprog)
q.As = add
q.From.Type = obj.TYPE_CONST
- q.From.Offset = int64(autosize) + ctxt.FixedFrameSize()
+ q.From.Offset = int64(autosize) + ctxt.Arch.FixedFrameSize
q.Reg = REGSP
q.To.Type = obj.TYPE_REG
q.To.Reg = REG_R3
@@ -409,7 +409,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
q = obj.Appendp(q, newprog)
q.As = add
q.From.Type = obj.TYPE_CONST
- q.From.Offset = ctxt.FixedFrameSize()
+ q.From.Offset = ctxt.Arch.FixedFrameSize
q.Reg = REGSP
q.To.Type = obj.TYPE_REG
q.To.Reg = REG_R2
diff --git a/src/cmd/internal/obj/objfile.go b/src/cmd/internal/obj/objfile.go
index e9302ee6421..c980a7cf2c5 100644
--- a/src/cmd/internal/obj/objfile.go
+++ b/src/cmd/internal/obj/objfile.go
@@ -448,14 +448,14 @@ func contentHash64(s *LSym) goobj.Hash64Type {
// Depending on the category of the referenced symbol, we choose
// different hash algorithms such that the hash is globally
// consistent.
-// - For referenced content-addressable symbol, its content hash
-// is globally consistent.
-// - For package symbol and builtin symbol, its local index is
-// globally consistent.
-// - For non-package symbol, its fully-expanded name is globally
-// consistent. For now, we require we know the current package
-// path so we can always expand symbol names. (Otherwise,
-// symbols with relocations are not considered hashable.)
+// - For referenced content-addressable symbol, its content hash
+// is globally consistent.
+// - For package symbol and builtin symbol, its local index is
+// globally consistent.
+// - For non-package symbol, its fully-expanded name is globally
+// consistent. For now, we require we know the current package
+// path so we can always expand symbol names. (Otherwise,
+// symbols with relocations are not considered hashable.)
//
// For now, we assume there is no circular dependencies among
// hashed symbols.
diff --git a/src/cmd/internal/obj/ppc64/asm9.go b/src/cmd/internal/obj/ppc64/asm9.go
index 50c9b37f02f..9ee469c6f22 100644
--- a/src/cmd/internal/obj/ppc64/asm9.go
+++ b/src/cmd/internal/obj/ppc64/asm9.go
@@ -917,7 +917,7 @@ func (c *ctxt9) aclass(a *obj.Addr) int {
return C_LOREG
case obj.NAME_PARAM:
- c.instoffset = int64(c.autosize) + a.Offset + c.ctxt.FixedFrameSize()
+ c.instoffset = int64(c.autosize) + a.Offset + c.ctxt.Arch.FixedFrameSize
if c.instoffset >= -BIG && c.instoffset < BIG {
return C_SOREG
}
@@ -983,7 +983,7 @@ func (c *ctxt9) aclass(a *obj.Addr) int {
return C_LACON
case obj.NAME_PARAM:
- c.instoffset = int64(c.autosize) + a.Offset + c.ctxt.FixedFrameSize()
+ c.instoffset = int64(c.autosize) + a.Offset + c.ctxt.Arch.FixedFrameSize
if c.instoffset >= -BIG && c.instoffset < BIG {
return C_SACON
}
diff --git a/src/cmd/internal/obj/ppc64/doc.go b/src/cmd/internal/obj/ppc64/doc.go
index bdaeaf69e1d..48aff3c69f7 100644
--- a/src/cmd/internal/obj/ppc64/doc.go
+++ b/src/cmd/internal/obj/ppc64/doc.go
@@ -32,7 +32,7 @@ for the target operand, which is first in PPC64 asm and last in Go asm.
Example:
- ADD R3, R4, R5 <=> add r5, r4, r3
+ ADD R3, R4, R5 <=> add r5, r4, r3
2. Constant operands
@@ -42,7 +42,7 @@ immediate value is used with the opcode if possible.
Example:
- ADD $1, R3, R4 <=> addi r4, r3, 1
+ ADD $1, R3, R4 <=> addi r4, r3, 1
3. Opcodes setting condition codes
@@ -55,7 +55,7 @@ vector instructions.
Example:
- ANDCC R3, R4, R5 <=> and. r5, r3, r4 (set CR0)
+ ANDCC R3, R4, R5 <=> and. r5, r3, r4 (set CR0)
4. Loads and stores from memory
@@ -79,20 +79,20 @@ is updated by the value in the index register.
Examples:
- MOVD (R3), R4 <=> ld r4,0(r3)
- MOVW (R3), R4 <=> lwa r4,0(r3)
- MOVWZU 4(R3), R4 <=> lwzu r4,4(r3)
- MOVWZ (R3+R5), R4 <=> lwzx r4,r3,r5
- MOVHZ (R3), R4 <=> lhz r4,0(r3)
- MOVHU 2(R3), R4 <=> lhau r4,2(r3)
- MOVBZ (R3), R4 <=> lbz r4,0(r3)
-
- MOVD R4,(R3) <=> std r4,0(r3)
- MOVW R4,(R3) <=> stw r4,0(r3)
- MOVW R4,(R3+R5) <=> stwx r4,r3,r5
- MOVWU R4,4(R3) <=> stwu r4,4(r3)
- MOVH R4,2(R3) <=> sth r4,2(r3)
- MOVBU R4,(R3)(R5) <=> stbux r4,r3,r5
+ MOVD (R3), R4 <=> ld r4,0(r3)
+ MOVW (R3), R4 <=> lwa r4,0(r3)
+ MOVWZU 4(R3), R4 <=> lwzu r4,4(r3)
+ MOVWZ (R3+R5), R4 <=> lwzx r4,r3,r5
+ MOVHZ (R3), R4 <=> lhz r4,0(r3)
+ MOVHU 2(R3), R4 <=> lhau r4,2(r3)
+ MOVBZ (R3), R4 <=> lbz r4,0(r3)
+
+ MOVD R4,(R3) <=> std r4,0(r3)
+ MOVW R4,(R3) <=> stw r4,0(r3)
+ MOVW R4,(R3+R5) <=> stwx r4,r3,r5
+ MOVWU R4,4(R3) <=> stwu r4,4(r3)
+ MOVH R4,2(R3) <=> sth r4,2(r3)
+ MOVBU R4,(R3)(R5) <=> stbux r4,r3,r5
4. Compares
@@ -110,8 +110,8 @@ bit settings.
Examples:
- CMP R3, R4 <=> cmp r3, r4 (CR0 assumed)
- CMP R3, R4, CR1 <=> cmp cr1, r3, r4
+ CMP R3, R4 <=> cmp r3, r4 (CR0 assumed)
+ CMP R3, R4, CR1 <=> cmp cr1, r3, r4
Note that the condition register is the target operand of compare opcodes, so
the remaining operands are in the same order for Go asm and PPC64 asm.
@@ -128,45 +128,45 @@ used operands.
BC op1, op2, op3
- op1: type of branch
- 16 -> bctr (branch on ctr)
- 12 -> bcr (branch if cr bit is set)
- 8 -> bcr+bctr (branch on ctr and cr values)
- 4 -> bcr != 0 (branch if specified cr bit is not set)
+ op1: type of branch
+ 16 -> bctr (branch on ctr)
+ 12 -> bcr (branch if cr bit is set)
+ 8 -> bcr+bctr (branch on ctr and cr values)
+ 4 -> bcr != 0 (branch if specified cr bit is not set)
- There are more combinations but these are the most common.
+ There are more combinations but these are the most common.
- op2: condition register field and condition bit
+ op2: condition register field and condition bit
- This contains an immediate value indicating which condition field
- to read and what bits to test. Each field is 4 bits long with CR0
- at bit 0, CR1 at bit 4, etc. The value is computed as 4*CR+condition
- with these condition values:
+ This contains an immediate value indicating which condition field
+ to read and what bits to test. Each field is 4 bits long with CR0
+ at bit 0, CR1 at bit 4, etc. The value is computed as 4*CR+condition
+ with these condition values:
- 0 -> LT
- 1 -> GT
- 2 -> EQ
- 3 -> OVG
+ 0 -> LT
+ 1 -> GT
+ 2 -> EQ
+ 3 -> OVG
- Thus 0 means test CR0 for LT, 5 means CR1 for GT, 30 means CR7 for EQ.
+ Thus 0 means test CR0 for LT, 5 means CR1 for GT, 30 means CR7 for EQ.
- op3: branch target
+ op3: branch target
Examples:
- BC 12, 0, target <=> blt cr0, target
- BC 12, 2, target <=> beq cr0, target
- BC 12, 5, target <=> bgt cr1, target
- BC 12, 30, target <=> beq cr7, target
- BC 4, 6, target <=> bne cr1, target
- BC 4, 1, target <=> ble cr1, target
+ BC 12, 0, target <=> blt cr0, target
+ BC 12, 2, target <=> beq cr0, target
+ BC 12, 5, target <=> bgt cr1, target
+ BC 12, 30, target <=> beq cr7, target
+ BC 4, 6, target <=> bne cr1, target
+ BC 4, 1, target <=> ble cr1, target
- The following extended opcodes are available for ease of use and readability:
+ The following extended opcodes are available for ease of use and readability:
- BNE CR2, target <=> bne cr2, target
- BEQ CR4, target <=> beq cr4, target
- BLT target <=> blt target (cr0 default)
- BGE CR7, target <=> bge cr7, target
+ BNE CR2, target <=> bne cr2, target
+ BEQ CR4, target <=> beq cr4, target
+ BLT target <=> blt target (cr0 default)
+ BGE CR7, target <=> bge cr7, target
Refer to the ISA for more information on additional values for the BC instruction,
how to handle OVG information, and much more.
@@ -202,12 +202,12 @@ the correct result, and the assembler does not add extra checking.
Examples:
- SRAD $8,R3,R4 => sradi r4,r3,8
- SRD $8,R3,R4 => rldicl r4,r3,56,8
- SLD $8,R3,R4 => rldicr r4,r3,8,55
- SRAW $16,R4,R5 => srawi r5,r4,16
- SRW $40,R4,R5 => rlwinm r5,r4,0,0,31
- SLW $12,R4,R5 => rlwinm r5,r4,12,0,19
+ SRAD $8,R3,R4 => sradi r4,r3,8
+ SRD $8,R3,R4 => rldicl r4,r3,56,8
+ SLD $8,R3,R4 => rldicr r4,r3,8,55
+ SRAW $16,R4,R5 => srawi r5,r4,16
+ SRW $40,R4,R5 => rlwinm r5,r4,0,0,31
+ SLW $12,R4,R5 => rlwinm r5,r4,12,0,19
Some non-simple shifts have operands in the Go assembly which don't map directly
onto operands in the PPC64 assembly. When an operand in a shift instruction in the
@@ -215,41 +215,40 @@ Go assembly is a bit mask, that mask is represented as a start and end bit in th
PPC64 assembly instead of a mask. See the ISA for more detail on these types of shifts.
Here are a few examples:
- RLWMI $7,R3,$65535,R6 => rlwimi r6,r3,7,16,31
- RLDMI $0,R4,$7,R6 => rldimi r6,r4,0,61
+ RLWMI $7,R3,$65535,R6 => rlwimi r6,r3,7,16,31
+ RLDMI $0,R4,$7,R6 => rldimi r6,r4,0,61
More recently, Go opcodes were added which map directly onto the PPC64 opcodes. It is
recommended to use the newer opcodes to avoid confusion.
- RLDICL $0,R4,$15,R6 => rldicl r6,r4,0,15
- RLDICR $0,R4,$15,R6 => rldicr r6.r4,0,15
+ RLDICL $0,R4,$15,R6 => rldicl r6,r4,0,15
+ RLDICR $0,R4,$15,R6 => rldicr r6.r4,0,15
-Register naming
+# Register naming
1. Special register usage in Go asm
The following registers should not be modified by user Go assembler code.
- R0: Go code expects this register to contain the value 0.
- R1: Stack pointer
- R2: TOC pointer when compiled with -shared or -dynlink (a.k.a position independent code)
- R13: TLS pointer
- R30: g (goroutine)
+ R0: Go code expects this register to contain the value 0.
+ R1: Stack pointer
+ R2: TOC pointer when compiled with -shared or -dynlink (a.k.a position independent code)
+ R13: TLS pointer
+ R30: g (goroutine)
Register names:
- Rn is used for general purpose registers. (0-31)
- Fn is used for floating point registers. (0-31)
- Vn is used for vector registers. Slot 0 of Vn overlaps with Fn. (0-31)
- VSn is used for vector-scalar registers. V0-V31 overlap with VS32-VS63. (0-63)
- CTR represents the count register.
- LR represents the link register.
- CR represents the condition register
- CRn represents a condition register field. (0-7)
- CRnLT represents CR bit 0 of CR field n. (0-7)
- CRnGT represents CR bit 1 of CR field n. (0-7)
- CRnEQ represents CR bit 2 of CR field n. (0-7)
- CRnSO represents CR bit 3 of CR field n. (0-7)
-
+ Rn is used for general purpose registers. (0-31)
+ Fn is used for floating point registers. (0-31)
+ Vn is used for vector registers. Slot 0 of Vn overlaps with Fn. (0-31)
+ VSn is used for vector-scalar registers. V0-V31 overlap with VS32-VS63. (0-63)
+ CTR represents the count register.
+ LR represents the link register.
+ CR represents the condition register
+ CRn represents a condition register field. (0-7)
+ CRnLT represents CR bit 0 of CR field n. (0-7)
+ CRnGT represents CR bit 1 of CR field n. (0-7)
+ CRnEQ represents CR bit 2 of CR field n. (0-7)
+ CRnSO represents CR bit 3 of CR field n. (0-7)
*/
package ppc64
diff --git a/src/cmd/internal/obj/ppc64/obj9.go b/src/cmd/internal/obj/ppc64/obj9.go
index c986c0d2b6e..098f1cd7fec 100644
--- a/src/cmd/internal/obj/ppc64/obj9.go
+++ b/src/cmd/internal/obj/ppc64/obj9.go
@@ -614,7 +614,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
if !p.From.Sym.NoFrame() {
// If there is a stack frame at all, it includes
// space to save the LR.
- autosize += int32(c.ctxt.FixedFrameSize())
+ autosize += int32(c.ctxt.Arch.FixedFrameSize)
}
if p.Mark&LEAF != 0 && autosize < objabi.StackSmall {
@@ -811,7 +811,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
q = obj.Appendp(q, c.newprog)
q.As = AADD
q.From.Type = obj.TYPE_CONST
- q.From.Offset = int64(autosize) + c.ctxt.FixedFrameSize()
+ q.From.Offset = int64(autosize) + c.ctxt.Arch.FixedFrameSize
q.Reg = REGSP
q.To.Type = obj.TYPE_REG
q.To.Reg = REG_R24
@@ -831,7 +831,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
q = obj.Appendp(q, c.newprog)
q.As = AADD
q.From.Type = obj.TYPE_CONST
- q.From.Offset = c.ctxt.FixedFrameSize()
+ q.From.Offset = c.ctxt.Arch.FixedFrameSize
q.Reg = REGSP
q.To.Type = obj.TYPE_REG
q.To.Reg = REG_R25
@@ -1009,6 +1009,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
/*
// instruction scheduling
+
if(debug['Q'] == 0)
return;
@@ -1065,7 +1066,7 @@ func (c *ctxt9) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
p = c.cursym.Func().SpillRegisterArgs(p, c.newprog)
// Save LR and REGCTXT
- frameSize := 8 + c.ctxt.FixedFrameSize()
+ frameSize := 8 + c.ctxt.Arch.FixedFrameSize
// MOVD LR, REGTMP
p = obj.Appendp(p, c.newprog)
diff --git a/src/cmd/internal/obj/riscv/obj.go b/src/cmd/internal/obj/riscv/obj.go
index 956d69ee2e7..b30958cb385 100644
--- a/src/cmd/internal/obj/riscv/obj.go
+++ b/src/cmd/internal/obj/riscv/obj.go
@@ -380,7 +380,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
// Save LR unless there is no frame.
if !text.From.Sym.NoFrame() {
- stacksize += ctxt.FixedFrameSize()
+ stacksize += ctxt.Arch.FixedFrameSize
}
cursym.Func().Args = text.To.Val.(int32)
@@ -415,17 +415,17 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
if cursym.Func().Text.From.Sym.Wrapper() {
// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
//
- // MOV g_panic(g), X11
- // BNE X11, ZERO, adjust
+ // MOV g_panic(g), X5
+ // BNE X5, ZERO, adjust
// end:
// NOP
// ...rest of function..
// adjust:
- // MOV panic_argp(X11), X12
- // ADD $(autosize+FIXED_FRAME), SP, X13
- // BNE X12, X13, end
- // ADD $FIXED_FRAME, SP, X12
- // MOV X12, panic_argp(X11)
+ // MOV panic_argp(X5), X6
+ // ADD $(autosize+FIXED_FRAME), SP, X7
+ // BNE X6, X7, end
+ // ADD $FIXED_FRAME, SP, X6
+ // MOV X6, panic_argp(X5)
// JMP end
//
// The NOP is needed to give the jumps somewhere to land.
@@ -435,11 +435,11 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
ldpanic.As = AMOV
ldpanic.From = obj.Addr{Type: obj.TYPE_MEM, Reg: REGG, Offset: 4 * int64(ctxt.Arch.PtrSize)} // G.panic
ldpanic.Reg = obj.REG_NONE
- ldpanic.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_X11}
+ ldpanic.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_X5}
bneadj := obj.Appendp(ldpanic, newprog)
bneadj.As = ABNE
- bneadj.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_X11}
+ bneadj.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_X5}
bneadj.Reg = REG_ZERO
bneadj.To.Type = obj.TYPE_BRANCH
@@ -453,22 +453,22 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
getargp := obj.Appendp(last, newprog)
getargp.As = AMOV
- getargp.From = obj.Addr{Type: obj.TYPE_MEM, Reg: REG_X11, Offset: 0} // Panic.argp
+ getargp.From = obj.Addr{Type: obj.TYPE_MEM, Reg: REG_X5, Offset: 0} // Panic.argp
getargp.Reg = obj.REG_NONE
- getargp.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_X12}
+ getargp.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_X6}
bneadj.To.SetTarget(getargp)
calcargp := obj.Appendp(getargp, newprog)
calcargp.As = AADDI
- calcargp.From = obj.Addr{Type: obj.TYPE_CONST, Offset: stacksize + ctxt.FixedFrameSize()}
+ calcargp.From = obj.Addr{Type: obj.TYPE_CONST, Offset: stacksize + ctxt.Arch.FixedFrameSize}
calcargp.Reg = REG_SP
- calcargp.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_X13}
+ calcargp.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_X7}
testargp := obj.Appendp(calcargp, newprog)
testargp.As = ABNE
- testargp.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_X12}
- testargp.Reg = REG_X13
+ testargp.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_X6}
+ testargp.Reg = REG_X7
testargp.To.Type = obj.TYPE_BRANCH
testargp.To.SetTarget(endadj)
@@ -476,13 +476,13 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
adjargp.As = AADDI
adjargp.From = obj.Addr{Type: obj.TYPE_CONST, Offset: int64(ctxt.Arch.PtrSize)}
adjargp.Reg = REG_SP
- adjargp.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_X12}
+ adjargp.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_X6}
setargp := obj.Appendp(adjargp, newprog)
setargp.As = AMOV
- setargp.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_X12}
+ setargp.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_X6}
setargp.Reg = obj.REG_NONE
- setargp.To = obj.Addr{Type: obj.TYPE_MEM, Reg: REG_X11, Offset: 0} // Panic.argp
+ setargp.To = obj.Addr{Type: obj.TYPE_MEM, Reg: REG_X5, Offset: 0} // Panic.argp
godone := obj.Appendp(setargp, newprog)
godone.As = AJAL
@@ -732,6 +732,11 @@ func stacksplit(ctxt *obj.Link, p *obj.Prog, cursym *obj.LSym, newprog obj.ProgA
// Save LR and REGCTXT
const frameSize = 16
p = ctxt.StartUnsafePoint(p, newprog)
+
+ // Spill Arguments. This has to happen before we open
+ // any more frame space.
+ p = cursym.Func().SpillRegisterArgs(p, newprog)
+
// MOV LR, -16(SP)
p = obj.Appendp(p, newprog)
p.As = AMOV
@@ -778,13 +783,15 @@ func stacksplit(ctxt *obj.Link, p *obj.Prog, cursym *obj.LSym, newprog obj.ProgA
p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_SP}
p.Spadj = -frameSize
+ // Unspill arguments
+ p = cursym.Func().UnspillRegisterArgs(p, newprog)
p = ctxt.EndUnsafePoint(p, newprog, -1)
}
// Jump back to here after morestack returns.
startPred := p
- // MOV g_stackguard(g), X10
+ // MOV g_stackguard(g), X6
p = obj.Appendp(p, newprog)
p.As = AMOV
p.From.Type = obj.TYPE_MEM
@@ -794,7 +801,7 @@ func stacksplit(ctxt *obj.Link, p *obj.Prog, cursym *obj.LSym, newprog obj.ProgA
p.From.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
}
p.To.Type = obj.TYPE_REG
- p.To.Reg = REG_X10
+ p.To.Reg = REG_X6
// Mark the stack bound check and morestack call async nonpreemptible.
// If we get preempted here, when resumed the preemption request is
@@ -811,7 +818,7 @@ func stacksplit(ctxt *obj.Link, p *obj.Prog, cursym *obj.LSym, newprog obj.ProgA
p = obj.Appendp(p, newprog)
p.As = ABLTU
p.From.Type = obj.TYPE_REG
- p.From.Reg = REG_X10
+ p.From.Reg = REG_X6
p.Reg = REG_SP
p.To.Type = obj.TYPE_BRANCH
to_done = p
@@ -826,52 +833,56 @@ func stacksplit(ctxt *obj.Link, p *obj.Prog, cursym *obj.LSym, newprog obj.ProgA
// stack guard to incorrectly succeed. We explicitly
// guard against underflow.
//
- // MOV $(framesize-StackSmall), X11
- // BLTU SP, X11, label-of-call-to-morestack
+ // MOV $(framesize-StackSmall), X7
+ // BLTU SP, X7, label-of-call-to-morestack
p = obj.Appendp(p, newprog)
p.As = AMOV
p.From.Type = obj.TYPE_CONST
p.From.Offset = offset
p.To.Type = obj.TYPE_REG
- p.To.Reg = REG_X11
+ p.To.Reg = REG_X7
p = obj.Appendp(p, newprog)
p.As = ABLTU
p.From.Type = obj.TYPE_REG
p.From.Reg = REG_SP
- p.Reg = REG_X11
+ p.Reg = REG_X7
p.To.Type = obj.TYPE_BRANCH
to_more = p
}
// Check against the stack guard. We've ensured this won't underflow.
- // ADD $-(framesize-StackSmall), SP, X11
- // // if X11 > stackguard { goto done }
- // BLTU stackguard, X11, done
+ // ADD $-(framesize-StackSmall), SP, X7
+ // // if X7 > stackguard { goto done }
+ // BLTU stackguard, X7, done
p = obj.Appendp(p, newprog)
p.As = AADDI
p.From.Type = obj.TYPE_CONST
p.From.Offset = -offset
p.Reg = REG_SP
p.To.Type = obj.TYPE_REG
- p.To.Reg = REG_X11
+ p.To.Reg = REG_X7
p = obj.Appendp(p, newprog)
p.As = ABLTU
p.From.Type = obj.TYPE_REG
- p.From.Reg = REG_X10
- p.Reg = REG_X11
+ p.From.Reg = REG_X6
+ p.Reg = REG_X7
p.To.Type = obj.TYPE_BRANCH
to_done = p
}
+ // Spill the register args that could be clobbered by the
+ // morestack code
p = ctxt.EmitEntryStackMap(cursym, p, newprog)
+ p = cursym.Func().SpillRegisterArgs(p, newprog)
// CALL runtime.morestack(SB)
p = obj.Appendp(p, newprog)
p.As = obj.ACALL
p.To.Type = obj.TYPE_BRANCH
+
if cursym.CFunc() {
p.To.Sym = ctxt.Lookup("runtime.morestackc")
} else if !cursym.Func().Text.From.Sym.NeedCtxt() {
@@ -884,6 +895,7 @@ func stacksplit(ctxt *obj.Link, p *obj.Prog, cursym *obj.LSym, newprog obj.ProgA
}
jalToSym(ctxt, p, REG_X5)
+ p = cursym.Func().UnspillRegisterArgs(p, newprog)
p = ctxt.EndUnsafePoint(p, newprog, -1)
// JMP start
diff --git a/src/cmd/internal/obj/s390x/asmz.go b/src/cmd/internal/obj/s390x/asmz.go
index 06921085c95..5760847bcd4 100644
--- a/src/cmd/internal/obj/s390x/asmz.go
+++ b/src/cmd/internal/obj/s390x/asmz.go
@@ -586,7 +586,7 @@ func (c *ctxtz) aclass(a *obj.Addr) int {
// a.Offset is still relative to pseudo-FP.
a.Reg = obj.REG_NONE
}
- c.instoffset = int64(c.autosize) + a.Offset + c.ctxt.FixedFrameSize()
+ c.instoffset = int64(c.autosize) + a.Offset + c.ctxt.Arch.FixedFrameSize
if c.instoffset >= -BIG && c.instoffset < BIG {
return C_SAUTO
}
@@ -657,7 +657,7 @@ func (c *ctxtz) aclass(a *obj.Addr) int {
// a.Offset is still relative to pseudo-FP.
a.Reg = obj.REG_NONE
}
- c.instoffset = int64(c.autosize) + a.Offset + c.ctxt.FixedFrameSize()
+ c.instoffset = int64(c.autosize) + a.Offset + c.ctxt.Arch.FixedFrameSize
if c.instoffset >= -BIG && c.instoffset < BIG {
return C_SACON
}
diff --git a/src/cmd/internal/obj/s390x/objz.go b/src/cmd/internal/obj/s390x/objz.go
index aebbf8dbc55..fed77037592 100644
--- a/src/cmd/internal/obj/s390x/objz.go
+++ b/src/cmd/internal/obj/s390x/objz.go
@@ -310,7 +310,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
if !p.From.Sym.NoFrame() {
// If there is a stack frame at all, it includes
// space to save the LR.
- autosize += int32(c.ctxt.FixedFrameSize())
+ autosize += int32(c.ctxt.Arch.FixedFrameSize)
}
if p.Mark&LEAF != 0 && autosize < objabi.StackSmall {
@@ -420,7 +420,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
q = obj.Appendp(q, c.newprog)
q.As = AADD
q.From.Type = obj.TYPE_CONST
- q.From.Offset = int64(autosize) + c.ctxt.FixedFrameSize()
+ q.From.Offset = int64(autosize) + c.ctxt.Arch.FixedFrameSize
q.Reg = REGSP
q.To.Type = obj.TYPE_REG
q.To.Reg = REG_R5
@@ -440,7 +440,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
q = obj.Appendp(q, c.newprog)
q.As = AADD
q.From.Type = obj.TYPE_CONST
- q.From.Offset = c.ctxt.FixedFrameSize()
+ q.From.Offset = c.ctxt.Arch.FixedFrameSize
q.Reg = REGSP
q.To.Type = obj.TYPE_REG
q.To.Reg = REG_R6
diff --git a/src/cmd/internal/obj/x86/asm6.go b/src/cmd/internal/obj/x86/asm6.go
index 64a0bc96b7a..b625845c094 100644
--- a/src/cmd/internal/obj/x86/asm6.go
+++ b/src/cmd/internal/obj/x86/asm6.go
@@ -872,9 +872,9 @@ var ysha1rnds4 = []ytab{
// up in instinit. For example, oclass distinguishes the constants 0 and 1
// from the more general 8-bit constants, but instinit says
//
-// ycover[Yi0*Ymax+Ys32] = 1
-// ycover[Yi1*Ymax+Ys32] = 1
-// ycover[Yi8*Ymax+Ys32] = 1
+// ycover[Yi0*Ymax+Ys32] = 1
+// ycover[Yi1*Ymax+Ys32] = 1
+// ycover[Yi8*Ymax+Ys32] = 1
//
// which means that Yi0, Yi1, and Yi8 all count as Ys32 (signed 32)
// if that's what an instruction can handle.
@@ -888,26 +888,28 @@ var ysha1rnds4 = []ytab{
// is, the Ztype) and the z bytes.
//
// For example, let's look at AADDL. The optab line says:
-// {AADDL, yaddl, Px, opBytes{0x83, 00, 0x05, 0x81, 00, 0x01, 0x03}},
+//
+// {AADDL, yaddl, Px, opBytes{0x83, 00, 0x05, 0x81, 00, 0x01, 0x03}},
//
// and yaddl says
-// var yaddl = []ytab{
-// {Yi8, Ynone, Yml, Zibo_m, 2},
-// {Yi32, Ynone, Yax, Zil_, 1},
-// {Yi32, Ynone, Yml, Zilo_m, 2},
-// {Yrl, Ynone, Yml, Zr_m, 1},
-// {Yml, Ynone, Yrl, Zm_r, 1},
-// }
+//
+// var yaddl = []ytab{
+// {Yi8, Ynone, Yml, Zibo_m, 2},
+// {Yi32, Ynone, Yax, Zil_, 1},
+// {Yi32, Ynone, Yml, Zilo_m, 2},
+// {Yrl, Ynone, Yml, Zr_m, 1},
+// {Yml, Ynone, Yrl, Zm_r, 1},
+// }
//
// so there are 5 possible types of ADDL instruction that can be laid down, and
// possible states used to lay them down (Ztype and z pointer, assuming z
// points at opBytes{0x83, 00, 0x05,0x81, 00, 0x01, 0x03}) are:
//
-// Yi8, Yml -> Zibo_m, z (0x83, 00)
-// Yi32, Yax -> Zil_, z+2 (0x05)
-// Yi32, Yml -> Zilo_m, z+2+1 (0x81, 0x00)
-// Yrl, Yml -> Zr_m, z+2+1+2 (0x01)
-// Yml, Yrl -> Zm_r, z+2+1+2+1 (0x03)
+// Yi8, Yml -> Zibo_m, z (0x83, 00)
+// Yi32, Yax -> Zil_, z+2 (0x05)
+// Yi32, Yml -> Zilo_m, z+2+1 (0x81, 0x00)
+// Yrl, Yml -> Zr_m, z+2+1+2 (0x01)
+// Yml, Yrl -> Zm_r, z+2+1+2+1 (0x03)
//
// The Pconstant in the optab line controls the prefix bytes to emit. That's
// relatively straightforward as this program goes.
@@ -917,7 +919,7 @@ var ysha1rnds4 = []ytab{
// encoded addressing mode for the Yml arg), and then a single immediate byte.
// Zilo_m is the same but a long (32-bit) immediate.
var optab =
-// as, ytab, andproto, opcode
+// as, ytab, andproto, opcode
[...]Optab{
{obj.AXXX, nil, 0, opBytes{}},
{AAAA, ynone, P32, opBytes{0x37}},
@@ -2228,6 +2230,16 @@ func span6(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) {
}
obj.MarkUnsafePoints(ctxt, s.Func().Text, newprog, useTLS, nil)
}
+
+ // Now that we know byte offsets, we can generate jump table entries.
+ // TODO: could this live in obj instead of obj/$ARCH?
+ for _, jt := range s.Func().JumpTables {
+ for i, p := range jt.Targets {
+ // The ith jumptable entry points to the p.Pc'th
+ // byte in the function symbol s.
+ jt.Sym.WriteAddr(ctxt, int64(i)*8, 8, s, p.Pc)
+ }
+ }
}
func instinit(ctxt *obj.Link) {
@@ -4162,6 +4174,7 @@ func (ab *AsmBuf) asmvex(ctxt *obj.Link, rm, v, r *obj.Addr, vex, opcode uint8)
// EVEX.R : 1 bit | EVEX extension bit | RxrEvex
//
// Examples:
+//
// REG_Z30 => 30
// REG_X15 => 15
// REG_R9 => 9
diff --git a/src/cmd/internal/obj/x86/evex.go b/src/cmd/internal/obj/x86/evex.go
index d8867283fa5..aa93cd8819a 100644
--- a/src/cmd/internal/obj/x86/evex.go
+++ b/src/cmd/internal/obj/x86/evex.go
@@ -168,6 +168,7 @@ func evexZcase(zcase uint8) bool {
// evexSuffixBits carries instruction EVEX suffix set flags.
//
// Examples:
+//
// "RU_SAE.Z" => {rounding: 3, zeroing: true}
// "Z" => {zeroing: true}
// "BCST" => {broadcast: true}
@@ -273,10 +274,10 @@ func ParseSuffix(p *obj.Prog, cond string) error {
// so we can burn some clocks to construct good error message.
//
// Reported issues:
-// - duplicated suffixes
-// - illegal rounding/SAE+broadcast combinations
-// - unknown suffixes
-// - misplaced suffix (e.g. wrong Z suffix position)
+// - duplicated suffixes
+// - illegal rounding/SAE+broadcast combinations
+// - unknown suffixes
+// - misplaced suffix (e.g. wrong Z suffix position)
func inferSuffixError(cond string) error {
suffixSet := make(map[string]bool) // Set for duplicates detection.
unknownSet := make(map[string]bool) // Set of unknown suffixes.
diff --git a/src/cmd/internal/objabi/funcid.go b/src/cmd/internal/objabi/funcid.go
index 084fcdf7121..c2eb4d545b7 100644
--- a/src/cmd/internal/objabi/funcid.go
+++ b/src/cmd/internal/objabi/funcid.go
@@ -49,26 +49,26 @@ const (
)
var funcIDs = map[string]FuncID{
- "abort": FuncID_abort,
- "asmcgocall": FuncID_asmcgocall,
- "asyncPreempt": FuncID_asyncPreempt,
- "cgocallback": FuncID_cgocallback,
- "debugCallV2": FuncID_debugCallV2,
- "gcBgMarkWorker": FuncID_gcBgMarkWorker,
- "go": FuncID_rt0_go,
- "goexit": FuncID_goexit,
- "gogo": FuncID_gogo,
- "gopanic": FuncID_gopanic,
- "handleAsyncEvent": FuncID_handleAsyncEvent,
- "main": FuncID_runtime_main,
- "mcall": FuncID_mcall,
- "morestack": FuncID_morestack,
- "mstart": FuncID_mstart,
- "panicwrap": FuncID_panicwrap,
- "runfinq": FuncID_runfinq,
- "sigpanic": FuncID_sigpanic,
- "switch": FuncID_systemstack_switch,
- "systemstack": FuncID_systemstack,
+ "abort": FuncID_abort,
+ "asmcgocall": FuncID_asmcgocall,
+ "asyncPreempt": FuncID_asyncPreempt,
+ "cgocallback": FuncID_cgocallback,
+ "debugCallV2": FuncID_debugCallV2,
+ "gcBgMarkWorker": FuncID_gcBgMarkWorker,
+ "rt0_go": FuncID_rt0_go,
+ "goexit": FuncID_goexit,
+ "gogo": FuncID_gogo,
+ "gopanic": FuncID_gopanic,
+ "handleAsyncEvent": FuncID_handleAsyncEvent,
+ "main": FuncID_runtime_main,
+ "mcall": FuncID_mcall,
+ "morestack": FuncID_morestack,
+ "mstart": FuncID_mstart,
+ "panicwrap": FuncID_panicwrap,
+ "runfinq": FuncID_runfinq,
+ "sigpanic": FuncID_sigpanic,
+ "systemstack_switch": FuncID_systemstack_switch,
+ "systemstack": FuncID_systemstack,
// Don't show in call stack but otherwise not special.
"deferreturn": FuncID_wrapper,
diff --git a/src/cmd/internal/objabi/symkind.go b/src/cmd/internal/objabi/symkind.go
index 28f430fc547..dba23a54bd0 100644
--- a/src/cmd/internal/objabi/symkind.go
+++ b/src/cmd/internal/objabi/symkind.go
@@ -37,6 +37,7 @@ type SymKind uint8
// These are used to index into cmd/link/internal/sym/AbiSymKindToSymKind
//
// TODO(rsc): Give idiomatic Go names.
+//
//go:generate stringer -type=SymKind
const (
// An otherwise invalid zero value for the type
diff --git a/src/cmd/internal/src/pos.go b/src/cmd/internal/src/pos.go
index 373a22a7f23..15f64acff83 100644
--- a/src/cmd/internal/src/pos.go
+++ b/src/cmd/internal/src/pos.go
@@ -214,8 +214,10 @@ func NewFileBase(filename, absFilename string) *PosBase {
}
// NewLinePragmaBase returns a new *PosBase for a line directive of the form
-// //line filename:line:col
-// /*line filename:line:col*/
+//
+// //line filename:line:col
+// /*line filename:line:col*/
+//
// at position pos.
func NewLinePragmaBase(pos Pos, filename, absFilename string, line, col uint) *PosBase {
return &PosBase{pos, filename, absFilename, FileSymPrefix + absFilename, line, col, -1}
diff --git a/src/cmd/internal/sys/arch.go b/src/cmd/internal/sys/arch.go
index ea76b596c15..5886b42e515 100644
--- a/src/cmd/internal/sys/arch.go
+++ b/src/cmd/internal/sys/arch.go
@@ -52,6 +52,22 @@ type Arch struct {
// can combine adjacent loads into a single larger, possibly unaligned, load.
// Note that currently the optimizations must be able to handle little endian byte order.
CanMergeLoads bool
+
+ // CanJumpTable reports whether the backend can handle
+ // compiling a jump table.
+ CanJumpTable bool
+
+ // HasLR indicates that this architecture uses a link register
+ // for calls.
+ HasLR bool
+
+ // FixedFrameSize is the smallest possible offset from the
+ // hardware stack pointer to a local variable on the stack.
+ // Architectures that use a link register save its value on
+ // the stack in the function prologue and so always have a
+ // pointer between the hardware stack pointer and the local
+ // variable area.
+ FixedFrameSize int64
}
// InFamily reports whether a is a member of any of the specified
@@ -66,102 +82,121 @@ func (a *Arch) InFamily(xs ...ArchFamily) bool {
}
var Arch386 = &Arch{
- Name: "386",
- Family: I386,
- ByteOrder: binary.LittleEndian,
- PtrSize: 4,
- RegSize: 4,
- MinLC: 1,
- Alignment: 1,
- CanMergeLoads: true,
+ Name: "386",
+ Family: I386,
+ ByteOrder: binary.LittleEndian,
+ PtrSize: 4,
+ RegSize: 4,
+ MinLC: 1,
+ Alignment: 1,
+ CanMergeLoads: true,
+ HasLR: false,
+ FixedFrameSize: 0,
}
var ArchAMD64 = &Arch{
- Name: "amd64",
- Family: AMD64,
- ByteOrder: binary.LittleEndian,
- PtrSize: 8,
- RegSize: 8,
- MinLC: 1,
- Alignment: 1,
- CanMergeLoads: true,
+ Name: "amd64",
+ Family: AMD64,
+ ByteOrder: binary.LittleEndian,
+ PtrSize: 8,
+ RegSize: 8,
+ MinLC: 1,
+ Alignment: 1,
+ CanMergeLoads: true,
+ CanJumpTable: true,
+ HasLR: false,
+ FixedFrameSize: 0,
}
var ArchARM = &Arch{
- Name: "arm",
- Family: ARM,
- ByteOrder: binary.LittleEndian,
- PtrSize: 4,
- RegSize: 4,
- MinLC: 4,
- Alignment: 4, // TODO: just for arm5?
- CanMergeLoads: false,
+ Name: "arm",
+ Family: ARM,
+ ByteOrder: binary.LittleEndian,
+ PtrSize: 4,
+ RegSize: 4,
+ MinLC: 4,
+ Alignment: 4, // TODO: just for arm5?
+ CanMergeLoads: false,
+ HasLR: true,
+ FixedFrameSize: 4, // LR
}
var ArchARM64 = &Arch{
- Name: "arm64",
- Family: ARM64,
- ByteOrder: binary.LittleEndian,
- PtrSize: 8,
- RegSize: 8,
- MinLC: 4,
- Alignment: 1,
- CanMergeLoads: true,
+ Name: "arm64",
+ Family: ARM64,
+ ByteOrder: binary.LittleEndian,
+ PtrSize: 8,
+ RegSize: 8,
+ MinLC: 4,
+ Alignment: 1,
+ CanMergeLoads: true,
+ HasLR: true,
+ FixedFrameSize: 8, // LR
}
var ArchLoong64 = &Arch{
- Name: "loong64",
- Family: Loong64,
- ByteOrder: binary.LittleEndian,
- PtrSize: 8,
- RegSize: 8,
- MinLC: 4,
- Alignment: 8, // Unaligned accesses are not guaranteed to be fast
- CanMergeLoads: false,
+ Name: "loong64",
+ Family: Loong64,
+ ByteOrder: binary.LittleEndian,
+ PtrSize: 8,
+ RegSize: 8,
+ MinLC: 4,
+ Alignment: 8, // Unaligned accesses are not guaranteed to be fast
+ CanMergeLoads: false,
+ HasLR: true,
+ FixedFrameSize: 8, // LR
}
var ArchMIPS = &Arch{
- Name: "mips",
- Family: MIPS,
- ByteOrder: binary.BigEndian,
- PtrSize: 4,
- RegSize: 4,
- MinLC: 4,
- Alignment: 4,
- CanMergeLoads: false,
+ Name: "mips",
+ Family: MIPS,
+ ByteOrder: binary.BigEndian,
+ PtrSize: 4,
+ RegSize: 4,
+ MinLC: 4,
+ Alignment: 4,
+ CanMergeLoads: false,
+ HasLR: true,
+ FixedFrameSize: 4, // LR
}
var ArchMIPSLE = &Arch{
- Name: "mipsle",
- Family: MIPS,
- ByteOrder: binary.LittleEndian,
- PtrSize: 4,
- RegSize: 4,
- MinLC: 4,
- Alignment: 4,
- CanMergeLoads: false,
+ Name: "mipsle",
+ Family: MIPS,
+ ByteOrder: binary.LittleEndian,
+ PtrSize: 4,
+ RegSize: 4,
+ MinLC: 4,
+ Alignment: 4,
+ CanMergeLoads: false,
+ HasLR: true,
+ FixedFrameSize: 4, // LR
}
var ArchMIPS64 = &Arch{
- Name: "mips64",
- Family: MIPS64,
- ByteOrder: binary.BigEndian,
- PtrSize: 8,
- RegSize: 8,
- MinLC: 4,
- Alignment: 8,
- CanMergeLoads: false,
+ Name: "mips64",
+ Family: MIPS64,
+ ByteOrder: binary.BigEndian,
+ PtrSize: 8,
+ RegSize: 8,
+ MinLC: 4,
+ Alignment: 8,
+ CanMergeLoads: false,
+ HasLR: true,
+ FixedFrameSize: 8, // LR
}
var ArchMIPS64LE = &Arch{
- Name: "mips64le",
- Family: MIPS64,
- ByteOrder: binary.LittleEndian,
- PtrSize: 8,
- RegSize: 8,
- MinLC: 4,
- Alignment: 8,
- CanMergeLoads: false,
+ Name: "mips64le",
+ Family: MIPS64,
+ ByteOrder: binary.LittleEndian,
+ PtrSize: 8,
+ RegSize: 8,
+ MinLC: 4,
+ Alignment: 8,
+ CanMergeLoads: false,
+ HasLR: true,
+ FixedFrameSize: 8, // LR
}
var ArchPPC64 = &Arch{
@@ -173,50 +208,62 @@ var ArchPPC64 = &Arch{
MinLC: 4,
Alignment: 1,
CanMergeLoads: false,
+ HasLR: true,
+ // PIC code on ppc64le requires 32 bytes of stack, and it's
+ // easier to just use that much stack always.
+ FixedFrameSize: 4 * 8,
}
var ArchPPC64LE = &Arch{
- Name: "ppc64le",
- Family: PPC64,
- ByteOrder: binary.LittleEndian,
- PtrSize: 8,
- RegSize: 8,
- MinLC: 4,
- Alignment: 1,
- CanMergeLoads: true,
+ Name: "ppc64le",
+ Family: PPC64,
+ ByteOrder: binary.LittleEndian,
+ PtrSize: 8,
+ RegSize: 8,
+ MinLC: 4,
+ Alignment: 1,
+ CanMergeLoads: true,
+ HasLR: true,
+ FixedFrameSize: 4 * 8,
}
var ArchRISCV64 = &Arch{
- Name: "riscv64",
- Family: RISCV64,
- ByteOrder: binary.LittleEndian,
- PtrSize: 8,
- RegSize: 8,
- MinLC: 4,
- Alignment: 8, // riscv unaligned loads work, but are really slow (trap + simulated by OS)
- CanMergeLoads: false,
+ Name: "riscv64",
+ Family: RISCV64,
+ ByteOrder: binary.LittleEndian,
+ PtrSize: 8,
+ RegSize: 8,
+ MinLC: 4,
+ Alignment: 8, // riscv unaligned loads work, but are really slow (trap + simulated by OS)
+ CanMergeLoads: false,
+ HasLR: true,
+ FixedFrameSize: 8, // LR
}
var ArchS390X = &Arch{
- Name: "s390x",
- Family: S390X,
- ByteOrder: binary.BigEndian,
- PtrSize: 8,
- RegSize: 8,
- MinLC: 2,
- Alignment: 1,
- CanMergeLoads: true,
+ Name: "s390x",
+ Family: S390X,
+ ByteOrder: binary.BigEndian,
+ PtrSize: 8,
+ RegSize: 8,
+ MinLC: 2,
+ Alignment: 1,
+ CanMergeLoads: true,
+ HasLR: true,
+ FixedFrameSize: 8, // LR
}
var ArchWasm = &Arch{
- Name: "wasm",
- Family: Wasm,
- ByteOrder: binary.LittleEndian,
- PtrSize: 8,
- RegSize: 8,
- MinLC: 1,
- Alignment: 1,
- CanMergeLoads: false,
+ Name: "wasm",
+ Family: Wasm,
+ ByteOrder: binary.LittleEndian,
+ PtrSize: 8,
+ RegSize: 8,
+ MinLC: 1,
+ Alignment: 1,
+ CanMergeLoads: false,
+ HasLR: false,
+ FixedFrameSize: 0,
}
var Archs = [...]*Arch{
diff --git a/src/cmd/internal/test2json/test2json.go b/src/cmd/internal/test2json/test2json.go
index 4eb6dd4838b..d8b870f256e 100644
--- a/src/cmd/internal/test2json/test2json.go
+++ b/src/cmd/internal/test2json/test2json.go
@@ -66,7 +66,7 @@ type Converter struct {
// The input buffer needs to be able to hold any single test
// directive line we want to recognize, like:
//
-// <many spaces> --- PASS: very/nested/s/u/b/t/e/s/t
+// <many spaces> --- PASS: very/nested/s/u/b/t/e/s/t
//
// If anyone reports a test directive line > 4k not working, it will
// be defensible to suggest they restructure their test or test names.
diff --git a/src/cmd/link/doc.go b/src/cmd/link/doc.go
index 98c954f0f1a..a5701327141 100644
--- a/src/cmd/link/doc.go
+++ b/src/cmd/link/doc.go
@@ -3,11 +3,11 @@
// license that can be found in the LICENSE file.
/*
-Link, typically invoked as ``go tool link'', reads the Go archive or object
+Link, typically invoked as “go tool link”, reads the Go archive or object
for a package main, along with its dependencies, and combines them
into an executable binary.
-Command Line
+# Command Line
Usage:
diff --git a/src/cmd/link/internal/benchmark/bench.go b/src/cmd/link/internal/benchmark/bench.go
index 39179515cd8..7c6f278264c 100644
--- a/src/cmd/link/internal/benchmark/bench.go
+++ b/src/cmd/link/internal/benchmark/bench.go
@@ -44,30 +44,30 @@ type mark struct {
//
// Typical usage should look like:
//
-// func main() {
-// filename := "" // Set to enable per-phase pprof file output.
-// bench := benchmark.New(benchmark.GC, filename)
-// defer bench.Report(os.Stdout)
-// // etc
-// bench.Start("foo")
-// foo()
-// bench.Start("bar")
-// bar()
-// }
+// func main() {
+// filename := "" // Set to enable per-phase pprof file output.
+// bench := benchmark.New(benchmark.GC, filename)
+// defer bench.Report(os.Stdout)
+// // etc
+// bench.Start("foo")
+// foo()
+// bench.Start("bar")
+// bar()
+// }
//
// Note that a nil Metrics object won't cause any errors, so one could write
// code like:
//
-// func main() {
-// enableBenchmarking := flag.Bool("enable", true, "enables benchmarking")
-// flag.Parse()
-// var bench *benchmark.Metrics
-// if *enableBenchmarking {
-// bench = benchmark.New(benchmark.GC)
-// }
-// bench.Start("foo")
-// // etc.
-// }
+// func main() {
+// enableBenchmarking := flag.Bool("enable", true, "enables benchmarking")
+// flag.Parse()
+// var bench *benchmark.Metrics
+// if *enableBenchmarking {
+// bench = benchmark.New(benchmark.GC)
+// }
+// bench.Start("foo")
+// // etc.
+// }
func New(gc Flags, filebase string) *Metrics {
if gc == GC {
runtime.GC()
diff --git a/src/cmd/link/internal/ld/asmb.go b/src/cmd/link/internal/ld/asmb.go
index d6ecb2895ba..cd8927b0876 100644
--- a/src/cmd/link/internal/ld/asmb.go
+++ b/src/cmd/link/internal/ld/asmb.go
@@ -14,8 +14,9 @@ import (
)
// Assembling the binary is broken into two steps:
-// - writing out the code/data/dwarf Segments, applying relocations on the fly
-// - writing out the architecture specific pieces.
+// - writing out the code/data/dwarf Segments, applying relocations on the fly
+// - writing out the architecture specific pieces.
+//
// This function handles the first part.
func asmb(ctxt *Link) {
// TODO(jfaller): delete me.
@@ -63,8 +64,9 @@ func asmb(ctxt *Link) {
}
// Assembling the binary is broken into two steps:
-// - writing out the code/data/dwarf Segments
-// - writing out the architecture specific pieces.
+// - writing out the code/data/dwarf Segments
+// - writing out the architecture specific pieces.
+//
// This function handles the second part.
func asmb2(ctxt *Link) {
if thearch.Asmb2 != nil {
diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go
index 0ec1e526a98..ce86f73cdab 100644
--- a/src/cmd/link/internal/ld/data.go
+++ b/src/cmd/link/internal/ld/data.go
@@ -178,7 +178,7 @@ func FoldSubSymbolOffset(ldr *loader.Loader, s loader.Sym) (loader.Sym, int64) {
// (to be applied by the external linker). For more on how relocations
// work in general, see
//
-// "Linkers and Loaders", by John R. Levine (Morgan Kaufmann, 1999), ch. 7
+// "Linkers and Loaders", by John R. Levine (Morgan Kaufmann, 1999), ch. 7
//
// This is a performance-critical function for the linker; be careful
// to avoid introducing unnecessary allocations in the main loop.
@@ -2112,12 +2112,7 @@ func (state *dodataState) dodataSect(ctxt *Link, symn sym.SymKind, syms []loader
return si < sj
})
} else {
- // PCLNTAB was built internally, and has the proper order based on value.
- // Sort the symbols as such.
- for k, s := range syms {
- sl[k].val = ldr.SymValue(s)
- }
- sort.Slice(sl, func(i, j int) bool { return sl[i].val < sl[j].val })
+ // PCLNTAB was built internally, and already has the proper order.
}
// Set alignment, construct result
@@ -2140,7 +2135,7 @@ func (state *dodataState) dodataSect(ctxt *Link, symn sym.SymKind, syms []loader
// Non-ELF binary formats are not always flexible enough to
// give us a place to put the Go build ID. On those systems, we put it
// at the very beginning of the text segment.
-// This ``header'' is read by cmd/go.
+// This “header” is read by cmd/go.
func (ctxt *Link) textbuildid() {
if ctxt.IsELF || ctxt.BuildMode == BuildModePlugin || *flagBuildid == "" {
return
diff --git a/src/cmd/link/internal/ld/deadcode.go b/src/cmd/link/internal/ld/deadcode.go
index 3ba4b06f4a5..48f447b1fae 100644
--- a/src/cmd/link/internal/ld/deadcode.go
+++ b/src/cmd/link/internal/ld/deadcode.go
@@ -307,10 +307,10 @@ func (d *deadcodePass) markMethod(m methodref) {
//
// There are three ways a method of a reachable type can be invoked:
//
-// 1. direct call
-// 2. through a reachable interface type
-// 3. reflect.Value.Method (or MethodByName), or reflect.Type.Method
-// (or MethodByName)
+// 1. direct call
+// 2. through a reachable interface type
+// 3. reflect.Value.Method (or MethodByName), or reflect.Type.Method
+// (or MethodByName)
//
// The first case is handled by the flood fill, a directly called method
// is marked as reachable.
@@ -321,9 +321,10 @@ func (d *deadcodePass) markMethod(m methodref) {
// as reachable. This is extremely conservative, but easy and correct.
//
// The third case is handled by looking to see if any of:
-// - reflect.Value.Method or MethodByName is reachable
-// - reflect.Type.Method or MethodByName is called (through the
-// REFLECTMETHOD attribute marked by the compiler).
+// - reflect.Value.Method or MethodByName is reachable
+// - reflect.Type.Method or MethodByName is called (through the
+// REFLECTMETHOD attribute marked by the compiler).
+//
// If any of these happen, all bets are off and all exported methods
// of reachable types are marked reachable.
//
diff --git a/src/cmd/link/internal/ld/decodesym.go b/src/cmd/link/internal/ld/decodesym.go
index 629bdcf4cac..a6ae202859e 100644
--- a/src/cmd/link/internal/ld/decodesym.go
+++ b/src/cmd/link/internal/ld/decodesym.go
@@ -21,6 +21,7 @@ import (
// tflag is documented in reflect/type.go.
//
// tflag values must be kept in sync with copies in:
+//
// cmd/compile/internal/reflectdata/reflect.go
// cmd/link/internal/ld/decodesym.go
// reflect/type.go
diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go
index 2e209d0c6b7..6ed9697aec4 100644
--- a/src/cmd/link/internal/ld/dwarf.go
+++ b/src/cmd/link/internal/ld/dwarf.go
@@ -1360,7 +1360,7 @@ func (d *dwctxt) writeframes(fs loader.Sym) dwarfSecInfo {
fsu := d.ldr.MakeSymbolUpdater(fs)
fsu.SetType(sym.SDWARFSECT)
isdw64 := isDwarf64(d.linkctxt)
- haslr := haslinkregister(d.linkctxt)
+ haslr := d.linkctxt.Arch.HasLR
// Length field is 4 bytes on Dwarf32 and 12 bytes on Dwarf64
lengthFieldSize := int64(4)
diff --git a/src/cmd/link/internal/ld/dwarf_test.go b/src/cmd/link/internal/ld/dwarf_test.go
index 1f7b37f892e..db5be74b9ae 100644
--- a/src/cmd/link/internal/ld/dwarf_test.go
+++ b/src/cmd/link/internal/ld/dwarf_test.go
@@ -1462,11 +1462,11 @@ func TestIssue42484(t *testing.T) {
// captures the name, order, and classification of the subprogram's
// input and output parameters. For example, for the go function
//
-// func foo(i1 int, f1 float64) (string, bool) {
+// func foo(i1 int, f1 float64) (string, bool) {
//
// this function would return a string something like
//
-// i1:0:1 f1:1:1 ~r0:2:2 ~r1:3:2
+// i1:0:1 f1:1:1 ~r0:2:2 ~r1:3:2
//
// where each chunk above is of the form NAME:ORDER:INOUTCLASSIFICATION
func processParams(die *dwarf.Entry, ex *dwtest.Examiner) string {
diff --git a/src/cmd/link/internal/ld/elf.go b/src/cmd/link/internal/ld/elf.go
index 733f4ec00b4..d93f72e1121 100644
--- a/src/cmd/link/internal/ld/elf.go
+++ b/src/cmd/link/internal/ld/elf.go
@@ -202,8 +202,8 @@ var nelfstr int
var buildinfo []byte
/*
- Initialize the global variable that describes the ELF header. It will be updated as
- we write section and prog headers.
+Initialize the global variable that describes the ELF header. It will be updated as
+we write section and prog headers.
*/
func Elfinit(ctxt *Link) {
ctxt.IsELF = true
@@ -550,30 +550,31 @@ func elfMipsAbiFlags(sh *ElfShdr, startva uint64, resoff uint64) int {
}
// Layout is given by this C definition:
-// typedef struct
-// {
-// /* Version of flags structure. */
-// uint16_t version;
-// /* The level of the ISA: 1-5, 32, 64. */
-// uint8_t isa_level;
-// /* The revision of ISA: 0 for MIPS V and below, 1-n otherwise. */
-// uint8_t isa_rev;
-// /* The size of general purpose registers. */
-// uint8_t gpr_size;
-// /* The size of co-processor 1 registers. */
-// uint8_t cpr1_size;
-// /* The size of co-processor 2 registers. */
-// uint8_t cpr2_size;
-// /* The floating-point ABI. */
-// uint8_t fp_abi;
-// /* Processor-specific extension. */
-// uint32_t isa_ext;
-// /* Mask of ASEs used. */
-// uint32_t ases;
-// /* Mask of general flags. */
-// uint32_t flags1;
-// uint32_t flags2;
-// } Elf_Internal_ABIFlags_v0;
+//
+// typedef struct
+// {
+// /* Version of flags structure. */
+// uint16_t version;
+// /* The level of the ISA: 1-5, 32, 64. */
+// uint8_t isa_level;
+// /* The revision of ISA: 0 for MIPS V and below, 1-n otherwise. */
+// uint8_t isa_rev;
+// /* The size of general purpose registers. */
+// uint8_t gpr_size;
+// /* The size of co-processor 1 registers. */
+// uint8_t cpr1_size;
+// /* The size of co-processor 2 registers. */
+// uint8_t cpr2_size;
+// /* The floating-point ABI. */
+// uint8_t fp_abi;
+// /* Processor-specific extension. */
+// uint32_t isa_ext;
+// /* Mask of ASEs used. */
+// uint32_t ases;
+// /* Mask of general flags. */
+// uint32_t flags1;
+// uint32_t flags2;
+// } Elf_Internal_ABIFlags_v0;
func elfWriteMipsAbiFlags(ctxt *Link) int {
sh := elfshname(".MIPS.abiflags")
ctxt.Out.SeekSet(int64(sh.Off))
diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go
index 07cffcafff4..36db56950b9 100644
--- a/src/cmd/link/internal/ld/lib.go
+++ b/src/cmd/link/internal/ld/lib.go
@@ -34,7 +34,6 @@ import (
"bytes"
"cmd/internal/bio"
"cmd/internal/goobj"
- "cmd/internal/obj"
"cmd/internal/objabi"
"cmd/internal/sys"
"cmd/link/internal/loadelf"
@@ -2344,227 +2343,6 @@ func addsection(ldr *loader.Loader, arch *sys.Arch, seg *sym.Segment, name strin
return sect
}
-type chain struct {
- sym loader.Sym
- up *chain
- limit int // limit on entry to sym
-}
-
-func haslinkregister(ctxt *Link) bool {
- return ctxt.FixedFrameSize() != 0
-}
-
-func callsize(ctxt *Link) int {
- if haslinkregister(ctxt) {
- return 0
- }
- return ctxt.Arch.RegSize
-}
-
-type stkChk struct {
- ldr *loader.Loader
- ctxt *Link
- morestack loader.Sym
- done loader.Bitmap
-}
-
-// Walk the call tree and check that there is always enough stack space
-// for the call frames, especially for a chain of nosplit functions.
-func (ctxt *Link) dostkcheck() {
- ldr := ctxt.loader
- sc := stkChk{
- ldr: ldr,
- ctxt: ctxt,
- morestack: ldr.Lookup("runtime.morestack", 0),
- done: loader.MakeBitmap(ldr.NSym()),
- }
-
- // Every splitting function ensures that there are at least StackLimit
- // bytes available below SP when the splitting prologue finishes.
- // If the splitting function calls F, then F begins execution with
- // at least StackLimit - callsize() bytes available.
- // Check that every function behaves correctly with this amount
- // of stack, following direct calls in order to piece together chains
- // of non-splitting functions.
- var ch chain
- ch.limit = objabi.StackLimit - callsize(ctxt)
- if buildcfg.GOARCH == "arm64" {
- // need extra 8 bytes below SP to save FP
- ch.limit -= 8
- }
-
- // Check every function, but do the nosplit functions in a first pass,
- // to make the printed failure chains as short as possible.
- for _, s := range ctxt.Textp {
- if ldr.IsNoSplit(s) {
- ch.sym = s
- sc.check(&ch, 0)
- }
- }
-
- for _, s := range ctxt.Textp {
- if !ldr.IsNoSplit(s) {
- ch.sym = s
- sc.check(&ch, 0)
- }
- }
-}
-
-func (sc *stkChk) check(up *chain, depth int) int {
- limit := up.limit
- s := up.sym
- ldr := sc.ldr
- ctxt := sc.ctxt
-
- // Don't duplicate work: only need to consider each
- // function at top of safe zone once.
- top := limit == objabi.StackLimit-callsize(ctxt)
- if top {
- if sc.done.Has(s) {
- return 0
- }
- sc.done.Set(s)
- }
-
- if depth > 500 {
- sc.ctxt.Errorf(s, "nosplit stack check too deep")
- sc.broke(up, 0)
- return -1
- }
-
- if ldr.AttrExternal(s) {
- // external function.
- // should never be called directly.
- // onlyctxt.Diagnose the direct caller.
- // TODO(mwhudson): actually think about this.
- // TODO(khr): disabled for now. Calls to external functions can only happen on the g0 stack.
- // See the trampolines in src/runtime/sys_darwin_$ARCH.go.
- //if depth == 1 && ldr.SymType(s) != sym.SXREF && !ctxt.DynlinkingGo() &&
- // ctxt.BuildMode != BuildModeCArchive && ctxt.BuildMode != BuildModePIE && ctxt.BuildMode != BuildModeCShared && ctxt.BuildMode != BuildModePlugin {
- // Errorf(s, "call to external function")
- //}
- return -1
- }
- info := ldr.FuncInfo(s)
- if !info.Valid() { // external function. see above.
- return -1
- }
-
- if limit < 0 {
- sc.broke(up, limit)
- return -1
- }
-
- // morestack looks like it calls functions,
- // but it switches the stack pointer first.
- if s == sc.morestack {
- return 0
- }
-
- var ch chain
- ch.up = up
-
- if !ldr.IsNoSplit(s) {
- // Ensure we have enough stack to call morestack.
- ch.limit = limit - callsize(ctxt)
- ch.sym = sc.morestack
- if sc.check(&ch, depth+1) < 0 {
- return -1
- }
- if !top {
- return 0
- }
- // Raise limit to allow frame.
- locals := info.Locals()
- limit = objabi.StackLimit + int(locals) + int(ctxt.FixedFrameSize())
- }
-
- // Walk through sp adjustments in function, consuming relocs.
- relocs := ldr.Relocs(s)
- var ch1 chain
- pcsp := obj.NewPCIter(uint32(ctxt.Arch.MinLC))
- ri := 0
- for pcsp.Init(ldr.Data(ldr.Pcsp(s))); !pcsp.Done; pcsp.Next() {
- // pcsp.value is in effect for [pcsp.pc, pcsp.nextpc).
-
- // Check stack size in effect for this span.
- if int32(limit)-pcsp.Value < 0 {
- sc.broke(up, int(int32(limit)-pcsp.Value))
- return -1
- }
-
- // Process calls in this span.
- for ; ri < relocs.Count(); ri++ {
- r := relocs.At(ri)
- if uint32(r.Off()) >= pcsp.NextPC {
- break
- }
- t := r.Type()
- switch {
- case t.IsDirectCall():
- ch.limit = int(int32(limit) - pcsp.Value - int32(callsize(ctxt)))
- ch.sym = r.Sym()
- if sc.check(&ch, depth+1) < 0 {
- return -1
- }
-
- // Indirect call. Assume it is a call to a splitting function,
- // so we have to make sure it can call morestack.
- // Arrange the data structures to report both calls, so that
- // if there is an error, stkprint shows all the steps involved.
- case t == objabi.R_CALLIND:
- ch.limit = int(int32(limit) - pcsp.Value - int32(callsize(ctxt)))
- ch.sym = 0
- ch1.limit = ch.limit - callsize(ctxt) // for morestack in called prologue
- ch1.up = &ch
- ch1.sym = sc.morestack
- if sc.check(&ch1, depth+2) < 0 {
- return -1
- }
- }
- }
- }
-
- return 0
-}
-
-func (sc *stkChk) broke(ch *chain, limit int) {
- sc.ctxt.Errorf(ch.sym, "nosplit stack overflow")
- sc.print(ch, limit)
-}
-
-func (sc *stkChk) print(ch *chain, limit int) {
- ldr := sc.ldr
- ctxt := sc.ctxt
- var name string
- if ch.sym != 0 {
- name = fmt.Sprintf("%s<%d>", ldr.SymName(ch.sym), ldr.SymVersion(ch.sym))
- if ldr.IsNoSplit(ch.sym) {
- name += " (nosplit)"
- }
- } else {
- name = "function pointer"
- }
-
- if ch.up == nil {
- // top of chain. ch.sym != 0.
- if ldr.IsNoSplit(ch.sym) {
- fmt.Printf("\t%d\tassumed on entry to %s\n", ch.limit, name)
- } else {
- fmt.Printf("\t%d\tguaranteed after split check in %s\n", ch.limit, name)
- }
- } else {
- sc.print(ch.up, ch.limit+callsize(ctxt))
- if !haslinkregister(ctxt) {
- fmt.Printf("\t%d\ton entry to %s\n", ch.limit, name)
- }
- }
-
- if ch.limit != limit {
- fmt.Printf("\t%d\tafter %s uses %d\n", limit, name, ch.limit-limit)
- }
-}
-
func usage() {
fmt.Fprintf(os.Stderr, "usage: link [options] main.o\n")
objabi.Flagprint(os.Stderr)
diff --git a/src/cmd/link/internal/ld/link.go b/src/cmd/link/internal/ld/link.go
index 64d18bd62c0..34221dfa8a7 100644
--- a/src/cmd/link/internal/ld/link.go
+++ b/src/cmd/link/internal/ld/link.go
@@ -33,7 +33,6 @@ package ld
import (
"bufio"
"cmd/internal/objabi"
- "cmd/internal/sys"
"cmd/link/internal/loader"
"cmd/link/internal/sym"
"debug/elf"
@@ -103,23 +102,6 @@ type cgodata struct {
directives [][]string
}
-// The smallest possible offset from the hardware stack pointer to a local
-// variable on the stack. Architectures that use a link register save its value
-// on the stack in the function prologue and so always have a pointer between
-// the hardware stack pointer and the local variable area.
-func (ctxt *Link) FixedFrameSize() int64 {
- switch ctxt.Arch.Family {
- case sys.AMD64, sys.I386:
- return 0
- case sys.PPC64:
- // PIC code on ppc64le requires 32 bytes of stack, and it's easier to
- // just use that much stack always on ppc64x.
- return int64(4 * ctxt.Arch.PtrSize)
- default:
- return int64(ctxt.Arch.PtrSize)
- }
-}
-
func (ctxt *Link) Logf(format string, args ...interface{}) {
fmt.Fprintf(ctxt.Bso, format, args...)
ctxt.Bso.Flush()
diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go
index fa95a7acf23..c52e6e909d8 100644
--- a/src/cmd/link/internal/ld/main.go
+++ b/src/cmd/link/internal/ld/main.go
@@ -93,6 +93,7 @@ var (
flagInterpreter = flag.String("I", "", "use `linker` as ELF dynamic linker")
FlagDebugTramp = flag.Int("debugtramp", 0, "debug trampolines")
FlagDebugTextSize = flag.Int("debugtextsize", 0, "debug text section max size")
+ flagDebugNosplit = flag.Bool("debugnosplit", false, "dump nosplit call graph")
FlagStrictDups = flag.Int("strictdups", 0, "sanity check duplicate symbol contents during object file reading (1=warn 2=err).")
FlagRound = flag.Int("R", -1, "set address rounding `quantum`")
FlagTextAddr = flag.Int64("T", -1, "set text segment `address`")
@@ -283,8 +284,8 @@ func Main(arch *sys.Arch, theArch Arch) {
bench.Start("callgraph")
ctxt.callgraph()
- bench.Start("dostkcheck")
- ctxt.dostkcheck()
+ bench.Start("doStackCheck")
+ ctxt.doStackCheck()
bench.Start("mangleTypeSym")
ctxt.mangleTypeSym()
diff --git a/src/cmd/link/internal/ld/outbuf.go b/src/cmd/link/internal/ld/outbuf.go
index 1d1751ccdcb..e078ee442fa 100644
--- a/src/cmd/link/internal/ld/outbuf.go
+++ b/src/cmd/link/internal/ld/outbuf.go
@@ -30,12 +30,12 @@ const outbufMode = 0775
// any system calls to read the value.
//
// Third, it also mmaps the output file (if available). The intended usage is:
-// - Mmap the output file
-// - Write the content
-// - possibly apply any edits in the output buffer
-// - possibly write more content to the file. These writes take place in a heap
-// backed buffer that will get synced to disk.
-// - Munmap the output file
+// - Mmap the output file
+// - Write the content
+// - possibly apply any edits in the output buffer
+// - possibly write more content to the file. These writes take place in a heap
+// backed buffer that will get synced to disk.
+// - Munmap the output file
//
// And finally, it provides a mechanism by which you can multithread the
// writing of output files. This mechanism is accomplished by copying a OutBuf,
@@ -43,22 +43,22 @@ const outbufMode = 0775
//
// Parallel OutBuf is intended to be used like:
//
-// func write(out *OutBuf) {
-// var wg sync.WaitGroup
-// for i := 0; i < 10; i++ {
-// wg.Add(1)
-// view, err := out.View(start[i])
-// if err != nil {
-// // handle output
-// continue
-// }
-// go func(out *OutBuf, i int) {
-// // do output
-// wg.Done()
-// }(view, i)
-// }
-// wg.Wait()
-// }
+// func write(out *OutBuf) {
+// var wg sync.WaitGroup
+// for i := 0; i < 10; i++ {
+// wg.Add(1)
+// view, err := out.View(start[i])
+// if err != nil {
+// // handle output
+// continue
+// }
+// go func(out *OutBuf, i int) {
+// // do output
+// wg.Done()
+// }(view, i)
+// }
+// wg.Wait()
+// }
type OutBuf struct {
arch *sys.Arch
off int64
diff --git a/src/cmd/link/internal/ld/outbuf_darwin.go b/src/cmd/link/internal/ld/outbuf_darwin.go
index b1ee3c56282..e372b3724aa 100644
--- a/src/cmd/link/internal/ld/outbuf_darwin.go
+++ b/src/cmd/link/internal/ld/outbuf_darwin.go
@@ -13,6 +13,7 @@ import (
)
// Implemented in the syscall package.
+//
//go:linkname fcntl syscall.fcntl
func fcntl(fd int, cmd int, arg int) (int, error)
diff --git a/src/cmd/link/internal/ld/pcln.go b/src/cmd/link/internal/ld/pcln.go
index a81490089f4..7003ed78589 100644
--- a/src/cmd/link/internal/ld/pcln.go
+++ b/src/cmd/link/internal/ld/pcln.go
@@ -353,21 +353,21 @@ func walkFilenames(ctxt *Link, funcs []loader.Sym, f func(*sym.CompilationUnit,
// This function creates a per-CU list of filenames if CU[M] references
// files[1-N], the following is generated:
//
-// runtime.cutab:
-// CU[M]
-// offsetToFilename[0]
-// offsetToFilename[1]
-// ..
+// runtime.cutab:
+// CU[M]
+// offsetToFilename[0]
+// offsetToFilename[1]
+// ..
//
-// runtime.filetab
-// filename[0]
-// filename[1]
+// runtime.filetab
+// filename[0]
+// filename[1]
//
// Looking up a filename then becomes:
-// 0) Given a func, and filename index [K]
-// 1) Get Func.CUIndex: M := func.cuOffset
-// 2) Find filename offset: fileOffset := runtime.cutab[M+K]
-// 3) Get the filename: getcstring(runtime.filetab[fileOffset])
+// 0. Given a func, and filename index [K]
+// 1. Get Func.CUIndex: M := func.cuOffset
+// 2. Find filename offset: fileOffset := runtime.cutab[M+K]
+// 3. Get the filename: getcstring(runtime.filetab[fileOffset])
func (state *pclntab) generateFilenameTabs(ctxt *Link, compUnits []*sym.CompilationUnit, funcs []loader.Sym) []uint32 {
// On a per-CU basis, keep track of all the filenames we need.
//
diff --git a/src/cmd/link/internal/ld/stackcheck.go b/src/cmd/link/internal/ld/stackcheck.go
new file mode 100644
index 00000000000..520e4d67b5d
--- /dev/null
+++ b/src/cmd/link/internal/ld/stackcheck.go
@@ -0,0 +1,421 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ld
+
+import (
+ "cmd/internal/obj"
+ "cmd/internal/objabi"
+ "cmd/link/internal/loader"
+ "fmt"
+ "internal/buildcfg"
+ "sort"
+ "strings"
+)
+
+type stackCheck struct {
+ ctxt *Link
+ ldr *loader.Loader
+ morestack loader.Sym
+ callSize int // The number of bytes added by a CALL
+
+ // height records the maximum number of bytes a function and
+ // its callees can add to the stack without a split check.
+ height map[loader.Sym]int16
+
+ // graph records the out-edges from each symbol. This is only
+ // populated on a second pass if the first pass reveals an
+ // over-limit function.
+ graph map[loader.Sym][]stackCheckEdge
+}
+
+type stackCheckEdge struct {
+ growth int // Stack growth in bytes at call to target
+ target loader.Sym // 0 for stack growth without a call
+}
+
+// stackCheckCycle is a sentinel stored in the height map to detect if
+// we've found a cycle. This is effectively an "infinite" stack
+// height, so we use the closest value to infinity that we can.
+const stackCheckCycle int16 = 1<<15 - 1
+
+// stackCheckIndirect is a sentinel Sym value used to represent the
+// target of an indirect/closure call.
+const stackCheckIndirect loader.Sym = -1
+
+// doStackCheck walks the call tree to check that there is always
+// enough stack space for call frames, especially for a chain of
+// nosplit functions.
+//
+// It walks all functions to accumulate the number of bytes they can
+// grow the stack by without a split check and checks this against the
+// limit.
+func (ctxt *Link) doStackCheck() {
+ sc := newStackCheck(ctxt, false)
+
+ // limit is number of bytes a splittable function ensures are
+ // available on the stack. If any call chain exceeds this
+ // depth, the stack check test fails.
+ //
+ // The call to morestack in every splittable function ensures
+ // that there are at least StackLimit bytes available below SP
+ // when morestack returns.
+ limit := objabi.StackLimit - sc.callSize
+ if buildcfg.GOARCH == "arm64" {
+ // Need an extra 8 bytes below SP to save FP.
+ limit -= 8
+ }
+
+ // Compute stack heights without any back-tracking information.
+ // This will almost certainly succeed and we can simply
+ // return. If it fails, we do a second pass with back-tracking
+ // to produce a good error message.
+ //
+ // This accumulates stack heights bottom-up so it only has to
+ // visit every function once.
+ var failed []loader.Sym
+ for _, s := range ctxt.Textp {
+ if sc.check(s) > limit {
+ failed = append(failed, s)
+ }
+ }
+
+ if len(failed) > 0 {
+ // Something was over-limit, so now we do the more
+ // expensive work to report a good error. First, for
+ // the over-limit functions, redo the stack check but
+ // record the graph this time.
+ sc = newStackCheck(ctxt, true)
+ for _, s := range failed {
+ sc.check(s)
+ }
+
+ // Find the roots of the graph (functions that are not
+ // called by any other function).
+ roots := sc.findRoots()
+
+ // Find and report all paths that go over the limit.
+ // This accumulates stack depths top-down. This is
+ // much less efficient because we may have to visit
+ // the same function multiple times at different
+ // depths, but lets us find all paths.
+ for _, root := range roots {
+ ctxt.Errorf(root, "nosplit stack overflow")
+ chain := []stackCheckChain{{stackCheckEdge{0, root}, false}}
+ sc.report(root, limit, &chain)
+ }
+ }
+}
+
+func newStackCheck(ctxt *Link, graph bool) *stackCheck {
+ sc := &stackCheck{
+ ctxt: ctxt,
+ ldr: ctxt.loader,
+ morestack: ctxt.loader.Lookup("runtime.morestack", 0),
+ height: make(map[loader.Sym]int16, len(ctxt.Textp)),
+ }
+ // Compute stack effect of a CALL operation. 0 on LR machines.
+ // 1 register pushed on non-LR machines.
+ if !ctxt.Arch.HasLR {
+ sc.callSize = ctxt.Arch.RegSize
+ }
+
+ if graph {
+ // We're going to record the call graph.
+ sc.graph = make(map[loader.Sym][]stackCheckEdge)
+ }
+
+ return sc
+}
+
+func (sc *stackCheck) symName(sym loader.Sym) string {
+ switch sym {
+ case stackCheckIndirect:
+ return "indirect"
+ case 0:
+ return "leaf"
+ }
+ return fmt.Sprintf("%s<%d>", sc.ldr.SymName(sym), sc.ldr.SymVersion(sym))
+}
+
+// check returns the stack height of sym. It populates sc.height and
+// sc.graph for sym and every function in its call tree.
+func (sc *stackCheck) check(sym loader.Sym) int {
+ if h, ok := sc.height[sym]; ok {
+ // We've already visited this symbol or we're in a cycle.
+ return int(h)
+ }
+ // Store the sentinel so we can detect cycles.
+ sc.height[sym] = stackCheckCycle
+ // Compute and record the height and optionally edges.
+ h, edges := sc.computeHeight(sym, *flagDebugNosplit || sc.graph != nil)
+ if h > int(stackCheckCycle) { // Prevent integer overflow
+ h = int(stackCheckCycle)
+ }
+ sc.height[sym] = int16(h)
+ if sc.graph != nil {
+ sc.graph[sym] = edges
+ }
+
+ if *flagDebugNosplit {
+ for _, edge := range edges {
+ fmt.Printf("nosplit: %s +%d", sc.symName(sym), edge.growth)
+ if edge.target == 0 {
+ // Local stack growth or leaf function.
+ fmt.Printf("\n")
+ } else {
+ fmt.Printf(" -> %s\n", sc.symName(edge.target))
+ }
+ }
+ }
+
+ return h
+}
+
+// computeHeight returns the stack height of sym. If graph is true, it
+// also returns the out-edges of sym.
+//
+// Caching is applied to this in check. Call check instead of calling
+// this directly.
+func (sc *stackCheck) computeHeight(sym loader.Sym, graph bool) (int, []stackCheckEdge) {
+ ldr := sc.ldr
+
+ // Check special cases.
+ if sym == sc.morestack {
+ // morestack looks like it calls functions, but they
+ // either happen only when already on the system stack
+ // (where there is ~infinite space), or after
+ // switching to the system stack. Hence, its stack
+ // height on the user stack is 0.
+ return 0, nil
+ }
+ if sym == stackCheckIndirect {
+ // Assume that indirect/closure calls are always to
+ // splittable functions, so they just need enough room
+ // to call morestack.
+ return sc.callSize, []stackCheckEdge{{sc.callSize, sc.morestack}}
+ }
+
+ // Ignore calls to external functions. Assume that these calls
+ // are only ever happening on the system stack, where there's
+ // plenty of room.
+ if ldr.AttrExternal(sym) {
+ return 0, nil
+ }
+ if info := ldr.FuncInfo(sym); !info.Valid() { // also external
+ return 0, nil
+ }
+
+ // Track the maximum height of this function and, if we're
+ // recording the graph, its out-edges.
+ var edges []stackCheckEdge
+ maxHeight := 0
+ ctxt := sc.ctxt
+ // addEdge adds a stack growth out of this function to
+ // function "target" or, if target == 0, a local stack growth
+ // within the function.
+ addEdge := func(growth int, target loader.Sym) {
+ if graph {
+ edges = append(edges, stackCheckEdge{growth, target})
+ }
+ height := growth
+ if target != 0 { // Don't walk into the leaf "edge"
+ height += sc.check(target)
+ }
+ if height > maxHeight {
+ maxHeight = height
+ }
+ }
+
+ if !ldr.IsNoSplit(sym) {
+ // Splittable functions start with a call to
+ // morestack, after which their height is 0. Account
+ // for the height of the call to morestack.
+ addEdge(sc.callSize, sc.morestack)
+ return maxHeight, edges
+ }
+
+ // This function is nosplit, so it adjusts SP without a split
+ // check.
+ //
+ // Walk through SP adjustments in function, consuming relocs
+ // and following calls.
+ maxLocalHeight := 0
+ relocs, ri := ldr.Relocs(sym), 0
+ pcsp := obj.NewPCIter(uint32(ctxt.Arch.MinLC))
+ for pcsp.Init(ldr.Data(ldr.Pcsp(sym))); !pcsp.Done; pcsp.Next() {
+ // pcsp.value is in effect for [pcsp.pc, pcsp.nextpc).
+ height := int(pcsp.Value)
+ if height > maxLocalHeight {
+ maxLocalHeight = height
+ }
+
+ // Process calls in this span.
+ for ; ri < relocs.Count(); ri++ {
+ r := relocs.At(ri)
+ if uint32(r.Off()) >= pcsp.NextPC {
+ break
+ }
+ t := r.Type()
+ if t.IsDirectCall() || t == objabi.R_CALLIND {
+ growth := height + sc.callSize
+ var target loader.Sym
+ if t == objabi.R_CALLIND {
+ target = stackCheckIndirect
+ } else {
+ target = r.Sym()
+ }
+ addEdge(growth, target)
+ }
+ }
+ }
+ if maxLocalHeight > maxHeight {
+ // This is either a leaf function, or the function
+ // grew its stack to larger than the maximum call
+ // height between calls. Either way, record that local
+ // stack growth.
+ addEdge(maxLocalHeight, 0)
+ }
+
+ return maxHeight, edges
+}
+
+func (sc *stackCheck) findRoots() []loader.Sym {
+ // Collect all nodes.
+ nodes := make(map[loader.Sym]struct{})
+ for k := range sc.graph {
+ nodes[k] = struct{}{}
+ }
+
+ // Start a DFS from each node and delete all reachable
+ // children. If we encounter an unrooted cycle, this will
+ // delete everything in that cycle, so we detect this case and
+ // track the lowest-numbered node encountered in the cycle and
+ // put that node back as a root.
+ var walk func(origin, sym loader.Sym) (cycle bool, lowest loader.Sym)
+ walk = func(origin, sym loader.Sym) (cycle bool, lowest loader.Sym) {
+ if _, ok := nodes[sym]; !ok {
+ // We already deleted this node.
+ return false, 0
+ }
+ delete(nodes, sym)
+
+ if origin == sym {
+ // We found an unrooted cycle. We already
+ // deleted all children of this node. Walk
+ // back up, tracking the lowest numbered
+ // symbol in this cycle.
+ return true, sym
+ }
+
+ // Delete children of this node.
+ for _, out := range sc.graph[sym] {
+ if c, l := walk(origin, out.target); c {
+ cycle = true
+ if lowest == 0 {
+ // On first cycle detection,
+ // add sym to the set of
+ // lowest-numbered candidates.
+ lowest = sym
+ }
+ if l < lowest {
+ lowest = l
+ }
+ }
+ }
+ return
+ }
+ for k := range nodes {
+ // Delete all children of k.
+ for _, out := range sc.graph[k] {
+ if cycle, lowest := walk(k, out.target); cycle {
+ // This is an unrooted cycle so we
+ // just deleted everything. Put back
+ // the lowest-numbered symbol.
+ nodes[lowest] = struct{}{}
+ }
+ }
+ }
+
+ // Sort roots by height. This makes the result deterministic
+ // and also improves the error reporting.
+ var roots []loader.Sym
+ for k := range nodes {
+ roots = append(roots, k)
+ }
+ sort.Slice(roots, func(i, j int) bool {
+ h1, h2 := sc.height[roots[i]], sc.height[roots[j]]
+ if h1 != h2 {
+ return h1 > h2
+ }
+ // Secondary sort by Sym.
+ return roots[i] < roots[j]
+ })
+ return roots
+}
+
+type stackCheckChain struct {
+ stackCheckEdge
+ printed bool
+}
+
+func (sc *stackCheck) report(sym loader.Sym, depth int, chain *[]stackCheckChain) {
+ // Walk the out-edges of sym. We temporarily pull the edges
+ // out of the graph to detect cycles and prevent infinite
+ // recursion.
+ edges, ok := sc.graph[sym]
+ isCycle := !(ok || sym == 0)
+ delete(sc.graph, sym)
+ for _, out := range edges {
+ *chain = append(*chain, stackCheckChain{out, false})
+ sc.report(out.target, depth-out.growth, chain)
+ *chain = (*chain)[:len(*chain)-1]
+ }
+ sc.graph[sym] = edges
+
+ // If we've reached the end of a chain and it went over the
+ // stack limit or was a cycle that would eventually go over,
+ // print the whole chain.
+ //
+ // We should either be in morestack (which has no out-edges)
+ // or the sentinel 0 Sym "called" from a leaf function (which
+ // has no out-edges), or we came back around a cycle (possibly
+ // to ourselves) and edges was temporarily nil'd.
+ if len(edges) == 0 && (depth < 0 || isCycle) {
+ var indent string
+ for i := range *chain {
+ ent := &(*chain)[i]
+ if ent.printed {
+ // Already printed on an earlier part
+ // of this call tree.
+ continue
+ }
+ ent.printed = true
+
+ if i == 0 {
+ // chain[0] is just the root function,
+ // not a stack growth.
+ fmt.Printf("%s\n", sc.symName(ent.target))
+ continue
+ }
+
+ indent = strings.Repeat(" ", i)
+ fmt.Print(indent)
+ // Grows the stack X bytes and (maybe) calls Y.
+ fmt.Printf("grows %d bytes", ent.growth)
+ if ent.target == 0 {
+ // Not a call, just a leaf. Print nothing.
+ } else {
+ fmt.Printf(", calls %s", sc.symName(ent.target))
+ }
+ fmt.Printf("\n")
+ }
+ // Print how far over this chain went.
+ if isCycle {
+ fmt.Printf("%sinfinite cycle\n", indent)
+ } else {
+ fmt.Printf("%s%d bytes over limit\n", indent, -depth)
+ }
+ }
+}
diff --git a/src/cmd/link/internal/ld/stackcheck_test.go b/src/cmd/link/internal/ld/stackcheck_test.go
new file mode 100644
index 00000000000..21dbf2b3fd4
--- /dev/null
+++ b/src/cmd/link/internal/ld/stackcheck_test.go
@@ -0,0 +1,89 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ld
+
+import (
+ "cmd/internal/objabi"
+ "cmd/internal/sys"
+ "fmt"
+ "internal/testenv"
+ "os"
+ "os/exec"
+ "regexp"
+ "testing"
+)
+
+// See also $GOROOT/test/nosplit.go for multi-platform edge case tests.
+
+func TestStackCheckOutput(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+ t.Parallel()
+
+ cmd := exec.Command(testenv.GoToolPath(t), "build", "-o", os.DevNull, "./testdata/stackcheck")
+ // The rules for computing frame sizes on all of the
+ // architectures are complicated, so just do this on amd64.
+ cmd.Env = append(os.Environ(), "GOARCH=amd64")
+ outB, err := cmd.CombinedOutput()
+
+ if err == nil {
+ t.Fatalf("expected link to fail")
+ }
+ out := string(outB)
+
+ t.Logf("linker output:\n%s", out)
+
+ // Construct expected stanzas
+ arch := sys.ArchAMD64
+ call := 0
+ if !arch.HasLR {
+ call = arch.RegSize
+ }
+ limit := objabi.StackLimit - call
+
+ wantMap := map[string]string{
+ "main.startSelf": fmt.Sprintf(
+ `main.startSelf<0>
+ grows 1008 bytes
+ %d bytes over limit
+`, 1008-limit),
+ "main.startChain": fmt.Sprintf(
+ `main.startChain<0>
+ grows 32 bytes, calls main.chain0<0>
+ grows 48 bytes, calls main.chainEnd<0>
+ grows 1008 bytes
+ %d bytes over limit
+ grows 32 bytes, calls main.chain2<0>
+ grows 80 bytes, calls main.chainEnd<0>
+ grows 1008 bytes
+ %d bytes over limit
+`, 32+48+1008-limit, 32+80+1008-limit),
+ "main.startRec": `main.startRec<0>
+ grows 8 bytes, calls main.startRec0<0>
+ grows 8 bytes, calls main.startRec<0>
+ infinite cycle
+`,
+ }
+
+ // Parse stanzas
+ stanza := regexp.MustCompile(`^(.*): nosplit stack overflow\n(.*\n(?: .*\n)*)`)
+ // Strip comments from cmd/go
+ out = regexp.MustCompile(`(?m)^#.*\n`).ReplaceAllString(out, "")
+ for len(out) > 0 {
+ m := stanza.FindStringSubmatch(out)
+ if m == nil {
+ t.Fatalf("unexpected output:\n%s", out)
+ }
+ out = out[len(m[0]):]
+ fn := m[1]
+ got := m[2]
+
+ want, ok := wantMap[fn]
+ if !ok {
+ t.Errorf("unexpected function: %s", fn)
+ } else if want != got {
+ t.Errorf("want:\n%sgot:\n%s", want, got)
+ }
+ }
+}
diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go
index 39066da286f..63e140aa718 100644
--- a/src/cmd/link/internal/ld/symtab.go
+++ b/src/cmd/link/internal/ld/symtab.go
@@ -857,7 +857,7 @@ func mangleABIName(ctxt *Link, ldr *loader.Loader, x loader.Sym, name string) st
return name
}
- if !ldr.IsExternal(x) && ldr.SymType(x) == sym.STEXT && ldr.SymVersion(x) != sym.SymVerABIInternal {
+ if ldr.SymType(x) == sym.STEXT && ldr.SymVersion(x) != sym.SymVerABIInternal && ldr.SymVersion(x) < sym.SymVerStatic {
if s2 := ldr.Lookup(name, sym.SymVerABIInternal); s2 != 0 && ldr.SymType(s2) == sym.STEXT {
name = fmt.Sprintf("%s.abi%d", name, ldr.SymVersion(x))
}
diff --git a/src/cmd/link/internal/ld/testdata/stackcheck/main.go b/src/cmd/link/internal/ld/testdata/stackcheck/main.go
new file mode 100644
index 00000000000..b708cc5e704
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/stackcheck/main.go
@@ -0,0 +1,20 @@
+// Copyright 2022 The Go Authors. 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
+
+func main() { asmMain() }
+
+func asmMain()
+
+func startSelf()
+
+func startChain()
+func chain0()
+func chain1()
+func chain2()
+func chainEnd()
+
+func startRec()
+func startRec0()
diff --git a/src/cmd/link/internal/ld/testdata/stackcheck/main.s b/src/cmd/link/internal/ld/testdata/stackcheck/main.s
new file mode 100644
index 00000000000..10f6a3f4c2f
--- /dev/null
+++ b/src/cmd/link/internal/ld/testdata/stackcheck/main.s
@@ -0,0 +1,40 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#define NOSPLIT 7
+
+TEXT ·asmMain(SB),0,$0-0
+ CALL ·startSelf(SB)
+ CALL ·startChain(SB)
+ CALL ·startRec(SB)
+ RET
+
+// Test reporting of basic over-the-limit
+TEXT ·startSelf(SB),NOSPLIT,$1000-0
+ RET
+
+// Test reporting of multiple over-the-limit chains
+TEXT ·startChain(SB),NOSPLIT,$16-0
+ CALL ·chain0(SB)
+ CALL ·chain1(SB)
+ CALL ·chain2(SB)
+ RET
+TEXT ·chain0(SB),NOSPLIT,$32-0
+ CALL ·chainEnd(SB)
+ RET
+TEXT ·chain1(SB),NOSPLIT,$48-0 // Doesn't go over
+ RET
+TEXT ·chain2(SB),NOSPLIT,$64-0
+ CALL ·chainEnd(SB)
+ RET
+TEXT ·chainEnd(SB),NOSPLIT,$1000-0 // Should be reported twice
+ RET
+
+// Test reporting of rootless recursion
+TEXT ·startRec(SB),NOSPLIT,$0-0
+ CALL ·startRec0(SB)
+ RET
+TEXT ·startRec0(SB),NOSPLIT,$0-0
+ CALL ·startRec(SB)
+ RET
diff --git a/src/cmd/link/internal/ld/xcoff.go b/src/cmd/link/internal/ld/xcoff.go
index aaddf19d16a..955289b7af4 100644
--- a/src/cmd/link/internal/ld/xcoff.go
+++ b/src/cmd/link/internal/ld/xcoff.go
@@ -755,8 +755,8 @@ func (f *xcoffFile) writeSymbolNewFile(ctxt *Link, name string, firstEntry uint6
}
// Update values for the previous package.
-// - Svalue of the C_FILE symbol: if it is the last one, this Svalue must be -1
-// - Xsclen of the csect symbol.
+// - Svalue of the C_FILE symbol: if it is the last one, this Svalue must be -1
+// - Xsclen of the csect symbol.
func (f *xcoffFile) updatePreviousFile(ctxt *Link, last bool) {
// first file
if currSymSrcFile.file == nil {
diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go
index 2cbec5d06f3..a069540035a 100644
--- a/src/cmd/link/internal/loader/loader.go
+++ b/src/cmd/link/internal/loader/loader.go
@@ -166,21 +166,21 @@ type symAndSize struct {
//
// Notes on the layout of global symbol index space:
//
-// - Go object files are read before host object files; each Go object
-// read adds its defined package symbols to the global index space.
-// Nonpackage symbols are not yet added.
+// - Go object files are read before host object files; each Go object
+// read adds its defined package symbols to the global index space.
+// Nonpackage symbols are not yet added.
//
-// - In loader.LoadNonpkgSyms, add non-package defined symbols and
-// references in all object files to the global index space.
+// - In loader.LoadNonpkgSyms, add non-package defined symbols and
+// references in all object files to the global index space.
//
-// - Host object file loading happens; the host object loader does a
-// name/version lookup for each symbol it finds; this can wind up
-// extending the external symbol index space range. The host object
-// loader stores symbol payloads in loader.payloads using SymbolBuilder.
+// - Host object file loading happens; the host object loader does a
+// name/version lookup for each symbol it finds; this can wind up
+// extending the external symbol index space range. The host object
+// loader stores symbol payloads in loader.payloads using SymbolBuilder.
//
-// - Each symbol gets a unique global index. For duplicated and
-// overwriting/overwritten symbols, the second (or later) appearance
-// of the symbol gets the same global index as the first appearance.
+// - Each symbol gets a unique global index. For duplicated and
+// overwriting/overwritten symbols, the second (or later) appearance
+// of the symbol gets the same global index as the first appearance.
type Loader struct {
start map[*oReader]Sym // map from object file to its start index
objs []objIdx // sorted by start index (i.e. objIdx.i)
@@ -671,7 +671,7 @@ func (l *Loader) resolve(r *oReader, s goobj.SymRef) Sym {
// when the runtime package is built. The canonical example is
// "runtime.racefuncenter" -- currently if you do something like
//
-// go build -gcflags=-race myprogram.go
+// go build -gcflags=-race myprogram.go
//
// the compiler will insert calls to the builtin runtime.racefuncenter,
// but the version of the runtime used for linkage won't actually contain
@@ -2341,6 +2341,10 @@ func (l *Loader) cloneToExternal(symIdx Sym) {
// need to access the old symbol content.)
l.objSyms[symIdx] = objSym{l.extReader.objidx, uint32(pi)}
l.extReader.syms = append(l.extReader.syms, symIdx)
+
+ // Some attributes were encoded in the object file. Copy them over.
+ l.SetAttrDuplicateOK(symIdx, r.Sym(li).Dupok())
+ l.SetAttrShared(symIdx, r.Shared())
}
// Copy the payload of symbol src to dst. Both src and dst must be external
@@ -2361,31 +2365,6 @@ func (l *Loader) CopySym(src, dst Sym) {
// TODO: other attributes?
}
-// CopyAttributes copies over all of the attributes of symbol 'src' to
-// symbol 'dst'.
-func (l *Loader) CopyAttributes(src Sym, dst Sym) {
- l.SetAttrReachable(dst, l.AttrReachable(src))
- l.SetAttrOnList(dst, l.AttrOnList(src))
- l.SetAttrLocal(dst, l.AttrLocal(src))
- l.SetAttrNotInSymbolTable(dst, l.AttrNotInSymbolTable(src))
- if l.IsExternal(dst) {
- l.SetAttrVisibilityHidden(dst, l.AttrVisibilityHidden(src))
- l.SetAttrDuplicateOK(dst, l.AttrDuplicateOK(src))
- l.SetAttrShared(dst, l.AttrShared(src))
- l.SetAttrExternal(dst, l.AttrExternal(src))
- } else {
- // Some attributes are modifiable only for external symbols.
- // In such cases, don't try to transfer over the attribute
- // from the source even if there is a clash. This comes up
- // when copying attributes from a dupOK ABI wrapper symbol to
- // the real target symbol (which may not be marked dupOK).
- }
- l.SetAttrSpecial(dst, l.AttrSpecial(src))
- l.SetAttrCgoExportDynamic(dst, l.AttrCgoExportDynamic(src))
- l.SetAttrCgoExportStatic(dst, l.AttrCgoExportStatic(src))
- l.SetAttrReadOnly(dst, l.AttrReadOnly(src))
-}
-
// CreateExtSym creates a new external symbol with the specified name
// without adding it to any lookup tables, returning a Sym index for it.
func (l *Loader) CreateExtSym(name string, ver int) Sym {
diff --git a/src/cmd/link/internal/ppc64/asm.go b/src/cmd/link/internal/ppc64/asm.go
index d2b140b45de..73c2718a336 100644
--- a/src/cmd/link/internal/ppc64/asm.go
+++ b/src/cmd/link/internal/ppc64/asm.go
@@ -766,7 +766,7 @@ func trampoline(ctxt *ld.Link, ldr *loader.Loader, ri int, rs, s loader.Sym) {
// For external linking, the linker can insert a call stub to handle a long call, but depends on having the TOC address in
// r2. For those build modes with external linking where the TOC address is not maintained in r2, trampolines must be created.
if ctxt.IsExternal() && r2Valid(ctxt) {
- // No trampolines needed since r2 contains the TOC
+ // The TOC pointer is valid. The external linker will insert trampolines.
return
}
@@ -819,14 +819,9 @@ func trampoline(ctxt *ld.Link, ldr *loader.Loader, ri int, rs, s loader.Sym) {
}
}
if ldr.SymType(tramp) == 0 {
- if r2Valid(ctxt) {
- // Should have returned for above cases
- ctxt.Errorf(s, "unexpected trampoline for shared or dynamic linking")
- } else {
- trampb := ldr.MakeSymbolUpdater(tramp)
- ctxt.AddTramp(trampb)
- gentramp(ctxt, ldr, trampb, rs, r.Add())
- }
+ trampb := ldr.MakeSymbolUpdater(tramp)
+ ctxt.AddTramp(trampb)
+ gentramp(ctxt, ldr, trampb, rs, r.Add())
}
sb := ldr.MakeSymbolUpdater(s)
relocs := sb.Relocs()
@@ -842,7 +837,6 @@ func trampoline(ctxt *ld.Link, ldr *loader.Loader, ri int, rs, s loader.Sym) {
func gentramp(ctxt *ld.Link, ldr *loader.Loader, tramp *loader.SymbolBuilder, target loader.Sym, offset int64) {
tramp.SetSize(16) // 4 instructions
P := make([]byte, tramp.Size())
- t := ldr.SymValue(target) + offset
var o1, o2 uint32
if ctxt.IsAIX() {
@@ -851,8 +845,8 @@ func gentramp(ctxt *ld.Link, ldr *loader.Loader, tramp *loader.SymbolBuilder, ta
// However, all text symbols are accessed with a TOC symbol as
// text relocations aren't supposed to be possible.
// So, keep using the external linking way to be more AIX friendly.
- o1 = uint32(0x3fe20000) // lis r2, toctargetaddr hi
- o2 = uint32(0xebff0000) // ld r31, toctargetaddr lo
+ o1 = uint32(0x3c000000) | 12<<21 | 2<<16 // addis r12, r2, toctargetaddr hi
+ o2 = uint32(0xe8000000) | 12<<21 | 12<<16 // ld r12, r12, toctargetaddr lo
toctramp := ldr.CreateSymForUpdate("TOC."+ldr.SymName(tramp.Sym()), 0)
toctramp.SetType(sym.SXCOFFTOC)
@@ -866,31 +860,32 @@ func gentramp(ctxt *ld.Link, ldr *loader.Loader, tramp *loader.SymbolBuilder, ta
// Used for default build mode for an executable
// Address of the call target is generated using
// relocation and doesn't depend on r2 (TOC).
- o1 = uint32(0x3fe00000) // lis r31,targetaddr hi
- o2 = uint32(0x3bff0000) // addi r31,targetaddr lo
+ o1 = uint32(0x3c000000) | 12<<21 // lis r12,targetaddr hi
+ o2 = uint32(0x38000000) | 12<<21 | 12<<16 // addi r12,r12,targetaddr lo
- // With external linking, the target address must be
- // relocated using LO and HA
- if ctxt.IsExternal() || ldr.SymValue(target) == 0 {
+ t := ldr.SymValue(target)
+ if t == 0 || r2Valid(ctxt) || ctxt.IsExternal() {
+ // Target address is unknown, generate relocations
r, _ := tramp.AddRel(objabi.R_ADDRPOWER)
+ if r2Valid(ctxt) {
+ // Use a TOC relative address if R2 holds the TOC pointer
+ o1 |= uint32(2 << 16) // Transform lis r31,ha into addis r31,r2,ha
+ r.SetType(objabi.R_ADDRPOWER_TOCREL)
+ }
r.SetOff(0)
r.SetSiz(8) // generates 2 relocations: HA + LO
r.SetSym(target)
r.SetAdd(offset)
} else {
- // adjustment needed if lo has sign bit set
- // when using addi to compute address
- val := uint32((t & 0xffff0000) >> 16)
- if t&0x8000 != 0 {
- val += 1
- }
- o1 |= val // hi part of addr
- o2 |= uint32(t & 0xffff) // lo part of addr
+ // The target address is known, resolve it
+ t += offset
+ o1 |= (uint32(t) + 0x8000) >> 16 // HA
+ o2 |= uint32(t) & 0xFFFF // LO
}
}
- o3 := uint32(0x7fe903a6) // mtctr r31
- o4 := uint32(0x4e800420) // bctr
+ o3 := uint32(0x7c0903a6) | 12<<21 // mtctr r12
+ o4 := uint32(0x4e800420) // bctr
ctxt.Arch.ByteOrder.PutUint32(P, o1)
ctxt.Arch.ByteOrder.PutUint32(P[4:], o2)
ctxt.Arch.ByteOrder.PutUint32(P[8:], o3)
@@ -962,7 +957,7 @@ func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loade
// If we are linking PIE or shared code, all golang generated object files have an extra 2 instruction prologue
// to regenerate the TOC pointer from R12. The exception are two special case functions tested below. Note,
// local call offsets for externally generated objects are accounted for when converting into golang relocs.
- if !ldr.IsExternal(rs) && ldr.AttrShared(rs) && tgtName != "runtime.duffzero" && tgtName != "runtime.duffcopy" {
+ if !ldr.AttrExternal(rs) && ldr.AttrShared(rs) && tgtName != "runtime.duffzero" && tgtName != "runtime.duffcopy" {
// Furthermore, only apply the offset if the target looks like the start of a function call.
if r.Add() == 0 && ldr.SymType(rs) == sym.STEXT {
t += 8
diff --git a/src/cmd/link/internal/sym/symkind.go b/src/cmd/link/internal/sym/symkind.go
index 0a0741f84bf..3ed04c49af9 100644
--- a/src/cmd/link/internal/sym/symkind.go
+++ b/src/cmd/link/internal/sym/symkind.go
@@ -38,6 +38,7 @@ type SymKind uint8
// Defined SymKind values.
//
// TODO(rsc): Give idiomatic Go names.
+//
//go:generate stringer -type=SymKind
const (
Sxxx SymKind = iota
diff --git a/src/cmd/link/link_test.go b/src/cmd/link/link_test.go
index 8df31d7fd49..ac68008d8dd 100644
--- a/src/cmd/link/link_test.go
+++ b/src/cmd/link/link_test.go
@@ -642,8 +642,12 @@ func TestTrampoline(t *testing.T) {
// For stress test, we set -debugtramp=2 flag, which sets a very low
// threshold for trampoline generation, and essentially all cross-package
// calls will use trampolines.
+ buildmodes := []string{"default"}
switch runtime.GOARCH {
- case "arm", "arm64", "ppc64", "ppc64le":
+ case "arm", "arm64", "ppc64":
+ case "ppc64le":
+ // Trampolines are generated differently when internal linking PIE, test them too.
+ buildmodes = append(buildmodes, "pie")
default:
t.Skipf("trampoline insertion is not implemented on %s", runtime.GOARCH)
}
@@ -661,18 +665,20 @@ func TestTrampoline(t *testing.T) {
}
exe := filepath.Join(tmpdir, "hello.exe")
- cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-debugtramp=2", "-o", exe, src)
- out, err := cmd.CombinedOutput()
- if err != nil {
- t.Fatalf("build failed: %v\n%s", err, out)
- }
- cmd = exec.Command(exe)
- out, err = cmd.CombinedOutput()
- if err != nil {
- t.Errorf("executable failed to run: %v\n%s", err, out)
- }
- if string(out) != "hello\n" {
- t.Errorf("unexpected output:\n%s", out)
+ for _, mode := range buildmodes {
+ cmd := exec.Command(testenv.GoToolPath(t), "build", "-buildmode="+mode, "-ldflags=-debugtramp=2", "-o", exe, src)
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("build (%s) failed: %v\n%s", mode, err, out)
+ }
+ cmd = exec.Command(exe)
+ out, err = cmd.CombinedOutput()
+ if err != nil {
+ t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out)
+ }
+ if string(out) != "hello\n" {
+ t.Errorf("unexpected output (%s):\n%s", mode, out)
+ }
}
}
@@ -693,8 +699,12 @@ func TestTrampolineCgo(t *testing.T) {
// For stress test, we set -debugtramp=2 flag, which sets a very low
// threshold for trampoline generation, and essentially all cross-package
// calls will use trampolines.
+ buildmodes := []string{"default"}
switch runtime.GOARCH {
- case "arm", "arm64", "ppc64", "ppc64le":
+ case "arm", "arm64", "ppc64":
+ case "ppc64le":
+ // Trampolines are generated differently when internal linking PIE, test them too.
+ buildmodes = append(buildmodes, "pie")
default:
t.Skipf("trampoline insertion is not implemented on %s", runtime.GOARCH)
}
@@ -713,37 +723,39 @@ func TestTrampolineCgo(t *testing.T) {
}
exe := filepath.Join(tmpdir, "hello.exe")
- cmd := exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-debugtramp=2", "-o", exe, src)
- out, err := cmd.CombinedOutput()
- if err != nil {
- t.Fatalf("build failed: %v\n%s", err, out)
- }
- cmd = exec.Command(exe)
- out, err = cmd.CombinedOutput()
- if err != nil {
- t.Errorf("executable failed to run: %v\n%s", err, out)
- }
- if string(out) != "hello\n" && string(out) != "hello\r\n" {
- t.Errorf("unexpected output:\n%s", out)
- }
+ for _, mode := range buildmodes {
+ cmd := exec.Command(testenv.GoToolPath(t), "build", "-buildmode="+mode, "-ldflags=-debugtramp=2", "-o", exe, src)
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("build (%s) failed: %v\n%s", mode, err, out)
+ }
+ cmd = exec.Command(exe)
+ out, err = cmd.CombinedOutput()
+ if err != nil {
+ t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out)
+ }
+ if string(out) != "hello\n" && string(out) != "hello\r\n" {
+ t.Errorf("unexpected output (%s):\n%s", mode, out)
+ }
- // Test internal linking mode.
+ // Test internal linking mode.
- if runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le" || (runtime.GOARCH == "arm64" && runtime.GOOS == "windows") || !testenv.CanInternalLink() {
- return // internal linking cgo is not supported
- }
- cmd = exec.Command(testenv.GoToolPath(t), "build", "-ldflags=-debugtramp=2 -linkmode=internal", "-o", exe, src)
- out, err = cmd.CombinedOutput()
- if err != nil {
- t.Fatalf("build failed: %v\n%s", err, out)
- }
- cmd = exec.Command(exe)
- out, err = cmd.CombinedOutput()
- if err != nil {
- t.Errorf("executable failed to run: %v\n%s", err, out)
- }
- if string(out) != "hello\n" && string(out) != "hello\r\n" {
- t.Errorf("unexpected output:\n%s", out)
+ if runtime.GOARCH == "ppc64" || (runtime.GOARCH == "arm64" && runtime.GOOS == "windows") || !testenv.CanInternalLink() {
+ return // internal linking cgo is not supported
+ }
+ cmd = exec.Command(testenv.GoToolPath(t), "build", "-buildmode="+mode, "-ldflags=-debugtramp=2 -linkmode=internal", "-o", exe, src)
+ out, err = cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("build (%s) failed: %v\n%s", mode, err, out)
+ }
+ cmd = exec.Command(exe)
+ out, err = cmd.CombinedOutput()
+ if err != nil {
+ t.Errorf("executable failed to run (%s): %v\n%s", mode, err, out)
+ }
+ if string(out) != "hello\n" && string(out) != "hello\r\n" {
+ t.Errorf("unexpected output (%s):\n%s", mode, out)
+ }
}
}
diff --git a/src/cmd/nm/doc.go b/src/cmd/nm/doc.go
index b62da47c009..b11a2a84565 100644
--- a/src/cmd/nm/doc.go
+++ b/src/cmd/nm/doc.go
@@ -5,6 +5,7 @@
// Nm lists the symbols defined or used by an object file, archive, or executable.
//
// Usage:
+//
// go tool nm [options] file...
//
// The default output prints one line per symbol, with three space-separated
@@ -37,5 +38,4 @@
// size orders from largest to smallest
// -type
// print symbol type after name
-//
package main
diff --git a/src/cmd/pack/doc.go b/src/cmd/pack/doc.go
index a702594e23d..22c361ee09e 100644
--- a/src/cmd/pack/doc.go
+++ b/src/cmd/pack/doc.go
@@ -3,11 +3,11 @@
// license that can be found in the LICENSE file.
/*
-
Pack is a simple version of the traditional Unix ar tool.
It implements only the operations needed by Go.
Usage:
+
go tool pack op file.a [name...]
Pack applies the operation to the archive, using the names as arguments to the operation.
@@ -36,6 +36,5 @@ For the c and r commands, names are printed as files are added.
For the p command, each file is prefixed by the name on a line by itself.
For the t command, the listing includes additional file metadata.
For the x command, names are printed as files are extracted.
-
*/
package main
diff --git a/src/cmd/test2json/main.go b/src/cmd/test2json/main.go
index fdf681a8ffe..b1c2d0696d2 100644
--- a/src/cmd/test2json/main.go
+++ b/src/cmd/test2json/main.go
@@ -26,7 +26,7 @@
// binary's output. To convert the output of a "go test" command,
// use "go test -json" instead of invoking test2json directly.
//
-// Output Format
+// # Output Format
//
// The JSON stream is a newline-separated sequence of TestEvent objects
// corresponding to the Go struct:
@@ -80,7 +80,6 @@
// as a sequence of events with Test set to the benchmark name, terminated
// by a final event with Action == "bench" or "fail".
// Benchmarks have no events with Action == "run", "pause", or "cont".
-//
package main
import (
diff --git a/src/cmd/trace/annotations_test.go b/src/cmd/trace/annotations_test.go
index 9f1c8e3b3b0..ca14d3160b0 100644
--- a/src/cmd/trace/annotations_test.go
+++ b/src/cmd/trace/annotations_test.go
@@ -53,9 +53,9 @@ func TestOverlappingDuration(t *testing.T) {
// prog0 starts three goroutines.
//
-// goroutine 1: taskless region
-// goroutine 2: starts task0, do work in task0.region0, starts task1 which ends immediately.
-// goroutine 3: do work in task0.region1 and task0.region2, ends task0
+// goroutine 1: taskless region
+// goroutine 2: starts task0, do work in task0.region0, starts task1 which ends immediately.
+// goroutine 3: do work in task0.region1 and task0.region2, ends task0
func prog0() {
ctx := context.Background()
diff --git a/src/cmd/trace/doc.go b/src/cmd/trace/doc.go
index ed1f930a7bb..26a96fac2f1 100644
--- a/src/cmd/trace/doc.go
+++ b/src/cmd/trace/doc.go
@@ -6,25 +6,31 @@
Trace is a tool for viewing trace files.
Trace files can be generated with:
- - runtime/trace.Start
- - net/http/pprof package
- - go test -trace
+ - runtime/trace.Start
+ - net/http/pprof package
+ - go test -trace
Example usage:
Generate a trace file with 'go test':
+
go test -trace trace.out pkg
+
View the trace in a web browser:
+
go tool trace trace.out
+
Generate a pprof-like profile from the trace:
+
go tool trace -pprof=TYPE trace.out > TYPE.pprof
Supported profile types are:
- - net: network blocking profile
- - sync: synchronization blocking profile
- - syscall: syscall blocking profile
- - sched: scheduler latency profile
+ - net: network blocking profile
+ - sync: synchronization blocking profile
+ - syscall: syscall blocking profile
+ - sched: scheduler latency profile
Then, you can use the pprof tool to analyze the profile:
+
go tool pprof TYPE.pprof
Note that while the various profiles available when launching
diff --git a/src/cmd/vet/doc.go b/src/cmd/vet/doc.go
index 279d081be39..e230d3be060 100644
--- a/src/cmd/vet/doc.go
+++ b/src/cmd/vet/doc.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
/*
-
Vet examines Go source code and reports suspicious constructs, such as Printf
calls whose arguments do not align with the format string. Vet uses heuristics
that do not guarantee all reports are genuine problems, but it can find errors
@@ -28,27 +27,27 @@ program correctness.
To list the available checks, run "go tool vet help":
- asmdecl report mismatches between assembly files and Go declarations
- assign check for useless assignments
- atomic check for common mistakes using the sync/atomic package
- bools check for common mistakes involving boolean operators
- buildtag check that +build tags are well-formed and correctly located
- cgocall detect some violations of the cgo pointer passing rules
- composites check for unkeyed composite literals
- copylocks check for locks erroneously passed by value
- httpresponse check for mistakes using HTTP responses
- loopclosure check references to loop variables from within nested functions
- lostcancel check cancel func returned by context.WithCancel is called
- nilfunc check for useless comparisons between functions and nil
- printf check consistency of Printf format strings and arguments
- shift check for shifts that equal or exceed the width of the integer
- stdmethods check signature of methods of well-known interfaces
- structtag check that struct field tags conform to reflect.StructTag.Get
- tests check for common mistaken usages of tests and examples
- unmarshal report passing non-pointer or non-interface values to unmarshal
- unreachable check for unreachable code
- unsafeptr check for invalid conversions of uintptr to unsafe.Pointer
- unusedresult check for unused results of calls to some functions
+ asmdecl report mismatches between assembly files and Go declarations
+ assign check for useless assignments
+ atomic check for common mistakes using the sync/atomic package
+ bools check for common mistakes involving boolean operators
+ buildtag check that +build tags are well-formed and correctly located
+ cgocall detect some violations of the cgo pointer passing rules
+ composites check for unkeyed composite literals
+ copylocks check for locks erroneously passed by value
+ httpresponse check for mistakes using HTTP responses
+ loopclosure check references to loop variables from within nested functions
+ lostcancel check cancel func returned by context.WithCancel is called
+ nilfunc check for useless comparisons between functions and nil
+ printf check consistency of Printf format strings and arguments
+ shift check for shifts that equal or exceed the width of the integer
+ stdmethods check signature of methods of well-known interfaces
+ structtag check that struct field tags conform to reflect.StructTag.Get
+ tests check for common mistaken usages of tests and examples
+ unmarshal report passing non-pointer or non-interface values to unmarshal
+ unreachable check for unreachable code
+ unsafeptr check for invalid conversions of uintptr to unsafe.Pointer
+ unusedresult check for unused results of calls to some functions
For details and flags of a particular check, such as printf, run "go tool vet help printf".
@@ -62,10 +61,9 @@ For information on writing a new check, see golang.org/x/tools/go/analysis.
Core flags:
- -c=N
- display offending line plus N lines of surrounding context
- -json
- emit analysis diagnostics (and errors) in JSON format
-
+ -c=N
+ display offending line plus N lines of surrounding context
+ -json
+ emit analysis diagnostics (and errors) in JSON format
*/
package main
diff --git a/src/compress/bzip2/bzip2.go b/src/compress/bzip2/bzip2.go
index 0d8c286c167..51054ccabc9 100644
--- a/src/compress/bzip2/bzip2.go
+++ b/src/compress/bzip2/bzip2.go
@@ -447,11 +447,11 @@ func (bz2 *reader) readBlock() (err error) {
// inverseBWT implements the inverse Burrows-Wheeler transform as described in
// http://www.hpl.hp.com/techreports/Compaq-DEC/SRC-RR-124.pdf, section 4.2.
-// In that document, origPtr is called ``I'' and c is the ``C'' array after the
+// In that document, origPtr is called “I” and c is the “C” array after the
// first pass over the data. It's an argument here because we merge the first
// pass with the Huffman decoding.
//
-// This also implements the ``single array'' method from the bzip2 source code
+// This also implements the “single array” method from the bzip2 source code
// which leaves the output, still shuffled, in the bottom 8 bits of tt with the
// index of the next byte in the top 24-bits. The index of the first byte is
// returned.
diff --git a/src/compress/flate/dict_decoder.go b/src/compress/flate/dict_decoder.go
index 3b59d483511..d2c19040f54 100644
--- a/src/compress/flate/dict_decoder.go
+++ b/src/compress/flate/dict_decoder.go
@@ -7,19 +7,19 @@ package flate
// dictDecoder implements the LZ77 sliding dictionary as used in decompression.
// LZ77 decompresses data through sequences of two forms of commands:
//
-// * Literal insertions: Runs of one or more symbols are inserted into the data
-// stream as is. This is accomplished through the writeByte method for a
-// single symbol, or combinations of writeSlice/writeMark for multiple symbols.
-// Any valid stream must start with a literal insertion if no preset dictionary
-// is used.
+// - Literal insertions: Runs of one or more symbols are inserted into the data
+// stream as is. This is accomplished through the writeByte method for a
+// single symbol, or combinations of writeSlice/writeMark for multiple symbols.
+// Any valid stream must start with a literal insertion if no preset dictionary
+// is used.
//
-// * Backward copies: Runs of one or more symbols are copied from previously
-// emitted data. Backward copies come as the tuple (dist, length) where dist
-// determines how far back in the stream to copy from and length determines how
-// many bytes to copy. Note that it is valid for the length to be greater than
-// the distance. Since LZ77 uses forward copies, that situation is used to
-// perform a form of run-length encoding on repeated runs of symbols.
-// The writeCopy and tryWriteCopy are used to implement this command.
+// - Backward copies: Runs of one or more symbols are copied from previously
+// emitted data. Backward copies come as the tuple (dist, length) where dist
+// determines how far back in the stream to copy from and length determines how
+// many bytes to copy. Note that it is valid for the length to be greater than
+// the distance. Since LZ77 uses forward copies, that situation is used to
+// perform a form of run-length encoding on repeated runs of symbols.
+// The writeCopy and tryWriteCopy are used to implement this command.
//
// For performance reasons, this implementation performs little to no sanity
// checks about the arguments. As such, the invariants documented for each
diff --git a/src/compress/flate/huffman_bit_writer.go b/src/compress/flate/huffman_bit_writer.go
index b3ae76d0821..6a4e48e9ee1 100644
--- a/src/compress/flate/huffman_bit_writer.go
+++ b/src/compress/flate/huffman_bit_writer.go
@@ -194,9 +194,9 @@ func (w *huffmanBitWriter) writeBytes(bytes []byte) {
// Codes 0-15 are single byte codes. Codes 16-18 are followed by additional
// information. Code badCode is an end marker
//
-// numLiterals The number of literals in literalEncoding
-// numOffsets The number of offsets in offsetEncoding
-// litenc, offenc The literal and offset encoder to use
+// numLiterals The number of literals in literalEncoding
+// numOffsets The number of offsets in offsetEncoding
+// litenc, offenc The literal and offset encoder to use
func (w *huffmanBitWriter) generateCodegen(numLiterals int, numOffsets int, litEnc, offEnc *huffmanEncoder) {
for i := range w.codegenFreq {
w.codegenFreq[i] = 0
@@ -353,9 +353,9 @@ func (w *huffmanBitWriter) writeCode(c hcode) {
// Write the header of a dynamic Huffman block to the output stream.
//
-// numLiterals The number of literals specified in codegen
-// numOffsets The number of offsets specified in codegen
-// numCodegens The number of codegens used in codegen
+// numLiterals The number of literals specified in codegen
+// numOffsets The number of offsets specified in codegen
+// numCodegens The number of codegens used in codegen
func (w *huffmanBitWriter) writeDynamicHeader(numLiterals int, numOffsets int, numCodegens int, isEof bool) {
if w.err != nil {
return
diff --git a/src/compress/lzw/reader.go b/src/compress/lzw/reader.go
index 952870a56a2..18df97029f2 100644
--- a/src/compress/lzw/reader.go
+++ b/src/compress/lzw/reader.go
@@ -3,8 +3,8 @@
// license that can be found in the LICENSE file.
// Package lzw implements the Lempel-Ziv-Welch compressed data format,
-// described in T. A. Welch, ``A Technique for High-Performance Data
-// Compression'', Computer, 17(6) (June 1984), pp 8-19.
+// described in T. A. Welch, “A Technique for High-Performance Data
+// Compression”, Computer, 17(6) (June 1984), pp 8-19.
//
// In particular, it implements LZW as used by the GIF and PDF file
// formats, which means variable-width codes up to 12 bits and the first
diff --git a/src/container/heap/heap.go b/src/container/heap/heap.go
index c3168f9b277..27de11e19e6 100644
--- a/src/container/heap/heap.go
+++ b/src/container/heap/heap.go
@@ -13,7 +13,6 @@
// ordering for the Less method, so Push adds items while Pop removes the
// highest-priority item from the queue. The Examples include such an
// implementation; the file example_pq_test.go has the complete source.
-//
package heap
import "sort"
diff --git a/src/container/list/list.go b/src/container/list/list.go
index 9555ad39003..f2d77f05600 100644
--- a/src/container/list/list.go
+++ b/src/container/list/list.go
@@ -5,10 +5,10 @@
// Package list implements a doubly linked list.
//
// To iterate over a list (where l is a *List):
+//
// for e := l.Front(); e != nil; e = e.Next() {
// // do something with e.Value
// }
-//
package list
// Element is an element of a linked list.
diff --git a/src/context/context.go b/src/context/context.go
index cf010b2a695..1070111efa5 100644
--- a/src/context/context.go
+++ b/src/context/context.go
@@ -30,9 +30,9 @@
// explicitly to each function that needs it. The Context should be the first
// parameter, typically named ctx:
//
-// func DoSomething(ctx context.Context, arg Arg) error {
-// // ... use ctx ...
-// }
+// func DoSomething(ctx context.Context, arg Arg) error {
+// // ... use ctx ...
+// }
//
// Do not pass a nil Context, even if a function permits it. Pass context.TODO
// if you are unsure about which Context to use.
@@ -498,11 +498,11 @@ func (c *timerCtx) cancel(removeFromParent bool, err error) {
// Canceling this context releases resources associated with it, so code should
// call cancel as soon as the operations running in this Context complete:
//
-// func slowOperationWithTimeout(ctx context.Context) (Result, error) {
-// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
-// defer cancel() // releases resources if slowOperation completes before timeout elapses
-// return slowOperation(ctx)
-// }
+// func slowOperationWithTimeout(ctx context.Context) (Result, error) {
+// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
+// defer cancel() // releases resources if slowOperation completes before timeout elapses
+// return slowOperation(ctx)
+// }
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
return WithDeadline(parent, time.Now().Add(timeout))
}
diff --git a/src/crypto/aes/asm_ppc64le.s b/src/crypto/aes/asm_ppc64le.s
index 5eae6753229..647e8469b70 100644
--- a/src/crypto/aes/asm_ppc64le.s
+++ b/src/crypto/aes/asm_ppc64le.s
@@ -22,13 +22,14 @@
#include "textflag.h"
-// For set{En,De}cryptKeyAsm
+// For expandKeyAsm
#define INP R3
#define BITS R4
-#define OUT R5
+#define OUTENC R5 // Pointer to next expanded encrypt key
#define PTR R6
#define CNT R7
#define ROUNDS R8
+#define OUTDEC R9 // Pointer to next expanded decrypt key
#define TEMP R19
#define ZERO V0
#define IN0 V1
@@ -43,6 +44,10 @@
#define OUTHEAD V10
#define OUTTAIL V11
+// For P9 instruction emulation
+#define ESPERM V21 // Endian swapping permute into BE
+#define TMP2 V22 // Temporary for P8_STXVB16X/P8_STXV
+
// For {en,de}cryptBlockAsm
#define BLK_INP R3
#define BLK_OUT R4
@@ -50,83 +55,82 @@
#define BLK_ROUNDS R6
#define BLK_IDX R7
-DATA ·rcon+0x00(SB)/8, $0x0100000001000000 // RCON
-DATA ·rcon+0x08(SB)/8, $0x0100000001000000 // RCON
-DATA ·rcon+0x10(SB)/8, $0x1b0000001b000000
-DATA ·rcon+0x18(SB)/8, $0x1b0000001b000000
-DATA ·rcon+0x20(SB)/8, $0x0d0e0f0c0d0e0f0c // MASK
-DATA ·rcon+0x28(SB)/8, $0x0d0e0f0c0d0e0f0c // MASK
-DATA ·rcon+0x30(SB)/8, $0x0000000000000000
-DATA ·rcon+0x38(SB)/8, $0x0000000000000000
-GLOBL ·rcon(SB), RODATA, $64
-
-// func setEncryptKeyAsm(key *byte, keylen int, enc *uint32) int
-TEXT ·setEncryptKeyAsm(SB), NOSPLIT|NOFRAME, $0
+DATA ·rcon+0x00(SB)/8, $0x0f0e0d0c0b0a0908 // Permute for vector doubleword endian swap
+DATA ·rcon+0x08(SB)/8, $0x0706050403020100
+DATA ·rcon+0x10(SB)/8, $0x0100000001000000 // RCON
+DATA ·rcon+0x18(SB)/8, $0x0100000001000000 // RCON
+DATA ·rcon+0x20(SB)/8, $0x1b0000001b000000
+DATA ·rcon+0x28(SB)/8, $0x1b0000001b000000
+DATA ·rcon+0x30(SB)/8, $0x0d0e0f0c0d0e0f0c // MASK
+DATA ·rcon+0x38(SB)/8, $0x0d0e0f0c0d0e0f0c // MASK
+DATA ·rcon+0x40(SB)/8, $0x0000000000000000
+DATA ·rcon+0x48(SB)/8, $0x0000000000000000
+GLOBL ·rcon(SB), RODATA, $80
+
+// Emulate unaligned BE vector load/stores on LE targets
+#define P8_LXVB16X(RA,RB,VT) \
+ LXVD2X (RA+RB), VT \
+ VPERM VT, VT, ESPERM, VT
+
+#define P8_STXVB16X(VS,RA,RB) \
+ VPERM VS, VS, ESPERM, TMP2 \
+ STXVD2X TMP2, (RA+RB)
+
+#define P8_STXV(VS,RA,RB) \
+ XXPERMDI VS, VS, $2, TMP2 \
+ STXVD2X TMP2, (RA+RB)
+
+#define P8_LXV(RA,RB,VT) \
+ LXVD2X (RA+RB), VT \
+ XXPERMDI VT, VT, $2, VT
+
+#define LXSDX_BE(RA,RB,VT) \
+ LXSDX (RA+RB), VT \
+ VPERM VT, VT, ESPERM, VT
+
+// func setEncryptKeyAsm(nr int, key *byte, enc *uint32, dec *uint32)
+TEXT ·expandKeyAsm(SB), NOSPLIT|NOFRAME, $0
// Load the arguments inside the registers
- MOVD key+0(FP), INP
- MOVD keylen+8(FP), BITS
- MOVD enc+16(FP), OUT
- JMP ·doEncryptKeyAsm(SB)
-
-// This text is used both setEncryptKeyAsm and setDecryptKeyAsm
-TEXT ·doEncryptKeyAsm(SB), NOSPLIT|NOFRAME, $0
- // Do not change R10 since it's storing the LR value in setDecryptKeyAsm
-
- // Check arguments
- MOVD $-1, PTR // li 6,-1 exit code to -1 (255)
- CMPU INP, $0 // cmpldi r3,0 input key pointer set?
- BC 0x0E, 2, enc_key_abort // beq- .Lenc_key_abort
- CMPU OUT, $0 // cmpldi r5,0 output key pointer set?
- BC 0x0E, 2, enc_key_abort // beq- .Lenc_key_abort
- MOVD $-2, PTR // li 6,-2 exit code to -2 (254)
- CMPW BITS, $128 // cmpwi 4,128 greater or equal to 128
- BC 0x0E, 0, enc_key_abort // blt- .Lenc_key_abort
- CMPW BITS, $256 // cmpwi 4,256 lesser or equal to 256
- BC 0x0E, 1, enc_key_abort // bgt- .Lenc_key_abort
- ANDCC $0x3f, BITS, TEMP // andi. 0,4,0x3f multiple of 64
- BC 0x06, 2, enc_key_abort // bne- .Lenc_key_abort
+ MOVD nr+0(FP), ROUNDS
+ MOVD key+8(FP), INP
+ MOVD enc+16(FP), OUTENC
+ MOVD dec+24(FP), OUTDEC
MOVD $·rcon(SB), PTR // PTR point to rcon addr
+ LVX (PTR), ESPERM
+ ADD $0x10, PTR
// Get key from memory and write aligned into VR
- NEG INP, R9 // neg 9,3 R9 is ~INP + 1
- LVX (INP)(R0), IN0 // lvx 1,0,3 Load key inside IN0
- ADD $15, INP, INP // addi 3,3,15 Add 15B to INP addr
- LVSR (R9)(R0), KEY // lvsr 3,0,9
- MOVD $0x20, R8 // li 8,0x20 R8 = 32
- CMPW BITS, $192 // cmpwi 4,192 Key size == 192?
- LVX (INP)(R0), IN1 // lvx 2,0,3
- VSPLTISB $0x0f, MASK// vspltisb 5,0x0f 0x0f0f0f0f... mask
+ P8_LXVB16X(INP, R0, IN0)
+ ADD $0x10, INP, INP
+ MOVD $0x20, TEMP
+
+ CMPW ROUNDS, $12
LVX (PTR)(R0), RCON // lvx 4,0,6 Load first 16 bytes into RCON
- VXOR KEY, MASK, KEY // vxor 3,3,5 Adjust for byte swap
- LVX (PTR)(R8), MASK // lvx 5,8,6
+ LVX (PTR)(TEMP), MASK
ADD $0x10, PTR, PTR // addi 6,6,0x10 PTR to next 16 bytes of RCON
- VPERM IN0, IN1, KEY, IN0 // vperm 1,1,2,3 Align
MOVD $8, CNT // li 7,8 CNT = 8
VXOR ZERO, ZERO, ZERO // vxor 0,0,0 Zero to be zero :)
MOVD CNT, CTR // mtctr 7 Set the counter to 8 (rounds)
- LVSL (OUT)(R0), OUTPERM // lvsl 8,0,5
- VSPLTISB $-1, OUTMASK // vspltisb 9,-1
- LVX (OUT)(R0), OUTHEAD // lvx 10,0,5
- VPERM OUTMASK, ZERO, OUTPERM, OUTMASK // vperm 9,9,0,8
-
- BLT loop128 // blt .Loop128
- ADD $8, INP, INP // addi 3,3,8
- BEQ l192 // beq .L192
- ADD $8, INP, INP // addi 3,3,8
- JMP l256 // b .L256
+ // The expanded decrypt key is the expanded encrypt key stored in reverse order.
+ // Move OUTDEC to the last key location, and store in descending order.
+ ADD $160, OUTDEC, OUTDEC
+ BLT loop128
+ ADD $32, OUTDEC, OUTDEC
+ BEQ l192
+ ADD $32, OUTDEC, OUTDEC
+ JMP l256
loop128:
// Key schedule (Round 1 to 8)
VPERM IN0, IN0, MASK, KEY // vperm 3,1,1,5 Rotate-n-splat
VSLDOI $12, ZERO, IN0, TMP // vsldoi 6,0,1,12
- VPERM IN0, IN0, OUTPERM, OUTTAIL // vperm 11,1,1,8 Rotate
- VSEL OUTHEAD, OUTTAIL, OUTMASK, STAGE // vsel 7,10,11,9
- VOR OUTTAIL, OUTTAIL, OUTHEAD // vor 10,11,11
+ P8_STXV(IN0, R0, OUTENC)
+ P8_STXV(IN0, R0, OUTDEC)
VCIPHERLAST KEY, RCON, KEY // vcipherlast 3,3,4
- STVX STAGE, (OUT+R0) // stvx 7,0,5 Write to output
- ADD $16, OUT, OUT // addi 5,5,16 Point to the next round
+ ADD $16, OUTENC, OUTENC
+ ADD $-16, OUTDEC, OUTDEC
VXOR IN0, TMP, IN0 // vxor 1,1,6
VSLDOI $12, ZERO, TMP, TMP // vsldoi 6,0,6,12
@@ -142,12 +146,11 @@ loop128:
// Key schedule (Round 9)
VPERM IN0, IN0, MASK, KEY // vperm 3,1,1,5 Rotate-n-spat
VSLDOI $12, ZERO, IN0, TMP // vsldoi 6,0,1,12
- VPERM IN0, IN0, OUTPERM, OUTTAIL // vperm 11,1,1,8 Rotate
- VSEL OUTHEAD, OUTTAIL, OUTMASK, STAGE // vsel 7,10,11,9
- VOR OUTTAIL, OUTTAIL, OUTHEAD // vor 10,11,11
+ P8_STXV(IN0, R0, OUTENC)
+ P8_STXV(IN0, R0, OUTDEC)
VCIPHERLAST KEY, RCON, KEY // vcipherlast 3,3,4
- STVX STAGE, (OUT+R0) // stvx 7,0,5 Round 9
- ADD $16, OUT, OUT // addi 5,5,16
+ ADD $16, OUTENC, OUTENC
+ ADD $-16, OUTDEC, OUTDEC
// Key schedule (Round 10)
VXOR IN0, TMP, IN0 // vxor 1,1,6
@@ -160,12 +163,11 @@ loop128:
VPERM IN0, IN0, MASK, KEY // vperm 3,1,1,5 Rotate-n-splat
VSLDOI $12, ZERO, IN0, TMP // vsldoi 6,0,1,12
- VPERM IN0, IN0, OUTPERM, OUTTAIL // vperm 11,1,1,8 Rotate
- VSEL OUTHEAD, OUTTAIL, OUTMASK, STAGE // vsel 7,10,11,9
- VOR OUTTAIL, OUTTAIL, OUTHEAD // vor 10,11,11
+ P8_STXV(IN0, R0, OUTENC)
+ P8_STXV(IN0, R0, OUTDEC)
VCIPHERLAST KEY, RCON, KEY // vcipherlast 3,3,4
- STVX STAGE, (OUT+R0) // stvx 7,0,5 Round 10
- ADD $16, OUT, OUT // addi 5,5,16
+ ADD $16, OUTENC, OUTENC
+ ADD $-16, OUTDEC, OUTDEC
// Key schedule (Round 11)
VXOR IN0, TMP, IN0 // vxor 1,1,6
@@ -174,26 +176,18 @@ loop128:
VSLDOI $12, ZERO, TMP, TMP // vsldoi 6,0,6,12
VXOR IN0, TMP, IN0 // vxor 1,1,6
VXOR IN0, KEY, IN0 // vxor 1,1,3
- VPERM IN0, IN0, OUTPERM, OUTTAIL // vperm 11,1,1,8
- VSEL OUTHEAD, OUTTAIL, OUTMASK, STAGE // vsel 7,10,11,9
- VOR OUTTAIL, OUTTAIL, OUTHEAD // vor 10,11,11
- STVX STAGE, (OUT+R0) // stvx 7,0,5 Round 11
+ P8_STXV(IN0, R0, OUTENC)
+ P8_STXV(IN0, R0, OUTDEC)
- ADD $15, OUT, INP // addi 3,5,15
- ADD $0x50, OUT, OUT // addi 5,5,0x50
-
- MOVD $10, ROUNDS // li 8,10
- JMP done // b .Ldone
+ RET
l192:
- LVX (INP)(R0), TMP // lvx 6,0,3
+ LXSDX_BE(INP, R0, IN1) // Load next 8 bytes into upper half of VSR in BE order.
MOVD $4, CNT // li 7,4
- VPERM IN0, IN0, OUTPERM, OUTTAIL // vperm 11,1,1,8
- VSEL OUTHEAD, OUTTAIL, OUTMASK, STAGE // vsel 7,10,11,9
- VOR OUTTAIL, OUTTAIL, OUTHEAD // vor 10,11,11
- STVX STAGE, (OUT+R0) // stvx 7,0,5
- ADD $16, OUT, OUT // addi 5,5,16
- VPERM IN1, TMP, KEY, IN1 // vperm 2,2,6,3
+ P8_STXV(IN0, R0, OUTENC)
+ P8_STXV(IN0, R0, OUTDEC)
+ ADD $16, OUTENC, OUTENC
+ ADD $-16, OUTDEC, OUTDEC
VSPLTISB $8, KEY // vspltisb 3,8
MOVD CNT, CTR // mtctr 7
VSUBUBM MASK, KEY, MASK // vsububm 5,5,3
@@ -221,24 +215,22 @@ loop192:
VPERM IN1, IN1, MASK, KEY // vperm 3,2,2,5
VSLDOI $12, ZERO, IN0, TMP // vsldoi 6,0,1,12
- VPERM STAGE, STAGE, OUTPERM, OUTTAIL // vperm 11,7,7,8
- VSEL OUTHEAD, OUTTAIL, OUTMASK, STAGE // vsel 7,10,11,9
- VOR OUTTAIL, OUTTAIL, OUTHEAD // vor 10,11,11
+ P8_STXV(STAGE, R0, OUTENC)
+ P8_STXV(STAGE, R0, OUTDEC)
VCIPHERLAST KEY, RCON, KEY // vcipherlast 3,3,4
- STVX STAGE, (OUT+R0) // stvx 7,0,5
- ADD $16, OUT, OUT // addi 5,5,16
+ ADD $16, OUTENC, OUTENC
+ ADD $-16, OUTDEC, OUTDEC
VSLDOI $8, IN0, IN1, STAGE // vsldoi 7,1,2,8
VXOR IN0, TMP, IN0 // vxor 1,1,6
VSLDOI $12, ZERO, TMP, TMP // vsldoi 6,0,6,12
- VPERM STAGE, STAGE, OUTPERM, OUTTAIL // vperm 11,7,7,8
- VSEL OUTHEAD, OUTTAIL, OUTMASK, STAGE // vsel 7,10,11,9
- VOR OUTTAIL, OUTTAIL, OUTHEAD // vor 10,11,11
+ P8_STXV(STAGE, R0, OUTENC)
+ P8_STXV(STAGE, R0, OUTDEC)
VXOR IN0, TMP, IN0 // vxor 1,1,6
VSLDOI $12, ZERO, TMP, TMP // vsldoi 6,0,6,12
VXOR IN0, TMP, IN0 // vxor 1,1,6
- STVX STAGE, (OUT+R0) // stvx 7,0,5
- ADD $16, OUT, OUT // addi 5,5,16
+ ADD $16, OUTENC, OUTENC
+ ADD $-16, OUTDEC, OUTDEC
VSPLTW $3, IN0, TMP // vspltw 6,1,3
VXOR TMP, IN1, TMP // vxor 6,6,2
@@ -247,39 +239,31 @@ loop192:
VXOR IN1, TMP, IN1 // vxor 2,2,6
VXOR IN0, KEY, IN0 // vxor 1,1,3
VXOR IN1, KEY, IN1 // vxor 2,2,3
- VPERM IN0, IN0, OUTPERM, OUTTAIL // vperm 11,1,1,8
- VSEL OUTHEAD, OUTTAIL, OUTMASK, STAGE // vsel 7,10,11,9
- VOR OUTTAIL, OUTTAIL, OUTHEAD // vor 10,11,11
- STVX STAGE, (OUT+R0) // stvx 7,0,5
- ADD $15, OUT, INP // addi 3,5,15
- ADD $16, OUT, OUT // addi 5,5,16
+ P8_STXV(IN0, R0, OUTENC)
+ P8_STXV(IN0, R0, OUTDEC)
+ ADD $16, OUTENC, OUTENC
+ ADD $-16, OUTDEC, OUTDEC
BC 0x10, 0, loop192 // bdnz .Loop192
- MOVD $12, ROUNDS // li 8,12
- ADD $0x20, OUT, OUT // addi 5,5,0x20
- BR done // b .Ldone
+ RET
l256:
- LVX (INP)(R0), TMP // lvx 6,0,3
+ P8_LXVB16X(INP, R0, IN1)
MOVD $7, CNT // li 7,7
- MOVD $14, ROUNDS // li 8,14
- VPERM IN0, IN0, OUTPERM, OUTTAIL // vperm 11,1,1,8
- VSEL OUTHEAD, OUTTAIL, OUTMASK, STAGE // vsel 7,10,11,9
- VOR OUTTAIL, OUTTAIL, OUTHEAD // vor 10,11,11
- STVX STAGE, (OUT+R0) // stvx 7,0,5
- ADD $16, OUT, OUT // addi 5,5,16
- VPERM IN1, TMP, KEY, IN1 // vperm 2,2,6,3
+ P8_STXV(IN0, R0, OUTENC)
+ P8_STXV(IN0, R0, OUTDEC)
+ ADD $16, OUTENC, OUTENC
+ ADD $-16, OUTDEC, OUTDEC
MOVD CNT, CTR // mtctr 7
loop256:
VPERM IN1, IN1, MASK, KEY // vperm 3,2,2,5
VSLDOI $12, ZERO, IN0, TMP // vsldoi 6,0,1,12
- VPERM IN1, IN1, OUTPERM, OUTTAIL // vperm 11,2,2,8
- VSEL OUTHEAD, OUTTAIL, OUTMASK, STAGE // vsel 7,10,11,9
- VOR OUTTAIL, OUTTAIL, OUTHEAD // vor 10,11,11
+ P8_STXV(IN1, R0, OUTENC)
+ P8_STXV(IN1, R0, OUTDEC)
VCIPHERLAST KEY, RCON, KEY // vcipherlast 3,3,4
- STVX STAGE, (OUT+R0) // stvx 7,0,5
- ADD $16, OUT, OUT // addi 5,5,16
+ ADD $16, OUTENC, OUTENC
+ ADD $-16, OUTDEC, OUTDEC
VXOR IN0, TMP, IN0 // vxor 1,1,6
VSLDOI $12, ZERO, TMP, TMP // vsldoi 6,0,6,12
@@ -288,12 +272,10 @@ loop256:
VXOR IN0, TMP, IN0 // vxor 1,1,6
VADDUWM RCON, RCON, RCON // vadduwm 4,4,4
VXOR IN0, KEY, IN0 // vxor 1,1,3
- VPERM IN0, IN0, OUTPERM, OUTTAIL // vperm 11,1,1,8
- VSEL OUTHEAD, OUTTAIL, OUTMASK, STAGE // vsel 7,10,11,9
- VOR OUTTAIL, OUTTAIL, OUTHEAD // vor 10,11,11
- STVX STAGE, (OUT+R0) // stvx 7,0,5
- ADD $15, OUT, INP // addi 3,5,15
- ADD $16, OUT, OUT // addi 5,5,16
+ P8_STXV(IN0, R0, OUTENC)
+ P8_STXV(IN0, R0, OUTDEC)
+ ADD $16, OUTENC, OUTENC
+ ADD $-16, OUTDEC, OUTDEC
BC 0x12, 0, done // bdz .Ldone
VSPLTW $3, IN0, KEY // vspltw 3,1,3
@@ -310,74 +292,16 @@ loop256:
JMP loop256 // b .Loop256
done:
- LVX (INP)(R0), IN1 // lvx 2,0,3
- VSEL OUTHEAD, IN1, OUTMASK, IN1 // vsel 2,10,2,9
- STVX IN1, (INP+R0) // stvx 2,0,3
- MOVD $0, PTR // li 6,0 set PTR to 0 (exit code 0)
- MOVW ROUNDS, 0(OUT) // stw 8,0(5)
-
-enc_key_abort:
- MOVD PTR, INP // mr 3,6 set exit code with PTR value
- MOVD INP, ret+24(FP) // Put return value into the FP
- RET // blr
-
-// func setDecryptKeyAsm(key *byte, keylen int, dec *uint32) int
-TEXT ·setDecryptKeyAsm(SB), NOSPLIT|NOFRAME, $0
- // Load the arguments inside the registers
- MOVD key+0(FP), INP
- MOVD keylen+8(FP), BITS
- MOVD dec+16(FP), OUT
-
- MOVD LR, R10 // mflr 10
- CALL ·doEncryptKeyAsm(SB)
- MOVD R10, LR // mtlr 10
-
- CMPW INP, $0 // cmpwi 3,0 exit 0 = ok
- BC 0x06, 2, dec_key_abort // bne- .Ldec_key_abort
-
- // doEncryptKeyAsm set ROUNDS (R8) with the proper value for each mode
- SLW $4, ROUNDS, CNT // slwi 7,8,4
- SUB $240, OUT, INP // subi 3,5,240
- SRW $1, ROUNDS, ROUNDS // srwi 8,8,1
- ADD R7, INP, OUT // add 5,3,7
- MOVD ROUNDS, CTR // mtctr 8
-
- // dec_key will invert the key sequence in order to be used for decrypt
-dec_key:
- MOVWZ 0(INP), TEMP // lwz 0, 0(3)
- MOVWZ 4(INP), R6 // lwz 6, 4(3)
- MOVWZ 8(INP), R7 // lwz 7, 8(3)
- MOVWZ 12(INP), R8 // lwz 8, 12(3)
- ADD $16, INP, INP // addi 3,3,16
- MOVWZ 0(OUT), R9 // lwz 9, 0(5)
- MOVWZ 4(OUT), R10 // lwz 10,4(5)
- MOVWZ 8(OUT), R11 // lwz 11,8(5)
- MOVWZ 12(OUT), R12 // lwz 12,12(5)
- MOVW TEMP, 0(OUT) // stw 0, 0(5)
- MOVW R6, 4(OUT) // stw 6, 4(5)
- MOVW R7, 8(OUT) // stw 7, 8(5)
- MOVW R8, 12(OUT) // stw 8, 12(5)
- SUB $16, OUT, OUT // subi 5,5,16
- MOVW R9, -16(INP) // stw 9, -16(3)
- MOVW R10, -12(INP) // stw 10,-12(3)
- MOVW R11, -8(INP) // stw 11,-8(3)
- MOVW R12, -4(INP) // stw 12,-4(3)
- BC 0x10, 0, dec_key // bdnz .Ldeckey
-
- XOR R3, R3, R3 // xor 3,3,3 Clean R3
-
-dec_key_abort:
- MOVD R3, ret+24(FP) // Put return value into the FP
- RET // blr
-
-// func encryptBlockAsm(dst, src *byte, enc *uint32)
+ RET
+
+// func encryptBlockAsm(nr int, xk *uint32, dst, src *byte)
TEXT ·encryptBlockAsm(SB), NOSPLIT|NOFRAME, $0
// Load the arguments inside the registers
- MOVD dst+0(FP), BLK_OUT
- MOVD src+8(FP), BLK_INP
- MOVD enc+16(FP), BLK_KEY
+ MOVD nr+0(FP), BLK_ROUNDS
+ MOVD xk+8(FP), BLK_KEY
+ MOVD dst+16(FP), BLK_OUT
+ MOVD src+24(FP), BLK_INP
- MOVWZ 240(BLK_KEY), BLK_ROUNDS // lwz 6,240(5)
MOVD $15, BLK_IDX // li 7,15
LVX (BLK_INP)(R0), ZERO // lvx 0,0,3
@@ -434,14 +358,14 @@ loop_enc:
RET // blr
-// func decryptBlockAsm(dst, src *byte, dec *uint32)
+// func decryptBlockAsm(nr int, xk *uint32, dst, src *byte)
TEXT ·decryptBlockAsm(SB), NOSPLIT|NOFRAME, $0
// Load the arguments inside the registers
- MOVD dst+0(FP), BLK_OUT
- MOVD src+8(FP), BLK_INP
- MOVD dec+16(FP), BLK_KEY
+ MOVD nr+0(FP), BLK_ROUNDS
+ MOVD xk+8(FP), BLK_KEY
+ MOVD dst+16(FP), BLK_OUT
+ MOVD src+24(FP), BLK_INP
- MOVWZ 240(BLK_KEY), BLK_ROUNDS // lwz 6,240(5)
MOVD $15, BLK_IDX // li 7,15
LVX (BLK_INP)(R0), ZERO // lvx 0,0,3
@@ -500,7 +424,7 @@ loop_dec:
// Remove defines from above so they can be defined here
#undef INP
-#undef OUT
+#undef OUTENC
#undef ROUNDS
#undef KEY
#undef TMP
@@ -569,6 +493,7 @@ loop_dec:
// for decryption which was omitted to avoid the
// complexity.
+// func cryptBlocksChain(src, dst *byte, length int, key *uint32, iv *byte, enc int, nr int)
TEXT ·cryptBlocksChain(SB), NOSPLIT|NOFRAME, $0
MOVD src+0(FP), INP
MOVD dst+8(FP), OUT
@@ -576,6 +501,7 @@ TEXT ·cryptBlocksChain(SB), NOSPLIT|NOFRAME, $0
MOVD key+24(FP), KEY
MOVD iv+32(FP), IVP
MOVD enc+40(FP), ENC
+ MOVD nr+48(FP), ROUNDS
CMPU LEN, $16 // cmpldi r5,16
BC 14, 0, LR // bltlr-
@@ -591,7 +517,6 @@ TEXT ·cryptBlocksChain(SB), NOSPLIT|NOFRAME, $0
VPERM IVEC, INPTAIL, INPPERM, IVEC // vperm v4,v4,v5,v6
NEG INP, R11 // neg r11,r3
LVSR (KEY)(R0), KEYPERM // lvsr v10,r0,r6
- MOVWZ 240(KEY), ROUNDS // lwz r9,240(r6)
LVSR (R11)(R0), V6 // lvsr v6,r0,r11
LVX (INP)(R0), INPTAIL // lvx v5,r0,r3
ADD $15, INP // addi r3,r3,15
diff --git a/src/crypto/aes/cbc_ppc64le.go b/src/crypto/aes/cbc_ppc64le.go
index fa8a430ed43..cb9ff4c8439 100644
--- a/src/crypto/aes/cbc_ppc64le.go
+++ b/src/crypto/aes/cbc_ppc64le.go
@@ -42,7 +42,7 @@ func (x *cbc) BlockSize() int { return BlockSize }
// cryptBlocksChain invokes the cipher message identifying encrypt or decrypt.
//go:noescape
-func cryptBlocksChain(src, dst *byte, length int, key *uint32, iv *byte, enc int)
+func cryptBlocksChain(src, dst *byte, length int, key *uint32, iv *byte, enc int, nr int)
func (x *cbc) CryptBlocks(dst, src []byte) {
if len(src)%BlockSize != 0 {
@@ -56,9 +56,9 @@ func (x *cbc) CryptBlocks(dst, src []byte) {
}
if len(src) > 0 {
if x.enc == cbcEncrypt {
- cryptBlocksChain(&src[0], &dst[0], len(src), &x.b.enc[0], &x.iv[0], x.enc)
+ cryptBlocksChain(&src[0], &dst[0], len(src), &x.b.enc[0], &x.iv[0], x.enc, len(x.b.enc)/4-1)
} else {
- cryptBlocksChain(&src[0], &dst[0], len(src), &x.b.dec[0], &x.iv[0], x.enc)
+ cryptBlocksChain(&src[0], &dst[0], len(src), &x.b.dec[0], &x.iv[0], x.enc, len(x.b.dec)/4-1)
}
}
}
diff --git a/src/crypto/aes/cbc_s390x.go b/src/crypto/aes/cbc_s390x.go
index 28a6b1d5461..766247abffb 100644
--- a/src/crypto/aes/cbc_s390x.go
+++ b/src/crypto/aes/cbc_s390x.go
@@ -39,6 +39,7 @@ func (x *cbc) BlockSize() int { return BlockSize }
// cryptBlocksChain invokes the cipher message with chaining (KMC) instruction
// with the given function code. The length must be a multiple of BlockSize (16).
+//
//go:noescape
func cryptBlocksChain(c code, iv, key, dst, src *byte, length int)
diff --git a/src/crypto/aes/cipher_ppc64le.go b/src/crypto/aes/cipher_ppc64le.go
index b788ea7d475..18615148fd5 100644
--- a/src/crypto/aes/cipher_ppc64le.go
+++ b/src/crypto/aes/cipher_ppc64le.go
@@ -12,37 +12,36 @@ import (
// defined in asm_ppc64le.s
//go:noescape
-func setEncryptKeyAsm(key *byte, keylen int, enc *uint32) int
+func expandKeyAsm(nr int, key *byte, enc *uint32, dec *uint32)
//go:noescape
-func setDecryptKeyAsm(key *byte, keylen int, dec *uint32) int
+func encryptBlockAsm(nr int, xk *uint32, dst, src *byte)
//go:noescape
-func doEncryptKeyAsm(key *byte, keylen int, dec *uint32) int
-
-//go:noescape
-func encryptBlockAsm(dst, src *byte, enc *uint32)
-
-//go:noescape
-func decryptBlockAsm(dst, src *byte, dec *uint32)
+func decryptBlockAsm(nr int, xk *uint32, dst, src *byte)
type aesCipherAsm struct {
aesCipher
}
func newCipher(key []byte) (cipher.Block, error) {
- n := 64 // size is fixed for all and round value is stored inside it too
+ n := len(key) + 28
c := aesCipherAsm{aesCipher{make([]uint32, n), make([]uint32, n)}}
k := len(key)
- ret := 0
- ret += setEncryptKeyAsm(&key[0], k*8, &c.enc[0])
- ret += setDecryptKeyAsm(&key[0], k*8, &c.dec[0])
-
- if ret > 0 {
+ var rounds int
+ switch len(key) {
+ case 128 / 8:
+ rounds = 10
+ case 192 / 8:
+ rounds = 12
+ case 256 / 8:
+ rounds = 14
+ default:
return nil, KeySizeError(k)
}
+ expandKeyAsm(rounds, &key[0], &c.enc[0], &c.dec[0])
return &c, nil
}
@@ -58,7 +57,7 @@ func (c *aesCipherAsm) Encrypt(dst, src []byte) {
if subtle.InexactOverlap(dst[:BlockSize], src[:BlockSize]) {
panic("crypto/aes: invalid buffer overlap")
}
- encryptBlockAsm(&dst[0], &src[0], &c.enc[0])
+ encryptBlockAsm(len(c.enc)/4-1, &c.enc[0], &dst[0], &src[0])
}
func (c *aesCipherAsm) Decrypt(dst, src []byte) {
@@ -71,12 +70,18 @@ func (c *aesCipherAsm) Decrypt(dst, src []byte) {
if subtle.InexactOverlap(dst[:BlockSize], src[:BlockSize]) {
panic("crypto/aes: invalid buffer overlap")
}
- decryptBlockAsm(&dst[0], &src[0], &c.dec[0])
+ decryptBlockAsm(len(c.dec)/4-1, &c.dec[0], &dst[0], &src[0])
}
// expandKey is used by BenchmarkExpand to ensure that the asm implementation
// of key expansion is used for the benchmark when it is available.
func expandKey(key []byte, enc, dec []uint32) {
- setEncryptKeyAsm(&key[0], len(key)*8, &enc[0])
- setDecryptKeyAsm(&key[0], len(key)*8, &dec[0])
+ rounds := 10 // rounds needed for AES128
+ switch len(key) {
+ case 192 / 8:
+ rounds = 12
+ case 256 / 8:
+ rounds = 14
+ }
+ expandKeyAsm(rounds, &key[0], &enc[0], &dec[0])
}
diff --git a/src/crypto/aes/cipher_s390x.go b/src/crypto/aes/cipher_s390x.go
index 65b6b2fc1b5..e3578511431 100644
--- a/src/crypto/aes/cipher_s390x.go
+++ b/src/crypto/aes/cipher_s390x.go
@@ -28,6 +28,7 @@ type aesCipherAsm struct {
// cryptBlocks invokes the cipher message (KM) instruction with
// the given function code. This is equivalent to AES in ECB
// mode. The length must be a multiple of BlockSize (16).
+//
//go:noescape
func cryptBlocks(c code, key, dst, src *byte, length int)
diff --git a/src/crypto/aes/ctr_s390x.go b/src/crypto/aes/ctr_s390x.go
index bfa8cbba7f3..f5c33d52992 100644
--- a/src/crypto/aes/ctr_s390x.go
+++ b/src/crypto/aes/ctr_s390x.go
@@ -17,6 +17,7 @@ var _ ctrAble = (*aesCipherAsm)(nil)
// dst. If a and b are not the same length then the number of bytes processed
// will be equal to the length of shorter of the two. Returns the number
// of bytes processed.
+//
//go:noescape
func xorBytes(dst, a, b []byte) int
diff --git a/src/crypto/aes/gcm_s390x.go b/src/crypto/aes/gcm_s390x.go
index c58aa2cda8f..98d530aeda1 100644
--- a/src/crypto/aes/gcm_s390x.go
+++ b/src/crypto/aes/gcm_s390x.go
@@ -100,6 +100,7 @@ func sliceForAppend(in []byte, n int) (head, tail []byte) {
// ghash uses the GHASH algorithm to hash data with the given key. The initial
// hash value is given by hash which will be updated with the new hash value.
// The length of data must be a multiple of 16-bytes.
+//
//go:noescape
func ghash(key *gcmHashKey, hash *[16]byte, data []byte)
@@ -127,6 +128,7 @@ func (g *gcmAsm) paddedGHASH(hash *[16]byte, data []byte) {
// The lengths of both dst and buf must be greater than or equal to the length
// of src. buf may be partially or completely overwritten during the execution
// of the function.
+//
//go:noescape
func cryptBlocksGCM(fn code, key, dst, src, buf []byte, cnt *gcmCount)
@@ -295,6 +297,7 @@ const (
// will be calculated and written to tag. cnt should contain the current
// counter state and will be overwritten with the updated counter state.
// TODO(mundaym): could pass in hash subkey
+//
//go:noescape
func kmaGCM(fn code, key, dst, src, aad []byte, tag *[16]byte, cnt *gcmCount)
diff --git a/src/crypto/cipher/gcm.go b/src/crypto/cipher/gcm.go
index 47078ce594a..5b14c0a7e2d 100644
--- a/src/crypto/cipher/gcm.go
+++ b/src/crypto/cipher/gcm.go
@@ -56,10 +56,11 @@ type gcmAble interface {
// gcmFieldElement represents a value in GF(2¹²⁸). In order to reflect the GCM
// standard and make binary.BigEndian suitable for marshaling these values, the
// bits are stored in big endian order. For example:
-// the coefficient of x⁰ can be obtained by v.low >> 63.
-// the coefficient of x⁶³ can be obtained by v.low & 1.
-// the coefficient of x⁶⁴ can be obtained by v.high >> 63.
-// the coefficient of x¹²⁷ can be obtained by v.high & 1.
+//
+// the coefficient of x⁰ can be obtained by v.low >> 63.
+// the coefficient of x⁶³ can be obtained by v.low & 1.
+// the coefficient of x⁶⁴ can be obtained by v.high >> 63.
+// the coefficient of x¹²⁷ can be obtained by v.high & 1.
type gcmFieldElement struct {
low, high uint64
}
diff --git a/src/crypto/crypto.go b/src/crypto/crypto.go
index fe1c0690bc2..10a1cd8403a 100644
--- a/src/crypto/crypto.go
+++ b/src/crypto/crypto.go
@@ -154,9 +154,9 @@ func RegisterHash(h Hash, f func() hash.Hash) {
// Although this type is an empty interface for backwards compatibility reasons,
// all public key types in the standard library implement the following interface
//
-// interface{
-// Equal(x crypto.PublicKey) bool
-// }
+// interface{
+// Equal(x crypto.PublicKey) bool
+// }
//
// which can be used for increased type safety within applications.
type PublicKey any
@@ -166,10 +166,10 @@ type PublicKey any
// Although this type is an empty interface for backwards compatibility reasons,
// all private key types in the standard library implement the following interface
//
-// interface{
-// Public() crypto.PublicKey
-// Equal(x crypto.PrivateKey) bool
-// }
+// interface{
+// Public() crypto.PublicKey
+// Equal(x crypto.PrivateKey) bool
+// }
//
// as well as purpose-specific interfaces such as Signer and Decrypter, which
// can be used for increased type safety within applications.
diff --git a/src/crypto/ecdsa/ecdsa_s390x.go b/src/crypto/ecdsa/ecdsa_s390x.go
index 1480d1bf6f6..bd9257977c7 100644
--- a/src/crypto/ecdsa/ecdsa_s390x.go
+++ b/src/crypto/ecdsa/ecdsa_s390x.go
@@ -18,6 +18,7 @@ import (
// The return value corresponds to the condition code set by the
// instruction. Interrupted invocations are handled by the
// function.
+//
//go:noescape
func kdsa(fc uint64, params *[4096]byte) (errn uint64)
diff --git a/src/crypto/ed25519/internal/edwards25519/doc.go b/src/crypto/ed25519/internal/edwards25519/doc.go
index ff31cd23daa..8cba6febfe1 100644
--- a/src/crypto/ed25519/internal/edwards25519/doc.go
+++ b/src/crypto/ed25519/internal/edwards25519/doc.go
@@ -4,7 +4,7 @@
// Package edwards25519 implements group logic for the twisted Edwards curve
//
-// -x^2 + y^2 = 1 + -(121665/121666)*x^2*y^2
+// -x^2 + y^2 = 1 + -(121665/121666)*x^2*y^2
//
// This is better known as the Edwards curve equivalent to Curve25519, and is
// the curve used by the Ed25519 signature scheme.
diff --git a/src/crypto/ed25519/internal/edwards25519/field/fe_alias_test.go b/src/crypto/ed25519/internal/edwards25519/field/fe_alias_test.go
index abe9986b885..bf1efdceade 100644
--- a/src/crypto/ed25519/internal/edwards25519/field/fe_alias_test.go
+++ b/src/crypto/ed25519/internal/edwards25519/field/fe_alias_test.go
@@ -77,11 +77,11 @@ func checkAliasingTwoArgs(f func(v, x, y *Element) *Element) func(v, x, y Elemen
// TestAliasing checks that receivers and arguments can alias each other without
// leading to incorrect results. That is, it ensures that it's safe to write
//
-// v.Invert(v)
+// v.Invert(v)
//
// or
//
-// v.Add(v, v)
+// v.Add(v, v)
//
// without any of the inputs getting clobbered by the output being written.
func TestAliasing(t *testing.T) {
diff --git a/src/crypto/ed25519/internal/edwards25519/field/fe_amd64.go b/src/crypto/ed25519/internal/edwards25519/field/fe_amd64.go
index 363020bd6b8..70c541692c3 100644
--- a/src/crypto/ed25519/internal/edwards25519/field/fe_amd64.go
+++ b/src/crypto/ed25519/internal/edwards25519/field/fe_amd64.go
@@ -5,9 +5,11 @@
package field
// feMul sets out = a * b. It works like feMulGeneric.
+//
//go:noescape
func feMul(out *Element, a *Element, b *Element)
// feSquare sets out = a * a. It works like feSquareGeneric.
+//
//go:noescape
func feSquare(out *Element, a *Element)
diff --git a/src/crypto/ed25519/internal/edwards25519/scalar.go b/src/crypto/ed25519/internal/edwards25519/scalar.go
index 3df2fb936f2..4530bc3ce26 100644
--- a/src/crypto/ed25519/internal/edwards25519/scalar.go
+++ b/src/crypto/ed25519/internal/edwards25519/scalar.go
@@ -12,7 +12,7 @@ import (
// A Scalar is an integer modulo
//
-// l = 2^252 + 27742317777372353535851937790883648493
+// l = 2^252 + 27742317777372353535851937790883648493
//
// which is the prime order of the edwards25519 group.
//
@@ -183,13 +183,15 @@ func load4(in []byte) int64 {
}
// Input:
-// a[0]+256*a[1]+...+256^31*a[31] = a
-// b[0]+256*b[1]+...+256^31*b[31] = b
-// c[0]+256*c[1]+...+256^31*c[31] = c
+//
+// a[0]+256*a[1]+...+256^31*a[31] = a
+// b[0]+256*b[1]+...+256^31*b[31] = b
+// c[0]+256*c[1]+...+256^31*c[31] = c
//
// Output:
-// s[0]+256*s[1]+...+256^31*s[31] = (ab+c) mod l
-// where l = 2^252 + 27742317777372353535851937790883648493.
+//
+// s[0]+256*s[1]+...+256^31*s[31] = (ab+c) mod l
+// where l = 2^252 + 27742317777372353535851937790883648493.
func scMulAdd(s, a, b, c *[32]byte) {
a0 := 2097151 & load3(a[:])
a1 := 2097151 & (load4(a[2:]) >> 5)
@@ -616,11 +618,13 @@ func scMulAdd(s, a, b, c *[32]byte) {
}
// Input:
-// s[0]+256*s[1]+...+256^63*s[63] = s
+//
+// s[0]+256*s[1]+...+256^63*s[63] = s
//
// Output:
-// s[0]+256*s[1]+...+256^31*s[31] = s mod l
-// where l = 2^252 + 27742317777372353535851937790883648493.
+//
+// s[0]+256*s[1]+...+256^31*s[31] = s mod l
+// where l = 2^252 + 27742317777372353535851937790883648493.
func scReduce(out *[32]byte, s *[64]byte) {
s0 := 2097151 & load3(s[:])
s1 := 2097151 & (load4(s[2:]) >> 5)
diff --git a/src/crypto/elliptic/elliptic_test.go b/src/crypto/elliptic/elliptic_test.go
index 5481929db15..eb5f0546c45 100644
--- a/src/crypto/elliptic/elliptic_test.go
+++ b/src/crypto/elliptic/elliptic_test.go
@@ -364,8 +364,8 @@ func BenchmarkMarshalUnmarshal(b *testing.B) {
b.Run("Compressed", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
- buf := Marshal(curve, x, y)
- xx, yy := Unmarshal(curve, buf)
+ buf := MarshalCompressed(curve, x, y)
+ xx, yy := UnmarshalCompressed(curve, buf)
if xx.Cmp(x) != 0 || yy.Cmp(y) != 0 {
b.Error("Unmarshal output different from Marshal input")
}
diff --git a/src/crypto/elliptic/fuzz_test.go b/src/crypto/elliptic/fuzz_test.go
deleted file mode 100644
index 2b5ddae1d9e..00000000000
--- a/src/crypto/elliptic/fuzz_test.go
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2018 The Go 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:build amd64 || arm64 || ppc64le
-
-package elliptic
-
-import (
- "crypto/rand"
- "testing"
- "time"
-)
-
-func TestFuzz(t *testing.T) {
- p256 := P256()
- p256Generic := p256.Params()
-
- var scalar1 [32]byte
- var scalar2 [32]byte
- var timeout *time.Timer
-
- if testing.Short() {
- timeout = time.NewTimer(10 * time.Millisecond)
- } else {
- timeout = time.NewTimer(2 * time.Second)
- }
-
- for {
- select {
- case <-timeout.C:
- return
- default:
- }
-
- rand.Read(scalar1[:])
- rand.Read(scalar2[:])
-
- x, y := p256.ScalarBaseMult(scalar1[:])
- x2, y2 := p256Generic.ScalarBaseMult(scalar1[:])
-
- xx, yy := p256.ScalarMult(x, y, scalar2[:])
- xx2, yy2 := p256Generic.ScalarMult(x2, y2, scalar2[:])
-
- if x.Cmp(x2) != 0 || y.Cmp(y2) != 0 {
- t.Fatalf("ScalarBaseMult does not match reference result with scalar: %x, please report this error to security@golang.org", scalar1)
- }
-
- if xx.Cmp(xx2) != 0 || yy.Cmp(yy2) != 0 {
- t.Fatalf("ScalarMult does not match reference result with scalars: %x and %x, please report this error to security@golang.org", scalar1, scalar2)
- }
- }
-}
diff --git a/src/crypto/elliptic/internal/fiat/p224_fiat64.go b/src/crypto/elliptic/internal/fiat/p224_fiat64.go
index 588e9ea6204..9337bfefef0 100644
--- a/src/crypto/elliptic/internal/fiat/p224_fiat64.go
+++ b/src/crypto/elliptic/internal/fiat/p224_fiat64.go
@@ -56,14 +56,18 @@ type p224NonMontgomeryDomainFieldElement [4]uint64
// p224CmovznzU64 is a single-word conditional move.
//
// Postconditions:
-// out1 = (if arg1 = 0 then arg2 else arg3)
+//
+// out1 = (if arg1 = 0 then arg2 else arg3)
//
// Input Bounds:
-// arg1: [0x0 ~> 0x1]
-// arg2: [0x0 ~> 0xffffffffffffffff]
-// arg3: [0x0 ~> 0xffffffffffffffff]
+//
+// arg1: [0x0 ~> 0x1]
+// arg2: [0x0 ~> 0xffffffffffffffff]
+// arg3: [0x0 ~> 0xffffffffffffffff]
+//
// Output Bounds:
-// out1: [0x0 ~> 0xffffffffffffffff]
+//
+// out1: [0x0 ~> 0xffffffffffffffff]
func p224CmovznzU64(out1 *uint64, arg1 p224Uint1, arg2 uint64, arg3 uint64) {
x1 := (uint64(arg1) * 0xffffffffffffffff)
x2 := ((x1 & arg3) | ((^x1) & arg2))
@@ -73,11 +77,14 @@ func p224CmovznzU64(out1 *uint64, arg1 p224Uint1, arg2 uint64, arg3 uint64) {
// p224Mul multiplies two field elements in the Montgomery domain.
//
// Preconditions:
-// 0 ≤ eval arg1 < m
-// 0 ≤ eval arg2 < m
+//
+// 0 ≤ eval arg1 < m
+// 0 ≤ eval arg2 < m
+//
// Postconditions:
-// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) * eval (from_montgomery arg2)) mod m
-// 0 ≤ eval out1 < m
+//
+// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) * eval (from_montgomery arg2)) mod m
+// 0 ≤ eval out1 < m
func p224Mul(out1 *p224MontgomeryDomainFieldElement, arg1 *p224MontgomeryDomainFieldElement, arg2 *p224MontgomeryDomainFieldElement) {
x1 := arg1[1]
x2 := arg1[2]
@@ -378,10 +385,13 @@ func p224Mul(out1 *p224MontgomeryDomainFieldElement, arg1 *p224MontgomeryDomainF
// p224Square squares a field element in the Montgomery domain.
//
// Preconditions:
-// 0 ≤ eval arg1 < m
+//
+// 0 ≤ eval arg1 < m
+//
// Postconditions:
-// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) * eval (from_montgomery arg1)) mod m
-// 0 ≤ eval out1 < m
+//
+// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) * eval (from_montgomery arg1)) mod m
+// 0 ≤ eval out1 < m
func p224Square(out1 *p224MontgomeryDomainFieldElement, arg1 *p224MontgomeryDomainFieldElement) {
x1 := arg1[1]
x2 := arg1[2]
@@ -682,11 +692,14 @@ func p224Square(out1 *p224MontgomeryDomainFieldElement, arg1 *p224MontgomeryDoma
// p224Add adds two field elements in the Montgomery domain.
//
// Preconditions:
-// 0 ≤ eval arg1 < m
-// 0 ≤ eval arg2 < m
+//
+// 0 ≤ eval arg1 < m
+// 0 ≤ eval arg2 < m
+//
// Postconditions:
-// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) + eval (from_montgomery arg2)) mod m
-// 0 ≤ eval out1 < m
+//
+// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) + eval (from_montgomery arg2)) mod m
+// 0 ≤ eval out1 < m
func p224Add(out1 *p224MontgomeryDomainFieldElement, arg1 *p224MontgomeryDomainFieldElement, arg2 *p224MontgomeryDomainFieldElement) {
var x1 uint64
var x2 uint64
@@ -731,11 +744,14 @@ func p224Add(out1 *p224MontgomeryDomainFieldElement, arg1 *p224MontgomeryDomainF
// p224Sub subtracts two field elements in the Montgomery domain.
//
// Preconditions:
-// 0 ≤ eval arg1 < m
-// 0 ≤ eval arg2 < m
+//
+// 0 ≤ eval arg1 < m
+// 0 ≤ eval arg2 < m
+//
// Postconditions:
-// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) - eval (from_montgomery arg2)) mod m
-// 0 ≤ eval out1 < m
+//
+// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) - eval (from_montgomery arg2)) mod m
+// 0 ≤ eval out1 < m
func p224Sub(out1 *p224MontgomeryDomainFieldElement, arg1 *p224MontgomeryDomainFieldElement, arg2 *p224MontgomeryDomainFieldElement) {
var x1 uint64
var x2 uint64
@@ -771,8 +787,9 @@ func p224Sub(out1 *p224MontgomeryDomainFieldElement, arg1 *p224MontgomeryDomainF
// p224SetOne returns the field element one in the Montgomery domain.
//
// Postconditions:
-// eval (from_montgomery out1) mod m = 1 mod m
-// 0 ≤ eval out1 < m
+//
+// eval (from_montgomery out1) mod m = 1 mod m
+// 0 ≤ eval out1 < m
func p224SetOne(out1 *p224MontgomeryDomainFieldElement) {
out1[0] = 0xffffffff00000000
out1[1] = 0xffffffffffffffff
@@ -783,10 +800,13 @@ func p224SetOne(out1 *p224MontgomeryDomainFieldElement) {
// p224FromMontgomery translates a field element out of the Montgomery domain.
//
// Preconditions:
-// 0 ≤ eval arg1 < m
+//
+// 0 ≤ eval arg1 < m
+//
// Postconditions:
-// eval out1 mod m = (eval arg1 * ((2^64)⁻¹ mod m)^4) mod m
-// 0 ≤ eval out1 < m
+//
+// eval out1 mod m = (eval arg1 * ((2^64)⁻¹ mod m)^4) mod m
+// 0 ≤ eval out1 < m
func p224FromMontgomery(out1 *p224NonMontgomeryDomainFieldElement, arg1 *p224MontgomeryDomainFieldElement) {
x1 := arg1[0]
var x2 uint64
@@ -960,10 +980,13 @@ func p224FromMontgomery(out1 *p224NonMontgomeryDomainFieldElement, arg1 *p224Mon
// p224ToMontgomery translates a field element into the Montgomery domain.
//
// Preconditions:
-// 0 ≤ eval arg1 < m
+//
+// 0 ≤ eval arg1 < m
+//
// Postconditions:
-// eval (from_montgomery out1) mod m = eval arg1 mod m
-// 0 ≤ eval out1 < m
+//
+// eval (from_montgomery out1) mod m = eval arg1 mod m
+// 0 ≤ eval out1 < m
func p224ToMontgomery(out1 *p224MontgomeryDomainFieldElement, arg1 *p224NonMontgomeryDomainFieldElement) {
x1 := arg1[1]
x2 := arg1[2]
@@ -1233,14 +1256,18 @@ func p224ToMontgomery(out1 *p224MontgomeryDomainFieldElement, arg1 *p224NonMontg
// p224Selectznz is a multi-limb conditional select.
//
// Postconditions:
-// eval out1 = (if arg1 = 0 then eval arg2 else eval arg3)
+//
+// eval out1 = (if arg1 = 0 then eval arg2 else eval arg3)
//
// Input Bounds:
-// arg1: [0x0 ~> 0x1]
-// arg2: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]]
-// arg3: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]]
+//
+// arg1: [0x0 ~> 0x1]
+// arg2: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]]
+// arg3: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]]
+//
// Output Bounds:
-// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]]
+//
+// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]]
func p224Selectznz(out1 *[4]uint64, arg1 p224Uint1, arg2 *[4]uint64, arg3 *[4]uint64) {
var x1 uint64
p224CmovznzU64(&x1, arg1, arg2[0], arg3[0])
@@ -1259,14 +1286,20 @@ func p224Selectznz(out1 *[4]uint64, arg1 p224Uint1, arg2 *[4]uint64, arg3 *[4]ui
// p224ToBytes serializes a field element NOT in the Montgomery domain to bytes in little-endian order.
//
// Preconditions:
-// 0 ≤ eval arg1 < m
+//
+// 0 ≤ eval arg1 < m
+//
// Postconditions:
-// out1 = map (λ x, ⌊((eval arg1 mod m) mod 2^(8 * (x + 1))) / 2^(8 * x)⌋) [0..27]
+//
+// out1 = map (λ x, ⌊((eval arg1 mod m) mod 2^(8 * (x + 1))) / 2^(8 * x)⌋) [0..27]
//
// Input Bounds:
-// arg1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffff]]
+//
+// arg1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffff]]
+//
// Output Bounds:
-// out1: [[0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff]]
+//
+// out1: [[0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff]]
func p224ToBytes(out1 *[28]uint8, arg1 *[4]uint64) {
x1 := arg1[3]
x2 := arg1[2]
@@ -1353,15 +1386,21 @@ func p224ToBytes(out1 *[28]uint8, arg1 *[4]uint64) {
// p224FromBytes deserializes a field element NOT in the Montgomery domain from bytes in little-endian order.
//
// Preconditions:
-// 0 ≤ bytes_eval arg1 < m
+//
+// 0 ≤ bytes_eval arg1 < m
+//
// Postconditions:
-// eval out1 mod m = bytes_eval arg1 mod m
-// 0 ≤ eval out1 < m
+//
+// eval out1 mod m = bytes_eval arg1 mod m
+// 0 ≤ eval out1 < m
//
// Input Bounds:
-// arg1: [[0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff]]
+//
+// arg1: [[0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff]]
+//
// Output Bounds:
-// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffff]]
+//
+// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffff]]
func p224FromBytes(out1 *[4]uint64, arg1 *[28]uint8) {
x1 := (uint64(arg1[27]) << 24)
x2 := (uint64(arg1[26]) << 16)
diff --git a/src/crypto/elliptic/internal/fiat/p384_fiat64.go b/src/crypto/elliptic/internal/fiat/p384_fiat64.go
index dc48cd38fb4..979eadd2df3 100644
--- a/src/crypto/elliptic/internal/fiat/p384_fiat64.go
+++ b/src/crypto/elliptic/internal/fiat/p384_fiat64.go
@@ -56,14 +56,18 @@ type p384NonMontgomeryDomainFieldElement [6]uint64
// p384CmovznzU64 is a single-word conditional move.
//
// Postconditions:
-// out1 = (if arg1 = 0 then arg2 else arg3)
+//
+// out1 = (if arg1 = 0 then arg2 else arg3)
//
// Input Bounds:
-// arg1: [0x0 ~> 0x1]
-// arg2: [0x0 ~> 0xffffffffffffffff]
-// arg3: [0x0 ~> 0xffffffffffffffff]
+//
+// arg1: [0x0 ~> 0x1]
+// arg2: [0x0 ~> 0xffffffffffffffff]
+// arg3: [0x0 ~> 0xffffffffffffffff]
+//
// Output Bounds:
-// out1: [0x0 ~> 0xffffffffffffffff]
+//
+// out1: [0x0 ~> 0xffffffffffffffff]
func p384CmovznzU64(out1 *uint64, arg1 p384Uint1, arg2 uint64, arg3 uint64) {
x1 := (uint64(arg1) * 0xffffffffffffffff)
x2 := ((x1 & arg3) | ((^x1) & arg2))
@@ -73,11 +77,14 @@ func p384CmovznzU64(out1 *uint64, arg1 p384Uint1, arg2 uint64, arg3 uint64) {
// p384Mul multiplies two field elements in the Montgomery domain.
//
// Preconditions:
-// 0 ≤ eval arg1 < m
-// 0 ≤ eval arg2 < m
+//
+// 0 ≤ eval arg1 < m
+// 0 ≤ eval arg2 < m
+//
// Postconditions:
-// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) * eval (from_montgomery arg2)) mod m
-// 0 ≤ eval out1 < m
+//
+// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) * eval (from_montgomery arg2)) mod m
+// 0 ≤ eval out1 < m
func p384Mul(out1 *p384MontgomeryDomainFieldElement, arg1 *p384MontgomeryDomainFieldElement, arg2 *p384MontgomeryDomainFieldElement) {
x1 := arg1[1]
x2 := arg1[2]
@@ -778,10 +785,13 @@ func p384Mul(out1 *p384MontgomeryDomainFieldElement, arg1 *p384MontgomeryDomainF
// p384Square squares a field element in the Montgomery domain.
//
// Preconditions:
-// 0 ≤ eval arg1 < m
+//
+// 0 ≤ eval arg1 < m
+//
// Postconditions:
-// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) * eval (from_montgomery arg1)) mod m
-// 0 ≤ eval out1 < m
+//
+// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) * eval (from_montgomery arg1)) mod m
+// 0 ≤ eval out1 < m
func p384Square(out1 *p384MontgomeryDomainFieldElement, arg1 *p384MontgomeryDomainFieldElement) {
x1 := arg1[1]
x2 := arg1[2]
@@ -1482,11 +1492,14 @@ func p384Square(out1 *p384MontgomeryDomainFieldElement, arg1 *p384MontgomeryDoma
// p384Add adds two field elements in the Montgomery domain.
//
// Preconditions:
-// 0 ≤ eval arg1 < m
-// 0 ≤ eval arg2 < m
+//
+// 0 ≤ eval arg1 < m
+// 0 ≤ eval arg2 < m
+//
// Postconditions:
-// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) + eval (from_montgomery arg2)) mod m
-// 0 ≤ eval out1 < m
+//
+// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) + eval (from_montgomery arg2)) mod m
+// 0 ≤ eval out1 < m
func p384Add(out1 *p384MontgomeryDomainFieldElement, arg1 *p384MontgomeryDomainFieldElement, arg2 *p384MontgomeryDomainFieldElement) {
var x1 uint64
var x2 uint64
@@ -1549,11 +1562,14 @@ func p384Add(out1 *p384MontgomeryDomainFieldElement, arg1 *p384MontgomeryDomainF
// p384Sub subtracts two field elements in the Montgomery domain.
//
// Preconditions:
-// 0 ≤ eval arg1 < m
-// 0 ≤ eval arg2 < m
+//
+// 0 ≤ eval arg1 < m
+// 0 ≤ eval arg2 < m
+//
// Postconditions:
-// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) - eval (from_montgomery arg2)) mod m
-// 0 ≤ eval out1 < m
+//
+// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) - eval (from_montgomery arg2)) mod m
+// 0 ≤ eval out1 < m
func p384Sub(out1 *p384MontgomeryDomainFieldElement, arg1 *p384MontgomeryDomainFieldElement, arg2 *p384MontgomeryDomainFieldElement) {
var x1 uint64
var x2 uint64
@@ -1603,8 +1619,9 @@ func p384Sub(out1 *p384MontgomeryDomainFieldElement, arg1 *p384MontgomeryDomainF
// p384SetOne returns the field element one in the Montgomery domain.
//
// Postconditions:
-// eval (from_montgomery out1) mod m = 1 mod m
-// 0 ≤ eval out1 < m
+//
+// eval (from_montgomery out1) mod m = 1 mod m
+// 0 ≤ eval out1 < m
func p384SetOne(out1 *p384MontgomeryDomainFieldElement) {
out1[0] = 0xffffffff00000001
out1[1] = 0xffffffff
@@ -1617,10 +1634,13 @@ func p384SetOne(out1 *p384MontgomeryDomainFieldElement) {
// p384FromMontgomery translates a field element out of the Montgomery domain.
//
// Preconditions:
-// 0 ≤ eval arg1 < m
+//
+// 0 ≤ eval arg1 < m
+//
// Postconditions:
-// eval out1 mod m = (eval arg1 * ((2^64)⁻¹ mod m)^6) mod m
-// 0 ≤ eval out1 < m
+//
+// eval out1 mod m = (eval arg1 * ((2^64)⁻¹ mod m)^6) mod m
+// 0 ≤ eval out1 < m
func p384FromMontgomery(out1 *p384NonMontgomeryDomainFieldElement, arg1 *p384MontgomeryDomainFieldElement) {
x1 := arg1[0]
var x2 uint64
@@ -2086,10 +2106,13 @@ func p384FromMontgomery(out1 *p384NonMontgomeryDomainFieldElement, arg1 *p384Mon
// p384ToMontgomery translates a field element into the Montgomery domain.
//
// Preconditions:
-// 0 ≤ eval arg1 < m
+//
+// 0 ≤ eval arg1 < m
+//
// Postconditions:
-// eval (from_montgomery out1) mod m = eval arg1 mod m
-// 0 ≤ eval out1 < m
+//
+// eval (from_montgomery out1) mod m = eval arg1 mod m
+// 0 ≤ eval out1 < m
func p384ToMontgomery(out1 *p384MontgomeryDomainFieldElement, arg1 *p384NonMontgomeryDomainFieldElement) {
x1 := arg1[1]
x2 := arg1[2]
@@ -2704,14 +2727,18 @@ func p384ToMontgomery(out1 *p384MontgomeryDomainFieldElement, arg1 *p384NonMontg
// p384Selectznz is a multi-limb conditional select.
//
// Postconditions:
-// eval out1 = (if arg1 = 0 then eval arg2 else eval arg3)
+//
+// eval out1 = (if arg1 = 0 then eval arg2 else eval arg3)
//
// Input Bounds:
-// arg1: [0x0 ~> 0x1]
-// arg2: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]]
-// arg3: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]]
+//
+// arg1: [0x0 ~> 0x1]
+// arg2: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]]
+// arg3: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]]
+//
// Output Bounds:
-// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]]
+//
+// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]]
func p384Selectznz(out1 *[6]uint64, arg1 p384Uint1, arg2 *[6]uint64, arg3 *[6]uint64) {
var x1 uint64
p384CmovznzU64(&x1, arg1, arg2[0], arg3[0])
@@ -2736,14 +2763,20 @@ func p384Selectznz(out1 *[6]uint64, arg1 p384Uint1, arg2 *[6]uint64, arg3 *[6]ui
// p384ToBytes serializes a field element NOT in the Montgomery domain to bytes in little-endian order.
//
// Preconditions:
-// 0 ≤ eval arg1 < m
+//
+// 0 ≤ eval arg1 < m
+//
// Postconditions:
-// out1 = map (λ x, ⌊((eval arg1 mod m) mod 2^(8 * (x + 1))) / 2^(8 * x)⌋) [0..47]
+//
+// out1 = map (λ x, ⌊((eval arg1 mod m) mod 2^(8 * (x + 1))) / 2^(8 * x)⌋) [0..47]
//
// Input Bounds:
-// arg1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]]
+//
+// arg1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]]
+//
// Output Bounds:
-// out1: [[0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff]]
+//
+// out1: [[0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff]]
func p384ToBytes(out1 *[48]uint8, arg1 *[6]uint64) {
x1 := arg1[5]
x2 := arg1[4]
@@ -2888,15 +2921,21 @@ func p384ToBytes(out1 *[48]uint8, arg1 *[6]uint64) {
// p384FromBytes deserializes a field element NOT in the Montgomery domain from bytes in little-endian order.
//
// Preconditions:
-// 0 ≤ bytes_eval arg1 < m
+//
+// 0 ≤ bytes_eval arg1 < m
+//
// Postconditions:
-// eval out1 mod m = bytes_eval arg1 mod m
-// 0 ≤ eval out1 < m
+//
+// eval out1 mod m = bytes_eval arg1 mod m
+// 0 ≤ eval out1 < m
//
// Input Bounds:
-// arg1: [[0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff]]
+//
+// arg1: [[0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff]]
+//
// Output Bounds:
-// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]]
+//
+// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]]
func p384FromBytes(out1 *[6]uint64, arg1 *[48]uint8) {
x1 := (uint64(arg1[47]) << 56)
x2 := (uint64(arg1[46]) << 48)
diff --git a/src/crypto/elliptic/internal/fiat/p521_fiat64.go b/src/crypto/elliptic/internal/fiat/p521_fiat64.go
index ea92c948fd5..87a359e88ed 100644
--- a/src/crypto/elliptic/internal/fiat/p521_fiat64.go
+++ b/src/crypto/elliptic/internal/fiat/p521_fiat64.go
@@ -56,14 +56,18 @@ type p521NonMontgomeryDomainFieldElement [9]uint64
// p521CmovznzU64 is a single-word conditional move.
//
// Postconditions:
-// out1 = (if arg1 = 0 then arg2 else arg3)
+//
+// out1 = (if arg1 = 0 then arg2 else arg3)
//
// Input Bounds:
-// arg1: [0x0 ~> 0x1]
-// arg2: [0x0 ~> 0xffffffffffffffff]
-// arg3: [0x0 ~> 0xffffffffffffffff]
+//
+// arg1: [0x0 ~> 0x1]
+// arg2: [0x0 ~> 0xffffffffffffffff]
+// arg3: [0x0 ~> 0xffffffffffffffff]
+//
// Output Bounds:
-// out1: [0x0 ~> 0xffffffffffffffff]
+//
+// out1: [0x0 ~> 0xffffffffffffffff]
func p521CmovznzU64(out1 *uint64, arg1 p521Uint1, arg2 uint64, arg3 uint64) {
x1 := (uint64(arg1) * 0xffffffffffffffff)
x2 := ((x1 & arg3) | ((^x1) & arg2))
@@ -73,11 +77,14 @@ func p521CmovznzU64(out1 *uint64, arg1 p521Uint1, arg2 uint64, arg3 uint64) {
// p521Mul multiplies two field elements in the Montgomery domain.
//
// Preconditions:
-// 0 ≤ eval arg1 < m
-// 0 ≤ eval arg2 < m
+//
+// 0 ≤ eval arg1 < m
+// 0 ≤ eval arg2 < m
+//
// Postconditions:
-// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) * eval (from_montgomery arg2)) mod m
-// 0 ≤ eval out1 < m
+//
+// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) * eval (from_montgomery arg2)) mod m
+// 0 ≤ eval out1 < m
func p521Mul(out1 *p521MontgomeryDomainFieldElement, arg1 *p521MontgomeryDomainFieldElement, arg2 *p521MontgomeryDomainFieldElement) {
x1 := arg1[1]
x2 := arg1[2]
@@ -1594,10 +1601,13 @@ func p521Mul(out1 *p521MontgomeryDomainFieldElement, arg1 *p521MontgomeryDomainF
// p521Square squares a field element in the Montgomery domain.
//
// Preconditions:
-// 0 ≤ eval arg1 < m
+//
+// 0 ≤ eval arg1 < m
+//
// Postconditions:
-// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) * eval (from_montgomery arg1)) mod m
-// 0 ≤ eval out1 < m
+//
+// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) * eval (from_montgomery arg1)) mod m
+// 0 ≤ eval out1 < m
func p521Square(out1 *p521MontgomeryDomainFieldElement, arg1 *p521MontgomeryDomainFieldElement) {
x1 := arg1[1]
x2 := arg1[2]
@@ -3114,11 +3124,14 @@ func p521Square(out1 *p521MontgomeryDomainFieldElement, arg1 *p521MontgomeryDoma
// p521Add adds two field elements in the Montgomery domain.
//
// Preconditions:
-// 0 ≤ eval arg1 < m
-// 0 ≤ eval arg2 < m
+//
+// 0 ≤ eval arg1 < m
+// 0 ≤ eval arg2 < m
+//
// Postconditions:
-// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) + eval (from_montgomery arg2)) mod m
-// 0 ≤ eval out1 < m
+//
+// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) + eval (from_montgomery arg2)) mod m
+// 0 ≤ eval out1 < m
func p521Add(out1 *p521MontgomeryDomainFieldElement, arg1 *p521MontgomeryDomainFieldElement, arg2 *p521MontgomeryDomainFieldElement) {
var x1 uint64
var x2 uint64
@@ -3208,11 +3221,14 @@ func p521Add(out1 *p521MontgomeryDomainFieldElement, arg1 *p521MontgomeryDomainF
// p521Sub subtracts two field elements in the Montgomery domain.
//
// Preconditions:
-// 0 ≤ eval arg1 < m
-// 0 ≤ eval arg2 < m
+//
+// 0 ≤ eval arg1 < m
+// 0 ≤ eval arg2 < m
+//
// Postconditions:
-// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) - eval (from_montgomery arg2)) mod m
-// 0 ≤ eval out1 < m
+//
+// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) - eval (from_montgomery arg2)) mod m
+// 0 ≤ eval out1 < m
func p521Sub(out1 *p521MontgomeryDomainFieldElement, arg1 *p521MontgomeryDomainFieldElement, arg2 *p521MontgomeryDomainFieldElement) {
var x1 uint64
var x2 uint64
@@ -3283,8 +3299,9 @@ func p521Sub(out1 *p521MontgomeryDomainFieldElement, arg1 *p521MontgomeryDomainF
// p521SetOne returns the field element one in the Montgomery domain.
//
// Postconditions:
-// eval (from_montgomery out1) mod m = 1 mod m
-// 0 ≤ eval out1 < m
+//
+// eval (from_montgomery out1) mod m = 1 mod m
+// 0 ≤ eval out1 < m
func p521SetOne(out1 *p521MontgomeryDomainFieldElement) {
out1[0] = 0x80000000000000
out1[1] = uint64(0x0)
@@ -3300,10 +3317,13 @@ func p521SetOne(out1 *p521MontgomeryDomainFieldElement) {
// p521FromMontgomery translates a field element out of the Montgomery domain.
//
// Preconditions:
-// 0 ≤ eval arg1 < m
+//
+// 0 ≤ eval arg1 < m
+//
// Postconditions:
-// eval out1 mod m = (eval arg1 * ((2^64)⁻¹ mod m)^9) mod m
-// 0 ≤ eval out1 < m
+//
+// eval out1 mod m = (eval arg1 * ((2^64)⁻¹ mod m)^9) mod m
+// 0 ≤ eval out1 < m
func p521FromMontgomery(out1 *p521NonMontgomeryDomainFieldElement, arg1 *p521MontgomeryDomainFieldElement) {
x1 := arg1[0]
var x2 uint64
@@ -4253,10 +4273,13 @@ func p521FromMontgomery(out1 *p521NonMontgomeryDomainFieldElement, arg1 *p521Mon
// p521ToMontgomery translates a field element into the Montgomery domain.
//
// Preconditions:
-// 0 ≤ eval arg1 < m
+//
+// 0 ≤ eval arg1 < m
+//
// Postconditions:
-// eval (from_montgomery out1) mod m = eval arg1 mod m
-// 0 ≤ eval out1 < m
+//
+// eval (from_montgomery out1) mod m = eval arg1 mod m
+// 0 ≤ eval out1 < m
func p521ToMontgomery(out1 *p521MontgomeryDomainFieldElement, arg1 *p521NonMontgomeryDomainFieldElement) {
var x1 uint64
var x2 uint64
@@ -5113,14 +5136,18 @@ func p521ToMontgomery(out1 *p521MontgomeryDomainFieldElement, arg1 *p521NonMontg
// p521Selectznz is a multi-limb conditional select.
//
// Postconditions:
-// eval out1 = (if arg1 = 0 then eval arg2 else eval arg3)
+//
+// eval out1 = (if arg1 = 0 then eval arg2 else eval arg3)
//
// Input Bounds:
-// arg1: [0x0 ~> 0x1]
-// arg2: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]]
-// arg3: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]]
+//
+// arg1: [0x0 ~> 0x1]
+// arg2: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]]
+// arg3: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]]
+//
// Output Bounds:
-// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]]
+//
+// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]]
func p521Selectznz(out1 *[9]uint64, arg1 p521Uint1, arg2 *[9]uint64, arg3 *[9]uint64) {
var x1 uint64
p521CmovznzU64(&x1, arg1, arg2[0], arg3[0])
@@ -5154,14 +5181,20 @@ func p521Selectznz(out1 *[9]uint64, arg1 p521Uint1, arg2 *[9]uint64, arg3 *[9]ui
// p521ToBytes serializes a field element NOT in the Montgomery domain to bytes in little-endian order.
//
// Preconditions:
-// 0 ≤ eval arg1 < m
+//
+// 0 ≤ eval arg1 < m
+//
// Postconditions:
-// out1 = map (λ x, ⌊((eval arg1 mod m) mod 2^(8 * (x + 1))) / 2^(8 * x)⌋) [0..65]
+//
+// out1 = map (λ x, ⌊((eval arg1 mod m) mod 2^(8 * (x + 1))) / 2^(8 * x)⌋) [0..65]
//
// Input Bounds:
-// arg1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0x1ff]]
+//
+// arg1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0x1ff]]
+//
// Output Bounds:
-// out1: [[0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0x1]]
+//
+// out1: [[0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0x1]]
func p521ToBytes(out1 *[66]uint8, arg1 *[9]uint64) {
x1 := arg1[8]
x2 := arg1[7]
@@ -5357,15 +5390,21 @@ func p521ToBytes(out1 *[66]uint8, arg1 *[9]uint64) {
// p521FromBytes deserializes a field element NOT in the Montgomery domain from bytes in little-endian order.
//
// Preconditions:
-// 0 ≤ bytes_eval arg1 < m
+//
+// 0 ≤ bytes_eval arg1 < m
+//
// Postconditions:
-// eval out1 mod m = bytes_eval arg1 mod m
-// 0 ≤ eval out1 < m
+//
+// eval out1 mod m = bytes_eval arg1 mod m
+// 0 ≤ eval out1 < m
//
// Input Bounds:
-// arg1: [[0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0x1]]
+//
+// arg1: [[0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0x1]]
+//
// Output Bounds:
-// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0x1ff]]
+//
+// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0x1ff]]
func p521FromBytes(out1 *[9]uint64, arg1 *[66]uint8) {
x1 := (uint64(p521Uint1(arg1[65])) << 8)
x2 := arg1[64]
diff --git a/src/crypto/elliptic/p256.go b/src/crypto/elliptic/p256.go
index fcff1d3d391..763b84283e4 100644
--- a/src/crypto/elliptic/p256.go
+++ b/src/crypto/elliptic/p256.go
@@ -122,23 +122,23 @@ var (
// The first table contains (x,y) field element pairs for 16 multiples of the
// base point, G.
//
-// Index | Index (binary) | Value
-// 0 | 0000 | 0G (all zeros, omitted)
-// 1 | 0001 | G
-// 2 | 0010 | 2**64G
-// 3 | 0011 | 2**64G + G
-// 4 | 0100 | 2**128G
-// 5 | 0101 | 2**128G + G
-// 6 | 0110 | 2**128G + 2**64G
-// 7 | 0111 | 2**128G + 2**64G + G
-// 8 | 1000 | 2**192G
-// 9 | 1001 | 2**192G + G
-// 10 | 1010 | 2**192G + 2**64G
-// 11 | 1011 | 2**192G + 2**64G + G
-// 12 | 1100 | 2**192G + 2**128G
-// 13 | 1101 | 2**192G + 2**128G + G
-// 14 | 1110 | 2**192G + 2**128G + 2**64G
-// 15 | 1111 | 2**192G + 2**128G + 2**64G + G
+// Index | Index (binary) | Value
+// 0 | 0000 | 0G (all zeros, omitted)
+// 1 | 0001 | G
+// 2 | 0010 | 2**64G
+// 3 | 0011 | 2**64G + G
+// 4 | 0100 | 2**128G
+// 5 | 0101 | 2**128G + G
+// 6 | 0110 | 2**128G + 2**64G
+// 7 | 0111 | 2**128G + 2**64G + G
+// 8 | 1000 | 2**192G
+// 9 | 1001 | 2**192G + G
+// 10 | 1010 | 2**192G + 2**64G
+// 11 | 1011 | 2**192G + 2**64G + G
+// 12 | 1100 | 2**192G + 2**128G
+// 13 | 1101 | 2**192G + 2**128G + G
+// 14 | 1110 | 2**192G + 2**128G + 2**64G
+// 15 | 1111 | 2**192G + 2**128G + 2**64G + G
//
// The second table follows the same style, but the terms are 2**32G,
// 2**96G, 2**160G, 2**224G.
@@ -212,8 +212,9 @@ var p256Precomputed = [p256Limbs * 2 * 15 * 2]uint32{
const bottom28Bits = 0xfffffff
// nonZeroToAllOnes returns:
-// 0xffffffff for 0 < x <= 2**31
-// 0 for x == 0 or x > 2**31.
+//
+// 0xffffffff for 0 < x <= 2**31
+// 0 for x == 0 or x > 2**31.
func nonZeroToAllOnes(x uint32) uint32 {
return ((x - 1) >> 31) - 1
}
@@ -563,7 +564,9 @@ func p256Square(out, in *[p256Limbs]uint32) {
// p256Mul sets out=in*in2.
//
// On entry: in[0,2,...] < 2**30, in[1,3,...] < 2**29 and
-// in2[0,2,...] < 2**30, in2[1,3,...] < 2**29.
+//
+// in2[0,2,...] < 2**30, in2[1,3,...] < 2**29.
+//
// On exit: out[0,2,...] < 2**30, out[1,3,...] < 2**29.
func p256Mul(out, in, in2 *[p256Limbs]uint32) {
var tmp [17]uint64
@@ -662,9 +665,10 @@ func p256Assign(out, in *[p256Limbs]uint32) {
// p256Invert calculates |out| = |in|^{-1}
//
// Based on Fermat's Little Theorem:
-// a^p = a (mod p)
-// a^{p-1} = 1 (mod p)
-// a^{p-2} = a^{-1} (mod p)
+//
+// a^p = a (mod p)
+// a^{p-1} = 1 (mod p)
+// a^{p-2} = a^{-1} (mod p)
func p256Invert(out, in *[p256Limbs]uint32) {
var ftmp, ftmp2 [p256Limbs]uint32
diff --git a/src/crypto/elliptic/p256_asm.go b/src/crypto/elliptic/p256_asm.go
index 8624e031a38..93adaf90565 100644
--- a/src/crypto/elliptic/p256_asm.go
+++ b/src/crypto/elliptic/p256_asm.go
@@ -53,26 +53,32 @@ func (curve p256Curve) Params() *CurveParams {
// Functions implemented in p256_asm_*64.s
// Montgomery multiplication modulo P256
+//
//go:noescape
func p256Mul(res, in1, in2 []uint64)
// Montgomery square modulo P256, repeated n times (n >= 1)
+//
//go:noescape
func p256Sqr(res, in []uint64, n int)
// Montgomery multiplication by 1
+//
//go:noescape
func p256FromMont(res, in []uint64)
// iff cond == 1 val <- -val
+//
//go:noescape
func p256NegCond(val []uint64, cond int)
// if cond == 0 res <- b; else res <- a
+//
//go:noescape
func p256MovCond(res, a, b []uint64, cond int)
// Endianness swap
+//
//go:noescape
func p256BigToLittle(res []uint64, in []byte)
@@ -80,6 +86,7 @@ func p256BigToLittle(res []uint64, in []byte)
func p256LittleToBig(res []byte, in []uint64)
// Constant time table access
+//
//go:noescape
func p256Select(point, table []uint64, idx int)
@@ -87,10 +94,12 @@ func p256Select(point, table []uint64, idx int)
func p256SelectBase(point *[12]uint64, table string, idx int)
// Montgomery multiplication modulo Ord(G)
+//
//go:noescape
func p256OrdMul(res, in1, in2 []uint64)
// Montgomery square modulo Ord(G), repeated n times
+//
//go:noescape
func p256OrdSqr(res, in []uint64, n int)
@@ -98,16 +107,19 @@ func p256OrdSqr(res, in []uint64, n int)
// If sign == 1 -> in2 = -in2
// If sel == 0 -> res = in1
// if zero == 0 -> res = in2
+//
//go:noescape
func p256PointAddAffineAsm(res, in1, in2 []uint64, sign, sel, zero int)
// Point add. Returns one if the two input points were equal and zero
// otherwise. (Note that, due to the way that the equations work out, some
// representations of ∞ are considered equal to everything by this function.)
+//
//go:noescape
func p256PointAddAsm(res, in1, in2 []uint64) int
// Point double
+//
//go:noescape
func p256PointDoubleAsm(res, in []uint64)
diff --git a/src/crypto/rand/rand_batched_test.go b/src/crypto/rand/rand_batched_test.go
index 28e45aa689e..7a981e7892f 100644
--- a/src/crypto/rand/rand_batched_test.go
+++ b/src/crypto/rand/rand_batched_test.go
@@ -2,21 +2,24 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-//go:build linux || freebsd || dragonfly || solaris
+//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
package rand
import (
"bytes"
+ "encoding/binary"
+ "errors"
+ prand "math/rand"
"testing"
)
func TestBatched(t *testing.T) {
- fillBatched := batched(func(p []byte) bool {
+ fillBatched := batched(func(p []byte) error {
for i := range p {
p[i] = byte(i)
}
- return true
+ return nil
}, 5)
p := make([]byte, 13)
@@ -29,16 +32,49 @@ func TestBatched(t *testing.T) {
}
}
+func TestBatchedBuffering(t *testing.T) {
+ var prandSeed [8]byte
+ Read(prandSeed[:])
+ prand.Seed(int64(binary.LittleEndian.Uint64(prandSeed[:])))
+
+ backingStore := make([]byte, 1<<23)
+ prand.Read(backingStore)
+ backingMarker := backingStore[:]
+ output := make([]byte, len(backingStore))
+ outputMarker := output[:]
+
+ fillBatched := batched(func(p []byte) error {
+ n := copy(p, backingMarker)
+ backingMarker = backingMarker[n:]
+ return nil
+ }, 731)
+
+ for len(outputMarker) > 0 {
+ max := 9200
+ if max > len(outputMarker) {
+ max = len(outputMarker)
+ }
+ howMuch := prand.Intn(max + 1)
+ if !fillBatched(outputMarker[:howMuch]) {
+ t.Fatal("batched function returned false")
+ }
+ outputMarker = outputMarker[howMuch:]
+ }
+ if !bytes.Equal(backingStore, output) {
+ t.Error("incorrect batch result")
+ }
+}
+
func TestBatchedError(t *testing.T) {
- b := batched(func(p []byte) bool { return false }, 5)
+ b := batched(func(p []byte) error { return errors.New("failure") }, 5)
if b(make([]byte, 13)) {
- t.Fatal("batched function should have returned false")
+ t.Fatal("batched function should have returned an error")
}
}
func TestBatchedEmpty(t *testing.T) {
- b := batched(func(p []byte) bool { return false }, 5)
+ b := batched(func(p []byte) error { return errors.New("failure") }, 5)
if !b(make([]byte, 0)) {
- t.Fatal("empty slice should always return true")
+ t.Fatal("empty slice should always return successful")
}
}
diff --git a/src/crypto/rand/rand_dragonfly.go b/src/crypto/rand/rand_dragonfly.go
deleted file mode 100644
index 8a36fea6cd3..00000000000
--- a/src/crypto/rand/rand_dragonfly.go
+++ /dev/null
@@ -1,9 +0,0 @@
-// Copyright 2021 The Go Authors. 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
-
-// maxGetRandomRead is the maximum number of bytes to ask for in one call to the
-// getrandom() syscall. In DragonFlyBSD at most 256 bytes will be returned per call.
-const maxGetRandomRead = 1 << 8
diff --git a/src/crypto/rand/rand_freebsd.go b/src/crypto/rand/rand_freebsd.go
deleted file mode 100644
index 75f683c386d..00000000000
--- a/src/crypto/rand/rand_freebsd.go
+++ /dev/null
@@ -1,9 +0,0 @@
-// Copyright 2018 The Go Authors. 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
-
-// maxGetRandomRead is the maximum number of bytes to ask for in one call to the
-// getrandom() syscall. In FreeBSD at most 256 bytes will be returned per call.
-const maxGetRandomRead = 1 << 8
diff --git a/src/crypto/rand/rand_getentropy.go b/src/crypto/rand/rand_getentropy.go
index 2bf2f520324..68f921b0fc3 100644
--- a/src/crypto/rand/rand_getentropy.go
+++ b/src/crypto/rand/rand_getentropy.go
@@ -6,25 +6,9 @@
package rand
-import (
- "internal/syscall/unix"
-)
+import "internal/syscall/unix"
func init() {
- altGetRandom = getEntropy
-}
-
-func getEntropy(p []byte) (ok bool) {
// getentropy(2) returns a maximum of 256 bytes per call
- for i := 0; i < len(p); i += 256 {
- end := i + 256
- if len(p) < end {
- end = len(p)
- }
- err := unix.GetEntropy(p[i:end])
- if err != nil {
- return false
- }
- }
- return true
+ altGetRandom = batched(unix.GetEntropy, 256)
}
diff --git a/src/crypto/rand/rand_batched.go b/src/crypto/rand/rand_getrandom.go
index 3e8e6203829..cb31a5687a5 100644
--- a/src/crypto/rand/rand_batched.go
+++ b/src/crypto/rand/rand_getrandom.go
@@ -8,25 +8,25 @@ package rand
import (
"internal/syscall/unix"
+ "runtime"
+ "syscall"
)
-// maxGetRandomRead is platform dependent.
func init() {
- altGetRandom = batched(getRandomBatch, maxGetRandomRead)
-}
-
-// batched returns a function that calls f to populate a []byte by chunking it
-// into subslices of, at most, readMax bytes.
-func batched(f func([]byte) bool, readMax int) func([]byte) bool {
- return func(buf []byte) bool {
- for len(buf) > readMax {
- if !f(buf[:readMax]) {
- return false
- }
- buf = buf[readMax:]
- }
- return len(buf) == 0 || f(buf)
+ var maxGetRandomRead int
+ switch runtime.GOOS {
+ case "linux", "android":
+ // Per the manpage:
+ // When reading from the urandom source, a maximum of 33554431 bytes
+ // is returned by a single call to getrandom() on systems where int
+ // has a size of 32 bits.
+ maxGetRandomRead = (1 << 25) - 1
+ case "freebsd", "dragonfly", "solaris":
+ maxGetRandomRead = 1 << 8
+ default:
+ panic("no maximum specified for GetRandom")
}
+ altGetRandom = batched(getRandom, maxGetRandomRead)
}
// If the kernel is too old to support the getrandom syscall(),
@@ -36,7 +36,13 @@ func batched(f func([]byte) bool, readMax int) func([]byte) bool {
// If the kernel supports the getrandom() syscall, unix.GetRandom will block
// until the kernel has sufficient randomness (as we don't use GRND_NONBLOCK).
// In this case, unix.GetRandom will not return an error.
-func getRandomBatch(p []byte) (ok bool) {
+func getRandom(p []byte) error {
n, err := unix.GetRandom(p, 0)
- return n == len(p) && err == nil
+ if err != nil {
+ return err
+ }
+ if n != len(p) {
+ return syscall.EIO
+ }
+ return nil
}
diff --git a/src/crypto/rand/rand_linux.go b/src/crypto/rand/rand_linux.go
deleted file mode 100644
index 26b93c54d28..00000000000
--- a/src/crypto/rand/rand_linux.go
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package rand
-
-// maxGetRandomRead is the maximum number of bytes to ask for in one call to the
-// getrandom() syscall. In linux at most 2^25-1 bytes will be returned per call.
-// From the manpage
-//
-// * When reading from the urandom source, a maximum of 33554431 bytes
-// is returned by a single call to getrandom() on systems where int
-// has a size of 32 bits.
-const maxGetRandomRead = (1 << 25) - 1
diff --git a/src/crypto/rand/rand_solaris.go b/src/crypto/rand/rand_solaris.go
deleted file mode 100644
index bbad0fe557e..00000000000
--- a/src/crypto/rand/rand_solaris.go
+++ /dev/null
@@ -1,10 +0,0 @@
-// Copyright 2021 The Go Authors. 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
-
-// maxGetRandomRead is the maximum number of bytes to ask for in one call to the
-// getrandom() syscall. Across all the Solaris platforms, 256 bytes is the
-// lowest number of bytes returned atomically per call.
-const maxGetRandomRead = 1 << 8
diff --git a/src/crypto/rand/rand_unix.go b/src/crypto/rand/rand_unix.go
index c9eb7d992de..0b137e131f0 100644
--- a/src/crypto/rand/rand_unix.go
+++ b/src/crypto/rand/rand_unix.go
@@ -15,7 +15,6 @@ import (
"io"
"os"
"sync"
- "sync/atomic"
"syscall"
"time"
)
@@ -36,20 +35,70 @@ func init() {
type reader struct {
f io.Reader
mu sync.Mutex
- used int32 // atomic; whether this reader has been used
+ used bool // whether this reader has been used
}
// altGetRandom if non-nil specifies an OS-specific function to get
// urandom-style randomness.
var altGetRandom func([]byte) (ok bool)
+// batched returns a function that calls f to populate a []byte by chunking it
+// into subslices of, at most, readMax bytes, buffering min(readMax, 4096)
+// bytes at a time.
+func batched(f func([]byte) error, readMax int) func([]byte) bool {
+ bufferSize := 4096
+ if bufferSize > readMax {
+ bufferSize = readMax
+ }
+ fullBuffer := make([]byte, bufferSize)
+ var buf []byte
+ return func(out []byte) bool {
+ // First we copy any amount remaining in the buffer.
+ n := copy(out, buf)
+ out, buf = out[n:], buf[n:]
+
+ // Then, if we're requesting more than the buffer size,
+ // generate directly into the output, chunked by readMax.
+ for len(out) >= len(fullBuffer) {
+ read := len(out) - (len(out) % len(fullBuffer))
+ if read > readMax {
+ read = readMax
+ }
+ if f(out[:read]) != nil {
+ return false
+ }
+ out = out[read:]
+ }
+
+ // If there's a partial block left over, fill the buffer,
+ // and copy in the remainder.
+ if len(out) > 0 {
+ if f(fullBuffer[:]) != nil {
+ return false
+ }
+ buf = fullBuffer[:]
+ n = copy(out, buf)
+ out, buf = out[n:], buf[n:]
+ }
+
+ if len(out) > 0 {
+ panic("crypto/rand batching failed to fill buffer")
+ }
+
+ return true
+ }
+}
+
func warnBlocked() {
println("crypto/rand: blocked for 60 seconds waiting to read random data from the kernel")
}
func (r *reader) Read(b []byte) (n int, err error) {
boring.Unreachable()
- if atomic.CompareAndSwapInt32(&r.used, 0, 1) {
+ r.mu.Lock()
+ defer r.mu.Unlock()
+ if !r.used {
+ r.used = true
// First use of randomness. Start timer to warn about
// being blocked on entropy not being available.
t := time.AfterFunc(time.Minute, warnBlocked)
@@ -58,8 +107,6 @@ func (r *reader) Read(b []byte) (n int, err error) {
if altGetRandom != nil && altGetRandom(b) {
return len(b), nil
}
- r.mu.Lock()
- defer r.mu.Unlock()
if r.f == nil {
f, err := os.Open(urandomDevice)
if err != nil {
diff --git a/src/crypto/rsa/pkcs1v15.go b/src/crypto/rsa/pkcs1v15.go
index 213ddb4addb..8cf3b6e255b 100644
--- a/src/crypto/rsa/pkcs1v15.go
+++ b/src/crypto/rsa/pkcs1v15.go
@@ -128,8 +128,8 @@ func DecryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) ([]byt
// session key beforehand and continue the protocol with the resulting value.
// This will remove any possibility that an attacker can learn any information
// about the plaintext.
-// See ``Chosen Ciphertext Attacks Against Protocols Based on the RSA
-// Encryption Standard PKCS #1'', Daniel Bleichenbacher, Advances in Cryptology
+// See “Chosen Ciphertext Attacks Against Protocols Based on the RSA
+// Encryption Standard PKCS #1”, Daniel Bleichenbacher, Advances in Cryptology
// (Crypto '98).
//
// Note that if the session key is too small then it may be possible for an
@@ -242,10 +242,12 @@ func nonZeroRandomBytes(s []byte, rand io.Reader) (err error) {
}
// These are ASN1 DER structures:
-// DigestInfo ::= SEQUENCE {
-// digestAlgorithm AlgorithmIdentifier,
-// digest OCTET STRING
-// }
+//
+// DigestInfo ::= SEQUENCE {
+// digestAlgorithm AlgorithmIdentifier,
+// digest OCTET STRING
+// }
+//
// For performance, we don't use the generic ASN1 encoder. Rather, we
// precompute a prefix of the digest value that makes a valid ASN1 DER string
// with the correct contents.
diff --git a/src/crypto/rsa/pkcs1v15_test.go b/src/crypto/rsa/pkcs1v15_test.go
index c5d825b42a0..69c509a7713 100644
--- a/src/crypto/rsa/pkcs1v15_test.go
+++ b/src/crypto/rsa/pkcs1v15_test.go
@@ -187,7 +187,8 @@ type signPKCS1v15Test struct {
}
// These vectors have been tested with
-// `openssl rsautl -verify -inkey pk -in signature | hexdump -C`
+//
+// `openssl rsautl -verify -inkey pk -in signature | hexdump -C`
var signPKCS1v15Tests = []signPKCS1v15Test{
{"Test.\n", "a4f3fa6ea93bcdd0c57be020c1193ecbfd6f200a3d95c409769b029578fa0e336ad9a347600e40d3ae823b8c7e6bad88cc07c1d54c3a1523cbbb6d58efc362ae"},
}
diff --git a/src/crypto/subtle/constant_time.go b/src/crypto/subtle/constant_time.go
index 7c3cf05c462..4e0527f9d5a 100644
--- a/src/crypto/subtle/constant_time.go
+++ b/src/crypto/subtle/constant_time.go
@@ -8,7 +8,8 @@ package subtle
// ConstantTimeCompare returns 1 if the two slices, x and y, have equal contents
// and 0 otherwise. The time taken is a function of the length of the slices and
-// is independent of the contents.
+// is independent of the contents. If the lengths of x and y do not match it
+// returns 0 immediately.
func ConstantTimeCompare(x, y []byte) int {
if len(x) != len(y) {
return 0
diff --git a/src/crypto/tls/cipher_suites.go b/src/crypto/tls/cipher_suites.go
index a33107e2f4f..3004b31698d 100644
--- a/src/crypto/tls/cipher_suites.go
+++ b/src/crypto/tls/cipher_suites.go
@@ -219,56 +219,56 @@ var cipherSuitesTLS13 = []*cipherSuiteTLS13{ // TODO: replace with a map.
//
// - Anything else comes before RC4
//
-// RC4 has practically exploitable biases. See https://www.rc4nomore.com.
+// RC4 has practically exploitable biases. See https://www.rc4nomore.com.
//
// - Anything else comes before CBC_SHA256
//
-// SHA-256 variants of the CBC ciphersuites don't implement any Lucky13
-// countermeasures. See http://www.isg.rhul.ac.uk/tls/Lucky13.html and
-// https://www.imperialviolet.org/2013/02/04/luckythirteen.html.
+// SHA-256 variants of the CBC ciphersuites don't implement any Lucky13
+// countermeasures. See http://www.isg.rhul.ac.uk/tls/Lucky13.html and
+// https://www.imperialviolet.org/2013/02/04/luckythirteen.html.
//
// - Anything else comes before 3DES
//
-// 3DES has 64-bit blocks, which makes it fundamentally susceptible to
-// birthday attacks. See https://sweet32.info.
+// 3DES has 64-bit blocks, which makes it fundamentally susceptible to
+// birthday attacks. See https://sweet32.info.
//
// - ECDHE comes before anything else
//
-// Once we got the broken stuff out of the way, the most important
-// property a cipher suite can have is forward secrecy. We don't
-// implement FFDHE, so that means ECDHE.
+// Once we got the broken stuff out of the way, the most important
+// property a cipher suite can have is forward secrecy. We don't
+// implement FFDHE, so that means ECDHE.
//
// - AEADs come before CBC ciphers
//
-// Even with Lucky13 countermeasures, MAC-then-Encrypt CBC cipher suites
-// are fundamentally fragile, and suffered from an endless sequence of
-// padding oracle attacks. See https://eprint.iacr.org/2015/1129,
-// https://www.imperialviolet.org/2014/12/08/poodleagain.html, and
-// https://blog.cloudflare.com/yet-another-padding-oracle-in-openssl-cbc-ciphersuites/.
+// Even with Lucky13 countermeasures, MAC-then-Encrypt CBC cipher suites
+// are fundamentally fragile, and suffered from an endless sequence of
+// padding oracle attacks. See https://eprint.iacr.org/2015/1129,
+// https://www.imperialviolet.org/2014/12/08/poodleagain.html, and
+// https://blog.cloudflare.com/yet-another-padding-oracle-in-openssl-cbc-ciphersuites/.
//
// - AES comes before ChaCha20
//
-// When AES hardware is available, AES-128-GCM and AES-256-GCM are faster
-// than ChaCha20Poly1305.
+// When AES hardware is available, AES-128-GCM and AES-256-GCM are faster
+// than ChaCha20Poly1305.
//
-// When AES hardware is not available, AES-128-GCM is one or more of: much
-// slower, way more complex, and less safe (because not constant time)
-// than ChaCha20Poly1305.
+// When AES hardware is not available, AES-128-GCM is one or more of: much
+// slower, way more complex, and less safe (because not constant time)
+// than ChaCha20Poly1305.
//
-// We use this list if we think both peers have AES hardware, and
-// cipherSuitesPreferenceOrderNoAES otherwise.
+// We use this list if we think both peers have AES hardware, and
+// cipherSuitesPreferenceOrderNoAES otherwise.
//
// - AES-128 comes before AES-256
//
-// The only potential advantages of AES-256 are better multi-target
-// margins, and hypothetical post-quantum properties. Neither apply to
-// TLS, and AES-256 is slower due to its four extra rounds (which don't
-// contribute to the advantages above).
+// The only potential advantages of AES-256 are better multi-target
+// margins, and hypothetical post-quantum properties. Neither apply to
+// TLS, and AES-256 is slower due to its four extra rounds (which don't
+// contribute to the advantages above).
//
// - ECDSA comes before RSA
//
-// The relative order of ECDSA and RSA cipher suites doesn't matter,
-// as they depend on the certificate. Pick one to get a stable order.
+// The relative order of ECDSA and RSA cipher suites doesn't matter,
+// as they depend on the certificate. Pick one to get a stable order.
var cipherSuitesPreferenceOrder = []uint16{
// AEADs w/ ECDHE
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
diff --git a/src/crypto/tls/conn.go b/src/crypto/tls/conn.go
index fba36d30100..b0c91791d90 100644
--- a/src/crypto/tls/conn.go
+++ b/src/crypto/tls/conn.go
@@ -587,12 +587,14 @@ func (c *Conn) readChangeCipherSpec() error {
// readRecordOrCCS reads one or more TLS records from the connection and
// updates the record layer state. Some invariants:
-// * c.in must be locked
-// * c.input must be empty
+// - c.in must be locked
+// - c.input must be empty
+//
// During the handshake one and only one of the following will happen:
// - c.hand grows
// - c.in.changeCipherSpec is called
// - an error is returned
+//
// After the handshake one and only one of the following will happen:
// - c.hand grows
// - c.input is set
diff --git a/src/crypto/tls/handshake_client_test.go b/src/crypto/tls/handshake_client_test.go
index 0950bb0ac45..380de9f6fb5 100644
--- a/src/crypto/tls/handshake_client_test.go
+++ b/src/crypto/tls/handshake_client_test.go
@@ -2564,7 +2564,7 @@ func testResumptionKeepsOCSPAndSCT(t *testing.T, ver uint16) {
}
}
-// TestClientHandshakeContextCancellation tests that cancelling
+// TestClientHandshakeContextCancellation tests that canceling
// the context given to the client side conn.HandshakeContext
// interrupts the in-progress handshake.
func TestClientHandshakeContextCancellation(t *testing.T) {
diff --git a/src/crypto/tls/handshake_server_test.go b/src/crypto/tls/handshake_server_test.go
index 6d2c4056261..16a22542ebf 100644
--- a/src/crypto/tls/handshake_server_test.go
+++ b/src/crypto/tls/handshake_server_test.go
@@ -1944,7 +1944,7 @@ func TestAESCipherReorderingTLS13(t *testing.T) {
}
}
-// TestServerHandshakeContextCancellation tests that cancelling
+// TestServerHandshakeContextCancellation tests that canceling
// the context given to the server side conn.HandshakeContext
// interrupts the in-progress handshake.
func TestServerHandshakeContextCancellation(t *testing.T) {
diff --git a/src/crypto/x509/cert_pool.go b/src/crypto/x509/cert_pool.go
index ae43c84424b..e9b2c122b96 100644
--- a/src/crypto/x509/cert_pool.go
+++ b/src/crypto/x509/cert_pool.go
@@ -77,7 +77,8 @@ func (s *CertPool) cert(n int) (*Certificate, error) {
return s.lazyCerts[n].getCert()
}
-func (s *CertPool) copy() *CertPool {
+// Clone returns a copy of s.
+func (s *CertPool) Clone() *CertPool {
p := &CertPool{
byName: make(map[string][]int, len(s.byName)),
lazyCerts: make([]lazyCert, len(s.lazyCerts)),
@@ -109,7 +110,7 @@ func (s *CertPool) copy() *CertPool {
// New changes in the system cert pool might not be reflected in subsequent calls.
func SystemCertPool() (*CertPool, error) {
if sysRoots := systemRootsPool(); sysRoots != nil {
- return sysRoots.copy(), nil
+ return sysRoots.Clone(), nil
}
return loadSystemRoots()
@@ -252,6 +253,9 @@ func (s *CertPool) Subjects() [][]byte {
// Equal reports whether s and other are equal.
func (s *CertPool) Equal(other *CertPool) bool {
+ if s == nil || other == nil {
+ return s == other
+ }
if s.systemPool != other.systemPool || len(s.haveSum) != len(other.haveSum) {
return false
}
diff --git a/src/crypto/x509/cert_pool_test.go b/src/crypto/x509/cert_pool_test.go
index d1ec9aaefd8..a12beda83d3 100644
--- a/src/crypto/x509/cert_pool_test.go
+++ b/src/crypto/x509/cert_pool_test.go
@@ -7,52 +7,102 @@ package x509
import "testing"
func TestCertPoolEqual(t *testing.T) {
- a, b := NewCertPool(), NewCertPool()
- if !a.Equal(b) {
- t.Error("two empty pools not equal")
- }
-
tc := &Certificate{Raw: []byte{1, 2, 3}, RawSubject: []byte{2}}
- a.AddCert(tc)
- if a.Equal(b) {
- t.Error("empty pool equals non-empty pool")
- }
-
- b.AddCert(tc)
- if !a.Equal(b) {
- t.Error("two non-empty pools not equal")
- }
-
otherTC := &Certificate{Raw: []byte{9, 8, 7}, RawSubject: []byte{8}}
- a.AddCert(otherTC)
- if a.Equal(b) {
- t.Error("non-equal pools equal")
- }
- systemA, err := SystemCertPool()
+ emptyPool := NewCertPool()
+ nonSystemPopulated := NewCertPool()
+ nonSystemPopulated.AddCert(tc)
+ nonSystemPopulatedAlt := NewCertPool()
+ nonSystemPopulatedAlt.AddCert(otherTC)
+ emptySystem, err := SystemCertPool()
if err != nil {
- t.Fatalf("unable to load system cert pool: %s", err)
+ t.Fatal(err)
}
- systemB, err := SystemCertPool()
+ populatedSystem, err := SystemCertPool()
if err != nil {
- t.Fatalf("unable to load system cert pool: %s", err)
- }
- if !systemA.Equal(systemB) {
- t.Error("two empty system pools not equal")
+ t.Fatal(err)
}
-
- systemA.AddCert(tc)
- if systemA.Equal(systemB) {
- t.Error("empty system pool equals non-empty system pool")
+ populatedSystem.AddCert(tc)
+ populatedSystemAlt, err := SystemCertPool()
+ if err != nil {
+ t.Fatal(err)
}
-
- systemB.AddCert(tc)
- if !systemA.Equal(systemB) {
- t.Error("two non-empty system pools not equal")
+ populatedSystemAlt.AddCert(otherTC)
+ tests := []struct {
+ name string
+ a *CertPool
+ b *CertPool
+ equal bool
+ }{
+ {
+ name: "two empty pools",
+ a: emptyPool,
+ b: emptyPool,
+ equal: true,
+ },
+ {
+ name: "one empty pool, one populated pool",
+ a: emptyPool,
+ b: nonSystemPopulated,
+ equal: false,
+ },
+ {
+ name: "two populated pools",
+ a: nonSystemPopulated,
+ b: nonSystemPopulated,
+ equal: true,
+ },
+ {
+ name: "two populated pools, different content",
+ a: nonSystemPopulated,
+ b: nonSystemPopulatedAlt,
+ equal: false,
+ },
+ {
+ name: "two empty system pools",
+ a: emptySystem,
+ b: emptySystem,
+ equal: true,
+ },
+ {
+ name: "one empty system pool, one populated system pool",
+ a: emptySystem,
+ b: populatedSystem,
+ equal: false,
+ },
+ {
+ name: "two populated system pools",
+ a: populatedSystem,
+ b: populatedSystem,
+ equal: true,
+ },
+ {
+ name: "two populated pools, different content",
+ a: populatedSystem,
+ b: populatedSystemAlt,
+ equal: false,
+ },
+ {
+ name: "two nil pools",
+ a: nil,
+ b: nil,
+ equal: true,
+ },
+ {
+ name: "one nil pool, one empty pool",
+ a: nil,
+ b: emptyPool,
+ equal: false,
+ },
}
- systemA.AddCert(otherTC)
- if systemA.Equal(systemB) {
- t.Error("non-equal system pools equal")
+ for _, tc := range tests {
+ t.Run(tc.name, func(t *testing.T) {
+ equal := tc.a.Equal(tc.b)
+ if equal != tc.equal {
+ t.Errorf("Unexpected Equal result: got %t, want %t", equal, tc.equal)
+ }
+ })
}
}
diff --git a/src/crypto/x509/internal/macos/corefoundation.go b/src/crypto/x509/internal/macos/corefoundation.go
index eb91a5db6e0..2677ff706ae 100644
--- a/src/crypto/x509/internal/macos/corefoundation.go
+++ b/src/crypto/x509/internal/macos/corefoundation.go
@@ -19,6 +19,7 @@ import (
)
// Core Foundation linker flags for the external linker. See Issue 42459.
+//
//go:cgo_ldflag "-framework"
//go:cgo_ldflag "CoreFoundation"
diff --git a/src/crypto/x509/internal/macos/security.go b/src/crypto/x509/internal/macos/security.go
index 381d918a94f..d8147ba8ba2 100644
--- a/src/crypto/x509/internal/macos/security.go
+++ b/src/crypto/x509/internal/macos/security.go
@@ -15,6 +15,7 @@ import (
)
// Security.framework linker flags for the external linker. See Issue 42459.
+//
//go:cgo_ldflag "-framework"
//go:cgo_ldflag "Security"
diff --git a/src/crypto/x509/parser.go b/src/crypto/x509/parser.go
index 333991bf14d..e0e8f6125fd 100644
--- a/src/crypto/x509/parser.go
+++ b/src/crypto/x509/parser.go
@@ -164,53 +164,29 @@ func parseAI(der cryptobyte.String) (pkix.AlgorithmIdentifier, error) {
return ai, nil
}
-func parseValidity(der cryptobyte.String) (time.Time, time.Time, error) {
- extract := func() (time.Time, error) {
- var t time.Time
- switch {
- case der.PeekASN1Tag(cryptobyte_asn1.UTCTime):
- // TODO(rolandshoemaker): once #45411 is fixed, the following code
- // should be replaced with a call to der.ReadASN1UTCTime.
- var utc cryptobyte.String
- if !der.ReadASN1(&utc, cryptobyte_asn1.UTCTime) {
- return t, errors.New("x509: malformed UTCTime")
- }
- s := string(utc)
-
- formatStr := "0601021504Z0700"
- var err error
- t, err = time.Parse(formatStr, s)
- if err != nil {
- formatStr = "060102150405Z0700"
- t, err = time.Parse(formatStr, s)
- }
- if err != nil {
- return t, err
- }
-
- if serialized := t.Format(formatStr); serialized != s {
- return t, errors.New("x509: malformed UTCTime")
- }
-
- if t.Year() >= 2050 {
- // UTCTime only encodes times prior to 2050. See https://tools.ietf.org/html/rfc5280#section-4.1.2.5.1
- t = t.AddDate(-100, 0, 0)
- }
- case der.PeekASN1Tag(cryptobyte_asn1.GeneralizedTime):
- if !der.ReadASN1GeneralizedTime(&t) {
- return t, errors.New("x509: malformed GeneralizedTime")
- }
- default:
- return t, errors.New("x509: unsupported time format")
+func parseTime(der *cryptobyte.String) (time.Time, error) {
+ var t time.Time
+ switch {
+ case der.PeekASN1Tag(cryptobyte_asn1.UTCTime):
+ if !der.ReadASN1UTCTime(&t) {
+ return t, errors.New("x509: malformed UTCTime")
}
- return t, nil
+ case der.PeekASN1Tag(cryptobyte_asn1.GeneralizedTime):
+ if !der.ReadASN1GeneralizedTime(&t) {
+ return t, errors.New("x509: malformed GeneralizedTime")
+ }
+ default:
+ return t, errors.New("x509: unsupported time format")
}
+ return t, nil
+}
- notBefore, err := extract()
+func parseValidity(der cryptobyte.String) (time.Time, time.Time, error) {
+ notBefore, err := parseTime(&der)
if err != nil {
return time.Time{}, time.Time{}, err
}
- notAfter, err := extract()
+ notAfter, err := parseTime(&der)
if err != nil {
return time.Time{}, time.Time{}, err
}
@@ -954,6 +930,7 @@ func parseCertificate(der []byte) (*Certificate, error) {
return nil, errors.New("x509: malformed extensions")
}
if present {
+ seenExts := make(map[string]bool)
if !extensions.ReadASN1(&extensions, cryptobyte_asn1.SEQUENCE) {
return nil, errors.New("x509: malformed extensions")
}
@@ -966,6 +943,11 @@ func parseCertificate(der []byte) (*Certificate, error) {
if err != nil {
return nil, err
}
+ oidStr := ext.Id.String()
+ if seenExts[oidStr] {
+ return nil, errors.New("x509: certificate contains duplicate extensions")
+ }
+ seenExts[oidStr] = true
cert.Extensions = append(cert.Extensions, ext)
}
err = processExtensions(cert)
@@ -1011,3 +993,164 @@ func ParseCertificates(der []byte) ([]*Certificate, error) {
}
return certs, nil
}
+
+// The X.509 standards confusingly 1-indexed the version names, but 0-indexed
+// the actual encoded version, so the version for X.509v2 is 1.
+const x509v2Version = 1
+
+// ParseRevocationList parses a X509 v2 Certificate Revocation List from the given
+// ASN.1 DER data.
+func ParseRevocationList(der []byte) (*RevocationList, error) {
+ rl := &RevocationList{}
+
+ input := cryptobyte.String(der)
+ // we read the SEQUENCE including length and tag bytes so that
+ // we can populate RevocationList.Raw, before unwrapping the
+ // SEQUENCE so it can be operated on
+ if !input.ReadASN1Element(&input, cryptobyte_asn1.SEQUENCE) {
+ return nil, errors.New("x509: malformed certificate")
+ }
+ rl.Raw = input
+ if !input.ReadASN1(&input, cryptobyte_asn1.SEQUENCE) {
+ return nil, errors.New("x509: malformed certificate")
+ }
+
+ var tbs cryptobyte.String
+ // do the same trick again as above to extract the raw
+ // bytes for Certificate.RawTBSCertificate
+ if !input.ReadASN1Element(&tbs, cryptobyte_asn1.SEQUENCE) {
+ return nil, errors.New("x509: malformed tbs certificate")
+ }
+ rl.RawTBSRevocationList = tbs
+ if !tbs.ReadASN1(&tbs, cryptobyte_asn1.SEQUENCE) {
+ return nil, errors.New("x509: malformed tbs certificate")
+ }
+
+ var version int
+ if !tbs.PeekASN1Tag(cryptobyte_asn1.INTEGER) {
+ return nil, errors.New("x509: unsupported crl version")
+ }
+ if !tbs.ReadASN1Integer(&version) {
+ return nil, errors.New("x509: malformed crl")
+ }
+ if version != x509v2Version {
+ return nil, fmt.Errorf("x509: unsupported crl version: %d", version)
+ }
+
+ var sigAISeq cryptobyte.String
+ if !tbs.ReadASN1(&sigAISeq, cryptobyte_asn1.SEQUENCE) {
+ return nil, errors.New("x509: malformed signature algorithm identifier")
+ }
+ // Before parsing the inner algorithm identifier, extract
+ // the outer algorithm identifier and make sure that they
+ // match.
+ var outerSigAISeq cryptobyte.String
+ if !input.ReadASN1(&outerSigAISeq, cryptobyte_asn1.SEQUENCE) {
+ return nil, errors.New("x509: malformed algorithm identifier")
+ }
+ if !bytes.Equal(outerSigAISeq, sigAISeq) {
+ return nil, errors.New("x509: inner and outer signature algorithm identifiers don't match")
+ }
+ sigAI, err := parseAI(sigAISeq)
+ if err != nil {
+ return nil, err
+ }
+ rl.SignatureAlgorithm = getSignatureAlgorithmFromAI(sigAI)
+
+ var signature asn1.BitString
+ if !input.ReadASN1BitString(&signature) {
+ return nil, errors.New("x509: malformed signature")
+ }
+ rl.Signature = signature.RightAlign()
+
+ var issuerSeq cryptobyte.String
+ if !tbs.ReadASN1Element(&issuerSeq, cryptobyte_asn1.SEQUENCE) {
+ return nil, errors.New("x509: malformed issuer")
+ }
+ rl.RawIssuer = issuerSeq
+ issuerRDNs, err := parseName(issuerSeq)
+ if err != nil {
+ return nil, err
+ }
+ rl.Issuer.FillFromRDNSequence(issuerRDNs)
+
+ rl.ThisUpdate, err = parseTime(&tbs)
+ if err != nil {
+ return nil, err
+ }
+ if tbs.PeekASN1Tag(cryptobyte_asn1.GeneralizedTime) || tbs.PeekASN1Tag(cryptobyte_asn1.UTCTime) {
+ rl.NextUpdate, err = parseTime(&tbs)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ if tbs.PeekASN1Tag(cryptobyte_asn1.SEQUENCE) {
+ var revokedSeq cryptobyte.String
+ if !tbs.ReadASN1(&revokedSeq, cryptobyte_asn1.SEQUENCE) {
+ return nil, errors.New("x509: malformed crl")
+ }
+ for !revokedSeq.Empty() {
+ var certSeq cryptobyte.String
+ if !revokedSeq.ReadASN1(&certSeq, cryptobyte_asn1.SEQUENCE) {
+ return nil, errors.New("x509: malformed crl")
+ }
+ rc := pkix.RevokedCertificate{}
+ rc.SerialNumber = new(big.Int)
+ if !certSeq.ReadASN1Integer(rc.SerialNumber) {
+ return nil, errors.New("x509: malformed serial number")
+ }
+ rc.RevocationTime, err = parseTime(&certSeq)
+ if err != nil {
+ return nil, err
+ }
+ var extensions cryptobyte.String
+ var present bool
+ if !tbs.ReadOptionalASN1(&extensions, &present, cryptobyte_asn1.SEQUENCE) {
+ return nil, errors.New("x509: malformed extensions")
+ }
+ if present {
+ if !extensions.ReadASN1(&extensions, cryptobyte_asn1.SEQUENCE) {
+ return nil, errors.New("x509: malformed extensions")
+ }
+ for !extensions.Empty() {
+ var extension cryptobyte.String
+ if !extensions.ReadASN1(&extension, cryptobyte_asn1.SEQUENCE) {
+ return nil, errors.New("x509: malformed extension")
+ }
+ ext, err := parseExtension(extension)
+ if err != nil {
+ return nil, err
+ }
+ rc.Extensions = append(rc.Extensions, ext)
+ }
+ }
+
+ rl.RevokedCertificates = append(rl.RevokedCertificates, rc)
+ }
+ }
+
+ var extensions cryptobyte.String
+ var present bool
+ if !tbs.ReadOptionalASN1(&extensions, &present, cryptobyte_asn1.Tag(0).Constructed().ContextSpecific()) {
+ return nil, errors.New("x509: malformed extensions")
+ }
+ if present {
+ if !extensions.ReadASN1(&extensions, cryptobyte_asn1.SEQUENCE) {
+ return nil, errors.New("x509: malformed extensions")
+ }
+ for !extensions.Empty() {
+ var extension cryptobyte.String
+ if !extensions.ReadASN1(&extension, cryptobyte_asn1.SEQUENCE) {
+ return nil, errors.New("x509: malformed extension")
+ }
+ ext, err := parseExtension(extension)
+ if err != nil {
+ return nil, err
+ }
+ rl.Extensions = append(rl.Extensions, ext)
+ }
+ }
+
+ return rl, nil
+}
diff --git a/src/crypto/x509/pkcs8_test.go b/src/crypto/x509/pkcs8_test.go
index cb7ee4c1627..aaceced9263 100644
--- a/src/crypto/x509/pkcs8_test.go
+++ b/src/crypto/x509/pkcs8_test.go
@@ -17,23 +17,28 @@ import (
)
// Generated using:
-// openssl genrsa 1024 | openssl pkcs8 -topk8 -nocrypt
+//
+// openssl genrsa 1024 | openssl pkcs8 -topk8 -nocrypt
var pkcs8RSAPrivateKeyHex = `30820278020100300d06092a864886f70d0101010500048202623082025e02010002818100cfb1b5bf9685ffa97b4f99df4ff122b70e59ac9b992f3bc2b3dde17d53c1a34928719b02e8fd17839499bfbd515bd6ef99c7a1c47a239718fe36bfd824c0d96060084b5f67f0273443007a24dfaf5634f7772c9346e10eb294c2306671a5a5e719ae24b4de467291bc571014b0e02dec04534d66a9bb171d644b66b091780e8d020301000102818100b595778383c4afdbab95d2bfed12b3f93bb0a73a7ad952f44d7185fd9ec6c34de8f03a48770f2009c8580bcd275e9632714e9a5e3f32f29dc55474b2329ff0ebc08b3ffcb35bc96e6516b483df80a4a59cceb71918cbabf91564e64a39d7e35dce21cb3031824fdbc845dba6458852ec16af5dddf51a8397a8797ae0337b1439024100ea0eb1b914158c70db39031dd8904d6f18f408c85fbbc592d7d20dee7986969efbda081fdf8bc40e1b1336d6b638110c836bfdc3f314560d2e49cd4fbde1e20b024100e32a4e793b574c9c4a94c8803db5152141e72d03de64e54ef2c8ed104988ca780cd11397bc359630d01b97ebd87067c5451ba777cf045ca23f5912f1031308c702406dfcdbbd5a57c9f85abc4edf9e9e29153507b07ce0a7ef6f52e60dcfebe1b8341babd8b789a837485da6c8d55b29bbb142ace3c24a1f5b54b454d01b51e2ad03024100bd6a2b60dee01e1b3bfcef6a2f09ed027c273cdbbaf6ba55a80f6dcc64e4509ee560f84b4f3e076bd03b11e42fe71a3fdd2dffe7e0902c8584f8cad877cdc945024100aa512fa4ada69881f1d8bb8ad6614f192b83200aef5edf4811313d5ef30a86cbd0a90f7b025c71ea06ec6b34db6306c86b1040670fd8654ad7291d066d06d031`
// Generated using:
-// openssl ecparam -genkey -name secp224r1 | openssl pkcs8 -topk8 -nocrypt
+//
+// openssl ecparam -genkey -name secp224r1 | openssl pkcs8 -topk8 -nocrypt
var pkcs8P224PrivateKeyHex = `3078020100301006072a8648ce3d020106052b810400210461305f020101041cca3d72b3e88fed2684576dad9b80a9180363a5424986900e3abcab3fa13c033a0004f8f2a6372872a4e61263ed893afb919576a4cacfecd6c081a2cbc76873cf4ba8530703c6042b3a00e2205087e87d2435d2e339e25702fae1`
// Generated using:
-// openssl ecparam -genkey -name secp256r1 | openssl pkcs8 -topk8 -nocrypt
+//
+// openssl ecparam -genkey -name secp256r1 | openssl pkcs8 -topk8 -nocrypt
var pkcs8P256PrivateKeyHex = `308187020100301306072a8648ce3d020106082a8648ce3d030107046d306b0201010420dad6b2f49ca774c36d8ae9517e935226f667c929498f0343d2424d0b9b591b43a14403420004b9c9b90095476afe7b860d8bd43568cab7bcb2eed7b8bf2fa0ce1762dd20b04193f859d2d782b1e4cbfd48492f1f533113a6804903f292258513837f07fda735`
// Generated using:
-// openssl ecparam -genkey -name secp384r1 | openssl pkcs8 -topk8 -nocrypt
+//
+// openssl ecparam -genkey -name secp384r1 | openssl pkcs8 -topk8 -nocrypt
var pkcs8P384PrivateKeyHex = `3081b6020100301006072a8648ce3d020106052b8104002204819e30819b02010104309bf832f6aaaeacb78ce47ffb15e6fd0fd48683ae79df6eca39bfb8e33829ac94aa29d08911568684c2264a08a4ceb679a164036200049070ad4ed993c7770d700e9f6dc2baa83f63dd165b5507f98e8ff29b5d2e78ccbe05c8ddc955dbf0f7497e8222cfa49314fe4e269459f8e880147f70d785e530f2939e4bf9f838325bb1a80ad4cf59272ae0e5efe9a9dc33d874492596304bd3`
// Generated using:
-// openssl ecparam -genkey -name secp521r1 | openssl pkcs8 -topk8 -nocrypt
+//
+// openssl ecparam -genkey -name secp521r1 | openssl pkcs8 -topk8 -nocrypt
//
// Note that OpenSSL will truncate the private key if it can (i.e. it emits it
// like an integer, even though it's an OCTET STRING field). Thus if you
diff --git a/src/crypto/x509/pkix/pkix.go b/src/crypto/x509/pkix/pkix.go
index e9179ed0679..bea8fe7f750 100644
--- a/src/crypto/x509/pkix/pkix.go
+++ b/src/crypto/x509/pkix/pkix.go
@@ -214,13 +214,13 @@ func (n Name) appendRDNs(in RDNSequence, values []string, oid asn1.ObjectIdentif
// ToRDNSequence converts n into a single RDNSequence. The following
// attributes are encoded as multi-value RDNs:
//
-// - Country
-// - Organization
-// - OrganizationalUnit
-// - Locality
-// - Province
-// - StreetAddress
-// - PostalCode
+// - Country
+// - Organization
+// - OrganizationalUnit
+// - Locality
+// - Province
+// - StreetAddress
+// - PostalCode
//
// Each ExtraNames entry is encoded as an individual RDN.
func (n Name) ToRDNSequence() (ret RDNSequence) {
@@ -296,6 +296,8 @@ func (certList *CertificateList) HasExpired(now time.Time) bool {
// TBSCertificateList represents the ASN.1 structure of the same name. See RFC
// 5280, section 5.1.
+//
+// Deprecated: x509.RevocationList should be used instead.
type TBSCertificateList struct {
Raw asn1.RawContent
Version int `asn1:"optional,default:0"`
@@ -309,6 +311,8 @@ type TBSCertificateList struct {
// RevokedCertificate represents the ASN.1 structure of the same name. See RFC
// 5280, section 5.1.
+//
+// Deprecated: x509.RevocationList should be used instead.
type RevokedCertificate struct {
SerialNumber *big.Int
RevocationTime time.Time
diff --git a/src/crypto/x509/root.go b/src/crypto/x509/root.go
index eef9c047b2e..91f4d29a1f9 100644
--- a/src/crypto/x509/root.go
+++ b/src/crypto/x509/root.go
@@ -8,6 +8,7 @@ package x509
// argument to the latest security_certificates version from
// https://opensource.apple.com/source/security_certificates/
// and run "go generate". See https://golang.org/issue/38843.
+//
//go:generate go run root_ios_gen.go -version 55188.120.1.0.1
import "sync"
diff --git a/src/crypto/x509/sec1.go b/src/crypto/x509/sec1.go
index 52c108ff1d6..8053ff5cda3 100644
--- a/src/crypto/x509/sec1.go
+++ b/src/crypto/x509/sec1.go
@@ -17,8 +17,10 @@ const ecPrivKeyVersion = 1
// ecPrivateKey reflects an ASN.1 Elliptic Curve Private Key Structure.
// References:
-// RFC 5915
-// SEC1 - http://www.secg.org/sec1-v2.pdf
+//
+// RFC 5915
+// SEC1 - http://www.secg.org/sec1-v2.pdf
+//
// Per RFC 5915 the NamedCurveOID is marked as ASN.1 OPTIONAL, however in
// most cases it is not.
type ecPrivateKey struct {
diff --git a/src/crypto/x509/verify.go b/src/crypto/x509/verify.go
index 71ab62a67fe..218d794cca4 100644
--- a/src/crypto/x509/verify.go
+++ b/src/crypto/x509/verify.go
@@ -6,6 +6,7 @@ package x509
import (
"bytes"
+ "crypto"
"errors"
"fmt"
"net"
@@ -602,73 +603,101 @@ func (c *Certificate) isValid(certType int, currentChain []*Certificate, opts *V
leaf = currentChain[0]
}
- if (certType == intermediateCertificate || certType == rootCertificate) &&
- c.hasNameConstraints() && leaf.hasSANExtension() {
- err := forEachSAN(leaf.getSANExtension(), func(tag int, data []byte) error {
- switch tag {
- case nameTypeEmail:
- name := string(data)
- mailbox, ok := parseRFC2821Mailbox(name)
- if !ok {
- return fmt.Errorf("x509: cannot parse rfc822Name %q", mailbox)
- }
-
- if err := c.checkNameConstraints(&comparisonCount, maxConstraintComparisons, "email address", name, mailbox,
- func(parsedName, constraint any) (bool, error) {
- return matchEmailConstraint(parsedName.(rfc2821Mailbox), constraint.(string))
- }, c.PermittedEmailAddresses, c.ExcludedEmailAddresses); err != nil {
- return err
- }
-
- case nameTypeDNS:
- name := string(data)
- if _, ok := domainToReverseLabels(name); !ok {
- return fmt.Errorf("x509: cannot parse dnsName %q", name)
- }
-
- if err := c.checkNameConstraints(&comparisonCount, maxConstraintComparisons, "DNS name", name, name,
- func(parsedName, constraint any) (bool, error) {
- return matchDomainConstraint(parsedName.(string), constraint.(string))
- }, c.PermittedDNSDomains, c.ExcludedDNSDomains); err != nil {
- return err
- }
-
- case nameTypeURI:
- name := string(data)
- uri, err := url.Parse(name)
- if err != nil {
- return fmt.Errorf("x509: internal error: URI SAN %q failed to parse", name)
- }
-
- if err := c.checkNameConstraints(&comparisonCount, maxConstraintComparisons, "URI", name, uri,
- func(parsedName, constraint any) (bool, error) {
- return matchURIConstraint(parsedName.(*url.URL), constraint.(string))
- }, c.PermittedURIDomains, c.ExcludedURIDomains); err != nil {
- return err
+ if (len(c.ExtKeyUsage) > 0 || len(c.UnknownExtKeyUsage) > 0) && len(opts.KeyUsages) > 0 {
+ acceptableUsage := false
+ um := make(map[ExtKeyUsage]bool, len(opts.KeyUsages))
+ for _, u := range opts.KeyUsages {
+ um[u] = true
+ }
+ if !um[ExtKeyUsageAny] {
+ for _, u := range c.ExtKeyUsage {
+ if u == ExtKeyUsageAny || um[u] {
+ acceptableUsage = true
+ break
}
+ }
+ if !acceptableUsage {
+ return CertificateInvalidError{c, IncompatibleUsage, ""}
+ }
+ }
+ }
- case nameTypeIP:
- ip := net.IP(data)
- if l := len(ip); l != net.IPv4len && l != net.IPv6len {
- return fmt.Errorf("x509: internal error: IP SAN %x failed to parse", data)
+ if (certType == intermediateCertificate || certType == rootCertificate) &&
+ c.hasNameConstraints() {
+ toCheck := []*Certificate{}
+ if leaf.hasSANExtension() {
+ toCheck = append(toCheck, leaf)
+ }
+ if c.hasSANExtension() {
+ toCheck = append(toCheck, c)
+ }
+ for _, sanCert := range toCheck {
+ err := forEachSAN(sanCert.getSANExtension(), func(tag int, data []byte) error {
+ switch tag {
+ case nameTypeEmail:
+ name := string(data)
+ mailbox, ok := parseRFC2821Mailbox(name)
+ if !ok {
+ return fmt.Errorf("x509: cannot parse rfc822Name %q", mailbox)
+ }
+
+ if err := c.checkNameConstraints(&comparisonCount, maxConstraintComparisons, "email address", name, mailbox,
+ func(parsedName, constraint any) (bool, error) {
+ return matchEmailConstraint(parsedName.(rfc2821Mailbox), constraint.(string))
+ }, c.PermittedEmailAddresses, c.ExcludedEmailAddresses); err != nil {
+ return err
+ }
+
+ case nameTypeDNS:
+ name := string(data)
+ if _, ok := domainToReverseLabels(name); !ok {
+ return fmt.Errorf("x509: cannot parse dnsName %q", name)
+ }
+
+ if err := c.checkNameConstraints(&comparisonCount, maxConstraintComparisons, "DNS name", name, name,
+ func(parsedName, constraint any) (bool, error) {
+ return matchDomainConstraint(parsedName.(string), constraint.(string))
+ }, c.PermittedDNSDomains, c.ExcludedDNSDomains); err != nil {
+ return err
+ }
+
+ case nameTypeURI:
+ name := string(data)
+ uri, err := url.Parse(name)
+ if err != nil {
+ return fmt.Errorf("x509: internal error: URI SAN %q failed to parse", name)
+ }
+
+ if err := c.checkNameConstraints(&comparisonCount, maxConstraintComparisons, "URI", name, uri,
+ func(parsedName, constraint any) (bool, error) {
+ return matchURIConstraint(parsedName.(*url.URL), constraint.(string))
+ }, c.PermittedURIDomains, c.ExcludedURIDomains); err != nil {
+ return err
+ }
+
+ case nameTypeIP:
+ ip := net.IP(data)
+ if l := len(ip); l != net.IPv4len && l != net.IPv6len {
+ return fmt.Errorf("x509: internal error: IP SAN %x failed to parse", data)
+ }
+
+ if err := c.checkNameConstraints(&comparisonCount, maxConstraintComparisons, "IP address", ip.String(), ip,
+ func(parsedName, constraint any) (bool, error) {
+ return matchIPConstraint(parsedName.(net.IP), constraint.(*net.IPNet))
+ }, c.PermittedIPRanges, c.ExcludedIPRanges); err != nil {
+ return err
+ }
+
+ default:
+ // Unknown SAN types are ignored.
}
- if err := c.checkNameConstraints(&comparisonCount, maxConstraintComparisons, "IP address", ip.String(), ip,
- func(parsedName, constraint any) (bool, error) {
- return matchIPConstraint(parsedName.(net.IP), constraint.(*net.IPNet))
- }, c.PermittedIPRanges, c.ExcludedIPRanges); err != nil {
- return err
- }
+ return nil
+ })
- default:
- // Unknown SAN types are ignored.
+ if err != nil {
+ return err
}
-
- return nil
- })
-
- if err != nil {
- return err
}
}
@@ -779,6 +808,10 @@ func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err e
}
}
+ if len(opts.KeyUsages) == 0 {
+ opts.KeyUsages = []ExtKeyUsage{ExtKeyUsageServerAuth}
+ }
+
err = c.isValid(leafCertificate, nil, &opts)
if err != nil {
return
@@ -791,38 +824,10 @@ func (c *Certificate) Verify(opts VerifyOptions) (chains [][]*Certificate, err e
}
}
- var candidateChains [][]*Certificate
if opts.Roots.contains(c) {
- candidateChains = append(candidateChains, []*Certificate{c})
- } else {
- if candidateChains, err = c.buildChains(nil, []*Certificate{c}, nil, &opts); err != nil {
- return nil, err
- }
- }
-
- keyUsages := opts.KeyUsages
- if len(keyUsages) == 0 {
- keyUsages = []ExtKeyUsage{ExtKeyUsageServerAuth}
- }
-
- // If any key usage is acceptable then we're done.
- for _, usage := range keyUsages {
- if usage == ExtKeyUsageAny {
- return candidateChains, nil
- }
- }
-
- for _, candidate := range candidateChains {
- if checkChainForKeyUsage(candidate, keyUsages) {
- chains = append(chains, candidate)
- }
- }
-
- if len(chains) == 0 {
- return nil, CertificateInvalidError{c, IncompatibleUsage, ""}
+ return [][]*Certificate{{c}}, nil
}
-
- return chains, nil
+ return c.buildChains([]*Certificate{c}, nil, &opts)
}
func appendToFreshChain(chain []*Certificate, cert *Certificate) []*Certificate {
@@ -838,15 +843,22 @@ func appendToFreshChain(chain []*Certificate, cert *Certificate) []*Certificate
// for failed checks due to different intermediates having the same Subject.
const maxChainSignatureChecks = 100
-func (c *Certificate) buildChains(cache map[*Certificate][][]*Certificate, currentChain []*Certificate, sigChecks *int, opts *VerifyOptions) (chains [][]*Certificate, err error) {
+func (c *Certificate) buildChains(currentChain []*Certificate, sigChecks *int, opts *VerifyOptions) (chains [][]*Certificate, err error) {
var (
hintErr error
hintCert *Certificate
)
+ type pubKeyEqual interface {
+ Equal(crypto.PublicKey) bool
+ }
+
considerCandidate := func(certType int, candidate *Certificate) {
for _, cert := range currentChain {
- if cert.Equal(candidate) {
+ // If a certificate already appeared in the chain we've built, don't
+ // reconsider it. This prevents loops, for isntance those created by
+ // mutual cross-signatures, or other cross-signature bridges oddities.
+ if bytes.Equal(cert.RawSubject, candidate.RawSubject) && cert.PublicKey.(pubKeyEqual).Equal(candidate.PublicKey) {
return
}
}
@@ -877,14 +889,8 @@ func (c *Certificate) buildChains(cache map[*Certificate][][]*Certificate, curre
case rootCertificate:
chains = append(chains, appendToFreshChain(currentChain, candidate))
case intermediateCertificate:
- if cache == nil {
- cache = make(map[*Certificate][][]*Certificate)
- }
- childChains, ok := cache[candidate]
- if !ok {
- childChains, err = candidate.buildChains(cache, appendToFreshChain(currentChain, candidate), sigChecks, opts)
- cache[candidate] = childChains
- }
+ var childChains [][]*Certificate
+ childChains, err = candidate.buildChains(appendToFreshChain(currentChain, candidate), sigChecks, opts)
chains = append(chains, childChains...)
}
}
diff --git a/src/crypto/x509/verify_test.go b/src/crypto/x509/verify_test.go
index 100a8ff0f94..1b2cbe34dd2 100644
--- a/src/crypto/x509/verify_test.go
+++ b/src/crypto/x509/verify_test.go
@@ -15,7 +15,9 @@ import (
"fmt"
"internal/testenv"
"math/big"
+ "reflect"
"runtime"
+ "sort"
"strings"
"testing"
"time"
@@ -1910,3 +1912,450 @@ func TestIssue51759(t *testing.T) {
}
})
}
+
+type trustGraphEdge struct {
+ Issuer string
+ Subject string
+ Type int
+ MutateTemplate func(*Certificate)
+}
+
+type trustGraphDescription struct {
+ Roots []string
+ Leaf string
+ Graph []trustGraphEdge
+}
+
+func genCertEdge(t *testing.T, subject string, key crypto.Signer, mutateTmpl func(*Certificate), certType int, issuer *Certificate, signer crypto.Signer) *Certificate {
+ t.Helper()
+
+ serial, err := rand.Int(rand.Reader, big.NewInt(100))
+ if err != nil {
+ t.Fatalf("failed to generate test serial: %s", err)
+ }
+ tmpl := &Certificate{
+ SerialNumber: serial,
+ Subject: pkix.Name{CommonName: subject},
+ NotBefore: time.Now().Add(-time.Hour),
+ NotAfter: time.Now().Add(time.Hour),
+ }
+ if certType == rootCertificate || certType == intermediateCertificate {
+ tmpl.IsCA, tmpl.BasicConstraintsValid = true, true
+ tmpl.KeyUsage = KeyUsageCertSign
+ } else if certType == leafCertificate {
+ tmpl.DNSNames = []string{"localhost"}
+ }
+ if mutateTmpl != nil {
+ mutateTmpl(tmpl)
+ }
+
+ if certType == rootCertificate {
+ issuer = tmpl
+ signer = key
+ }
+
+ d, err := CreateCertificate(rand.Reader, tmpl, issuer, key.Public(), signer)
+ if err != nil {
+ t.Fatalf("failed to generate test cert: %s", err)
+ }
+ c, err := ParseCertificate(d)
+ if err != nil {
+ t.Fatalf("failed to parse test cert: %s", err)
+ }
+ return c
+}
+
+func buildTrustGraph(t *testing.T, d trustGraphDescription) (*CertPool, *CertPool, *Certificate) {
+ t.Helper()
+
+ certs := map[string]*Certificate{}
+ keys := map[string]crypto.Signer{}
+ roots := []*Certificate{}
+ for _, r := range d.Roots {
+ k, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+ if err != nil {
+ t.Fatalf("failed to generate test key: %s", err)
+ }
+ root := genCertEdge(t, r, k, nil, rootCertificate, nil, nil)
+ roots = append(roots, root)
+ certs[r] = root
+ keys[r] = k
+ }
+
+ intermediates := []*Certificate{}
+ var leaf *Certificate
+ for _, e := range d.Graph {
+ issuerCert, ok := certs[e.Issuer]
+ if !ok {
+ t.Fatalf("unknown issuer %s", e.Issuer)
+ }
+ issuerKey, ok := keys[e.Issuer]
+ if !ok {
+ t.Fatalf("unknown issuer %s", e.Issuer)
+ }
+
+ k, ok := keys[e.Subject]
+ if !ok {
+ var err error
+ k, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+ if err != nil {
+ t.Fatalf("failed to generate test key: %s", err)
+ }
+ keys[e.Subject] = k
+ }
+ cert := genCertEdge(t, e.Subject, k, e.MutateTemplate, e.Type, issuerCert, issuerKey)
+ certs[e.Subject] = cert
+ if e.Subject == d.Leaf {
+ leaf = cert
+ } else {
+ intermediates = append(intermediates, cert)
+ }
+ }
+
+ rootPool, intermediatePool := NewCertPool(), NewCertPool()
+ for i := len(roots) - 1; i >= 0; i-- {
+ rootPool.AddCert(roots[i])
+ }
+ for i := len(intermediates) - 1; i >= 0; i-- {
+ intermediatePool.AddCert(intermediates[i])
+ }
+
+ return rootPool, intermediatePool, leaf
+}
+
+func chainsToStrings(chains [][]*Certificate) []string {
+ chainStrings := []string{}
+ for _, chain := range chains {
+ names := []string{}
+ for _, c := range chain {
+ names = append(names, c.Subject.String())
+ }
+ chainStrings = append(chainStrings, strings.Join(names, " -> "))
+ }
+ sort.Strings(chainStrings)
+ return chainStrings
+}
+
+func TestPathBuilding(t *testing.T) {
+ tests := []struct {
+ name string
+ graph trustGraphDescription
+ expectedChains []string
+ expectedErr string
+ }{
+ {
+ // Build the following graph from RFC 4158, figure 7 (note that in this graph edges represent
+ // certificates where the parent is the issuer and the child is the subject.) For the certificate
+ // C->B, use an unsupported ExtKeyUsage (in this case ExtKeyUsageCodeSigning) which invalidates
+ // the path Trust Anchor -> C -> B -> EE. The remaining valid paths should be:
+ // * Trust Anchor -> A -> B -> EE
+ // * Trust Anchor -> C -> A -> B -> EE
+ //
+ // +---------+
+ // | Trust |
+ // | Anchor |
+ // +---------+
+ // | |
+ // v v
+ // +---+ +---+
+ // | A |<-->| C |
+ // +---+ +---+
+ // | |
+ // | +---+ |
+ // +->| B |<-+
+ // +---+
+ // |
+ // v
+ // +----+
+ // | EE |
+ // +----+
+ name: "bad EKU",
+ graph: trustGraphDescription{
+ Roots: []string{"root"},
+ Leaf: "leaf",
+ Graph: []trustGraphEdge{
+ {
+ Issuer: "root",
+ Subject: "inter a",
+ Type: intermediateCertificate,
+ },
+ {
+ Issuer: "root",
+ Subject: "inter c",
+ Type: intermediateCertificate,
+ },
+ {
+ Issuer: "inter c",
+ Subject: "inter a",
+ Type: intermediateCertificate,
+ },
+ {
+ Issuer: "inter a",
+ Subject: "inter c",
+ Type: intermediateCertificate,
+ },
+ {
+ Issuer: "inter c",
+ Subject: "inter b",
+ Type: intermediateCertificate,
+ MutateTemplate: func(t *Certificate) {
+ t.ExtKeyUsage = []ExtKeyUsage{ExtKeyUsageCodeSigning}
+ },
+ },
+ {
+ Issuer: "inter a",
+ Subject: "inter b",
+ Type: intermediateCertificate,
+ },
+ {
+ Issuer: "inter b",
+ Subject: "leaf",
+ Type: leafCertificate,
+ },
+ },
+ },
+ expectedChains: []string{
+ "CN=leaf -> CN=inter b -> CN=inter a -> CN=inter c -> CN=root",
+ "CN=leaf -> CN=inter b -> CN=inter a -> CN=root",
+ },
+ },
+ {
+ // Build the following graph from RFC 4158, figure 7 (note that in this graph edges represent
+ // certificates where the parent is the issuer and the child is the subject.) For the certificate
+ // C->B, use a unconstrained SAN which invalidates the path Trust Anchor -> C -> B -> EE. The
+ // remaining valid paths should be:
+ // * Trust Anchor -> A -> B -> EE
+ // * Trust Anchor -> C -> A -> B -> EE
+ //
+ // +---------+
+ // | Trust |
+ // | Anchor |
+ // +---------+
+ // | |
+ // v v
+ // +---+ +---+
+ // | A |<-->| C |
+ // +---+ +---+
+ // | |
+ // | +---+ |
+ // +->| B |<-+
+ // +---+
+ // |
+ // v
+ // +----+
+ // | EE |
+ // +----+
+ name: "bad EKU",
+ graph: trustGraphDescription{
+ Roots: []string{"root"},
+ Leaf: "leaf",
+ Graph: []trustGraphEdge{
+ {
+ Issuer: "root",
+ Subject: "inter a",
+ Type: intermediateCertificate,
+ },
+ {
+ Issuer: "root",
+ Subject: "inter c",
+ Type: intermediateCertificate,
+ },
+ {
+ Issuer: "inter c",
+ Subject: "inter a",
+ Type: intermediateCertificate,
+ },
+ {
+ Issuer: "inter a",
+ Subject: "inter c",
+ Type: intermediateCertificate,
+ },
+ {
+ Issuer: "inter c",
+ Subject: "inter b",
+ Type: intermediateCertificate,
+ MutateTemplate: func(t *Certificate) {
+ t.PermittedDNSDomains = []string{"good"}
+ t.DNSNames = []string{"bad"}
+ },
+ },
+ {
+ Issuer: "inter a",
+ Subject: "inter b",
+ Type: intermediateCertificate,
+ },
+ {
+ Issuer: "inter b",
+ Subject: "leaf",
+ Type: leafCertificate,
+ },
+ },
+ },
+ expectedChains: []string{
+ "CN=leaf -> CN=inter b -> CN=inter a -> CN=inter c -> CN=root",
+ "CN=leaf -> CN=inter b -> CN=inter a -> CN=root",
+ },
+ },
+ {
+ // Build the following graph, we should find both paths:
+ // * Trust Anchor -> A -> C -> EE
+ // * Trust Anchor -> A -> B -> C -> EE
+ //
+ // +---------+
+ // | Trust |
+ // | Anchor |
+ // +---------+
+ // |
+ // v
+ // +---+
+ // | A |
+ // +---+
+ // | |
+ // | +----+
+ // | v
+ // | +---+
+ // | | B |
+ // | +---+
+ // | |
+ // | +---v
+ // v v
+ // +---+
+ // | C |
+ // +---+
+ // |
+ // v
+ // +----+
+ // | EE |
+ // +----+
+ name: "all paths",
+ graph: trustGraphDescription{
+ Roots: []string{"root"},
+ Leaf: "leaf",
+ Graph: []trustGraphEdge{
+ {
+ Issuer: "root",
+ Subject: "inter a",
+ Type: intermediateCertificate,
+ },
+ {
+ Issuer: "inter a",
+ Subject: "inter b",
+ Type: intermediateCertificate,
+ },
+ {
+ Issuer: "inter a",
+ Subject: "inter c",
+ Type: intermediateCertificate,
+ },
+ {
+ Issuer: "inter b",
+ Subject: "inter c",
+ Type: intermediateCertificate,
+ },
+ {
+ Issuer: "inter c",
+ Subject: "leaf",
+ Type: leafCertificate,
+ },
+ },
+ },
+ expectedChains: []string{
+ "CN=leaf -> CN=inter c -> CN=inter a -> CN=root",
+ "CN=leaf -> CN=inter c -> CN=inter b -> CN=inter a -> CN=root",
+ },
+ },
+ {
+ // Build the following graph, which contains a cross-signature loop
+ // (A and C cross sign each other). Paths that include the A -> C -> A
+ // (and vice versa) loop should be ignored, resulting in the paths:
+ // * Trust Anchor -> A -> B -> EE
+ // * Trust Anchor -> C -> B -> EE
+ // * Trust Anchor -> A -> C -> B -> EE
+ // * Trust Anchor -> C -> A -> B -> EE
+ //
+ // +---------+
+ // | Trust |
+ // | Anchor |
+ // +---------+
+ // | |
+ // v v
+ // +---+ +---+
+ // | A |<-->| C |
+ // +---+ +---+
+ // | |
+ // | +---+ |
+ // +->| B |<-+
+ // +---+
+ // |
+ // v
+ // +----+
+ // | EE |
+ // +----+
+ name: "ignore cross-sig loops",
+ graph: trustGraphDescription{
+ Roots: []string{"root"},
+ Leaf: "leaf",
+ Graph: []trustGraphEdge{
+ {
+ Issuer: "root",
+ Subject: "inter a",
+ Type: intermediateCertificate,
+ },
+ {
+ Issuer: "root",
+ Subject: "inter c",
+ Type: intermediateCertificate,
+ },
+ {
+ Issuer: "inter c",
+ Subject: "inter a",
+ Type: intermediateCertificate,
+ },
+ {
+ Issuer: "inter a",
+ Subject: "inter c",
+ Type: intermediateCertificate,
+ },
+ {
+ Issuer: "inter c",
+ Subject: "inter b",
+ Type: intermediateCertificate,
+ },
+ {
+ Issuer: "inter a",
+ Subject: "inter b",
+ Type: intermediateCertificate,
+ },
+ {
+ Issuer: "inter b",
+ Subject: "leaf",
+ Type: leafCertificate,
+ },
+ },
+ },
+ expectedChains: []string{
+ "CN=leaf -> CN=inter b -> CN=inter a -> CN=inter c -> CN=root",
+ "CN=leaf -> CN=inter b -> CN=inter a -> CN=root",
+ "CN=leaf -> CN=inter b -> CN=inter c -> CN=inter a -> CN=root",
+ "CN=leaf -> CN=inter b -> CN=inter c -> CN=root",
+ },
+ },
+ }
+
+ for _, tc := range tests {
+ t.Run(tc.name, func(t *testing.T) {
+ roots, intermediates, leaf := buildTrustGraph(t, tc.graph)
+ chains, err := leaf.Verify(VerifyOptions{
+ Roots: roots,
+ Intermediates: intermediates,
+ })
+ if err != nil && err.Error() != tc.expectedErr {
+ t.Fatalf("unexpected error: got %q, want %q", err, tc.expectedErr)
+ }
+ gotChains := chainsToStrings(chains)
+ if !reflect.DeepEqual(gotChains, tc.expectedChains) {
+ t.Errorf("unexpected chains returned:\ngot:\n\t%s\nwant:\n\t%s", strings.Join(gotChains, "\n\t"), strings.Join(tc.expectedChains, "\n\t"))
+ }
+ })
+ }
+}
diff --git a/src/crypto/x509/x509.go b/src/crypto/x509/x509.go
index cb43079a9c7..ceb04ae20e9 100644
--- a/src/crypto/x509/x509.go
+++ b/src/crypto/x509/x509.go
@@ -155,7 +155,7 @@ type tbsCertificate struct {
PublicKey publicKeyInfo
UniqueId asn1.BitString `asn1:"optional,tag:1"`
SubjectUniqueId asn1.BitString `asn1:"optional,tag:2"`
- Extensions []pkix.Extension `asn1:"optional,explicit,tag:3"`
+ Extensions []pkix.Extension `asn1:"omitempty,optional,explicit,tag:3"`
}
type dsaAlgorithmParameters struct {
@@ -247,7 +247,6 @@ func (algo PublicKeyAlgorithm) String() string {
// pkcs-1 OBJECT IDENTIFIER ::= {
// iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 1 }
//
-//
// RFC 3279 2.2.1 RSA Signature Algorithms
//
// md2WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 2 }
@@ -273,7 +272,6 @@ func (algo PublicKeyAlgorithm) String() string {
//
// sha512WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 13 }
//
-//
// RFC 5758 3.1 DSA Signature Algorithms
//
// dsaWithSha256 OBJECT IDENTIFIER ::= {
@@ -351,10 +349,10 @@ var signatureAlgorithmDetails = []struct {
// hashToPSSParameters contains the DER encoded RSA PSS parameters for the
// SHA256, SHA384, and SHA512 hashes as defined in RFC 3447, Appendix A.2.3.
// The parameters contain the following values:
-// * hashAlgorithm contains the associated hash identifier with NULL parameters
-// * maskGenAlgorithm always contains the default mgf1SHA1 identifier
-// * saltLength contains the length of the associated hash
-// * trailerField always contains the default trailerFieldBC value
+// - hashAlgorithm contains the associated hash identifier with NULL parameters
+// - maskGenAlgorithm always contains the default mgf1SHA1 identifier
+// - saltLength contains the length of the associated hash
+// - trailerField always contains the default trailerFieldBC value
var hashToPSSParameters = map[crypto.Hash]asn1.RawValue{
crypto.SHA256: asn1.RawValue{FullBytes: []byte{48, 52, 160, 15, 48, 13, 6, 9, 96, 134, 72, 1, 101, 3, 4, 2, 1, 5, 0, 161, 28, 48, 26, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 8, 48, 13, 6, 9, 96, 134, 72, 1, 101, 3, 4, 2, 1, 5, 0, 162, 3, 2, 1, 32}},
crypto.SHA384: asn1.RawValue{FullBytes: []byte{48, 52, 160, 15, 48, 13, 6, 9, 96, 134, 72, 1, 101, 3, 4, 2, 2, 5, 0, 161, 28, 48, 26, 6, 9, 42, 134, 72, 134, 247, 13, 1, 1, 8, 48, 13, 6, 9, 96, 134, 72, 1, 101, 3, 4, 2, 2, 5, 0, 162, 3, 2, 1, 48}},
@@ -466,18 +464,18 @@ func getPublicKeyAlgorithmFromOID(oid asn1.ObjectIdentifier) PublicKeyAlgorithm
// RFC 5480, 2.1.1.1. Named Curve
//
-// secp224r1 OBJECT IDENTIFIER ::= {
-// iso(1) identified-organization(3) certicom(132) curve(0) 33 }
+// secp224r1 OBJECT IDENTIFIER ::= {
+// iso(1) identified-organization(3) certicom(132) curve(0) 33 }
//
-// secp256r1 OBJECT IDENTIFIER ::= {
-// iso(1) member-body(2) us(840) ansi-X9-62(10045) curves(3)
-// prime(1) 7 }
+// secp256r1 OBJECT IDENTIFIER ::= {
+// iso(1) member-body(2) us(840) ansi-X9-62(10045) curves(3)
+// prime(1) 7 }
//
-// secp384r1 OBJECT IDENTIFIER ::= {
-// iso(1) identified-organization(3) certicom(132) curve(0) 34 }
+// secp384r1 OBJECT IDENTIFIER ::= {
+// iso(1) identified-organization(3) certicom(132) curve(0) 34 }
//
-// secp521r1 OBJECT IDENTIFIER ::= {
-// iso(1) identified-organization(3) certicom(132) curve(0) 35 }
+// secp521r1 OBJECT IDENTIFIER ::= {
+// iso(1) identified-organization(3) certicom(132) curve(0) 35 }
//
// NB: secp256r1 is equivalent to prime256v1
var (
@@ -735,7 +733,7 @@ var debugAllowSHA1 = godebug.Get("x509sha1") == "1"
//
// To temporarily restore support for SHA-1 signatures, include the value
// "x509sha1=1" in the GODEBUG environment variable. Note that this option will
-// be removed in Go 1.19.
+// be removed in a future release.
type InsecureAlgorithmError SignatureAlgorithm
func (e InsecureAlgorithmError) Error() string {
@@ -880,6 +878,8 @@ func checkSignature(algo SignatureAlgorithm, signed, signature []byte, publicKey
}
// CheckCRLSignature checks that the signature in crl is from c.
+//
+// Deprecated: Use RevocationList.CheckSignatureFrom instead.
func (c *Certificate) CheckCRLSignature(crl *pkix.CertificateList) error {
algo := getSignatureAlgorithmFromAI(crl.SignatureAlgorithm)
return c.CheckSignature(algo, crl.TBSCertList.Raw, crl.SignatureValue.RightAlign())
@@ -1419,38 +1419,38 @@ var emptyASN1Subject = []byte{0x30, 0}
// CreateCertificate creates a new X.509 v3 certificate based on a template.
// The following members of template are currently used:
//
-// - AuthorityKeyId
-// - BasicConstraintsValid
-// - CRLDistributionPoints
-// - DNSNames
-// - EmailAddresses
-// - ExcludedDNSDomains
-// - ExcludedEmailAddresses
-// - ExcludedIPRanges
-// - ExcludedURIDomains
-// - ExtKeyUsage
-// - ExtraExtensions
-// - IPAddresses
-// - IsCA
-// - IssuingCertificateURL
-// - KeyUsage
-// - MaxPathLen
-// - MaxPathLenZero
-// - NotAfter
-// - NotBefore
-// - OCSPServer
-// - PermittedDNSDomains
-// - PermittedDNSDomainsCritical
-// - PermittedEmailAddresses
-// - PermittedIPRanges
-// - PermittedURIDomains
-// - PolicyIdentifiers
-// - SerialNumber
-// - SignatureAlgorithm
-// - Subject
-// - SubjectKeyId
-// - URIs
-// - UnknownExtKeyUsage
+// - AuthorityKeyId
+// - BasicConstraintsValid
+// - CRLDistributionPoints
+// - DNSNames
+// - EmailAddresses
+// - ExcludedDNSDomains
+// - ExcludedEmailAddresses
+// - ExcludedIPRanges
+// - ExcludedURIDomains
+// - ExtKeyUsage
+// - ExtraExtensions
+// - IPAddresses
+// - IsCA
+// - IssuingCertificateURL
+// - KeyUsage
+// - MaxPathLen
+// - MaxPathLenZero
+// - NotAfter
+// - NotBefore
+// - OCSPServer
+// - PermittedDNSDomains
+// - PermittedDNSDomainsCritical
+// - PermittedEmailAddresses
+// - PermittedIPRanges
+// - PermittedURIDomains
+// - PolicyIdentifiers
+// - SerialNumber
+// - SignatureAlgorithm
+// - Subject
+// - SubjectKeyId
+// - URIs
+// - UnknownExtKeyUsage
//
// The certificate is signed by parent. If parent is equal to template then the
// certificate is self-signed. The parameter pub is the public key of the
@@ -1478,6 +1478,22 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub, priv
return nil, errors.New("x509: no SerialNumber given")
}
+ // RFC 5280 Section 4.1.2.2: serial number must positive and should not be longer
+ // than 20 octets.
+ //
+ // We cannot simply check for len(serialBytes) > 20, because encoding/asn1 may
+ // pad the slice in order to prevent the integer being mistaken for a negative
+ // number (DER uses the high bit of the left-most byte to indicate the sign.),
+ // so we need to double check the composition of the serial if it is exactly
+ // 20 bytes.
+ if template.SerialNumber.Sign() == -1 {
+ return nil, errors.New("x509: serial number must be positive")
+ }
+ serialBytes := template.SerialNumber.Bytes()
+ if len(serialBytes) > 20 || (len(serialBytes) == 20 && serialBytes[0]&0x80 != 0) {
+ return nil, errors.New("x509: serial number exceeds 20 octets")
+ }
+
if template.BasicConstraintsValid && !template.IsCA && template.MaxPathLen != -1 && (template.MaxPathLen != 0 || template.MaxPathLenZero) {
return nil, errors.New("x509: only CAs are allowed to specify MaxPathLen")
}
@@ -1607,6 +1623,8 @@ var pemType = "X509 CRL"
// encoded CRLs will appear where they should be DER encoded, so this function
// will transparently handle PEM encoding as long as there isn't any leading
// garbage.
+//
+// Deprecated: Use ParseRevocationList instead.
func ParseCRL(crlBytes []byte) (*pkix.CertificateList, error) {
if bytes.HasPrefix(crlBytes, pemCRLPrefix) {
block, _ := pem.Decode(crlBytes)
@@ -1618,6 +1636,8 @@ func ParseCRL(crlBytes []byte) (*pkix.CertificateList, error) {
}
// ParseDERCRL parses a DER encoded CRL from the given bytes.
+//
+// Deprecated: Use ParseRevocationList instead.
func ParseDERCRL(derBytes []byte) (*pkix.CertificateList, error) {
certList := new(pkix.CertificateList)
if rest, err := asn1.Unmarshal(derBytes, certList); err != nil {
@@ -1631,7 +1651,7 @@ func ParseDERCRL(derBytes []byte) (*pkix.CertificateList, error) {
// CreateCRL returns a DER encoded CRL, signed by this Certificate, that
// contains the given list of revoked certificates.
//
-// Note: this method does not generate an RFC 5280 conformant X.509 v2 CRL.
+// Deprecated: this method does not generate an RFC 5280 conformant X.509 v2 CRL.
// To generate a standards compliant CRL, use CreateRevocationList instead.
func (c *Certificate) CreateCRL(rand io.Reader, priv any, revokedCerts []pkix.RevokedCertificate, now, expiry time.Time) (crlBytes []byte, err error) {
key, ok := priv.(crypto.Signer)
@@ -1805,12 +1825,18 @@ func parseCSRExtensions(rawAttributes []asn1.RawValue) ([]pkix.Extension, error)
}
var ret []pkix.Extension
+ seenExts := make(map[string]bool)
for _, rawAttr := range rawAttributes {
var attr pkcs10Attribute
if rest, err := asn1.Unmarshal(rawAttr.FullBytes, &attr); err != nil || len(rest) != 0 || len(attr.Values) == 0 {
// Ignore attributes that don't parse.
continue
}
+ oidStr := attr.Id.String()
+ if seenExts[oidStr] {
+ return nil, errors.New("x509: certificate request contains duplicate extensions")
+ }
+ seenExts[oidStr] = true
if !attr.Id.Equal(oidExtensionRequest) {
continue
@@ -1820,6 +1846,14 @@ func parseCSRExtensions(rawAttributes []asn1.RawValue) ([]pkix.Extension, error)
if _, err := asn1.Unmarshal(attr.Values[0].FullBytes, &extensions); err != nil {
return nil, err
}
+ requestedExts := make(map[string]bool)
+ for _, ext := range extensions {
+ oidStr := ext.Id.String()
+ if requestedExts[oidStr] {
+ return nil, errors.New("x509: certificate request contains duplicate requested extensions")
+ }
+ requestedExts[oidStr] = true
+ }
ret = append(ret, extensions...)
}
@@ -1829,14 +1863,14 @@ func parseCSRExtensions(rawAttributes []asn1.RawValue) ([]pkix.Extension, error)
// CreateCertificateRequest creates a new certificate request based on a
// template. The following members of template are used:
//
-// - SignatureAlgorithm
-// - Subject
-// - DNSNames
-// - EmailAddresses
-// - IPAddresses
-// - URIs
-// - ExtraExtensions
-// - Attributes (deprecated)
+// - SignatureAlgorithm
+// - Subject
+// - DNSNames
+// - EmailAddresses
+// - IPAddresses
+// - URIs
+// - ExtraExtensions
+// - Attributes (deprecated)
//
// priv is the private key to sign the CSR with, and the corresponding public
// key will be included in the CSR. It must implement crypto.Signer and its
@@ -2073,6 +2107,14 @@ func (c *CertificateRequest) CheckSignature() error {
// RevocationList contains the fields used to create an X.509 v2 Certificate
// Revocation list with CreateRevocationList.
type RevocationList struct {
+ Raw []byte
+ RawTBSRevocationList []byte
+ RawIssuer []byte
+
+ Issuer pkix.Name
+ AuthorityKeyId []byte
+
+ Signature []byte
// SignatureAlgorithm is used to determine the signature algorithm to be
// used when signing the CRL. If 0 the default algorithm for the signing
// key will be used.
@@ -2087,6 +2129,7 @@ type RevocationList struct {
// which should be a monotonically increasing sequence number for a given
// CRL scope and CRL issuer.
Number *big.Int
+
// ThisUpdate is used to populate the thisUpdate field in the CRL, which
// indicates the issuance date of the CRL.
ThisUpdate time.Time
@@ -2094,6 +2137,11 @@ type RevocationList struct {
// indicates the date by which the next CRL will be issued. NextUpdate
// must be greater than ThisUpdate.
NextUpdate time.Time
+
+ // Extensions contains raw X.509 extensions. When creating a CRL,
+ // the Extensions field is ignored, see ExtraExtensions.
+ Extensions []pkix.Extension
+
// ExtraExtensions contains any additional extensions to add directly to
// the CRL.
ExtraExtensions []pkix.Extension
@@ -2207,3 +2255,22 @@ func CreateRevocationList(rand io.Reader, template *RevocationList, issuer *Cert
SignatureValue: asn1.BitString{Bytes: signature, BitLength: len(signature) * 8},
})
}
+
+// CheckSignatureFrom verifies that the signature on rl is a valid signature
+// from issuer.
+func (rl *RevocationList) CheckSignatureFrom(parent *Certificate) error {
+ if parent.Version == 3 && !parent.BasicConstraintsValid ||
+ parent.BasicConstraintsValid && !parent.IsCA {
+ return ConstraintViolationError{}
+ }
+
+ if parent.KeyUsage != 0 && parent.KeyUsage&KeyUsageCRLSign == 0 {
+ return ConstraintViolationError{}
+ }
+
+ if parent.PublicKeyAlgorithm == UnknownPublicKeyAlgorithm {
+ return ErrUnsupportedAlgorithm
+ }
+
+ return parent.CheckSignature(rl.SignatureAlgorithm, rl.RawTBSRevocationList, rl.Signature)
+}
diff --git a/src/crypto/x509/x509_test.go b/src/crypto/x509/x509_test.go
index d2889fc1d7b..486d6bf3d23 100644
--- a/src/crypto/x509/x509_test.go
+++ b/src/crypto/x509/x509_test.go
@@ -602,11 +602,7 @@ func TestCreateSelfSignedCertificate(t *testing.T) {
for _, test := range tests {
commonName := "test.example.com"
template := Certificate{
- // SerialNumber is negative to ensure that negative
- // values are parsed. This is due to the prevalence of
- // buggy code that produces certificates with negative
- // serial numbers.
- SerialNumber: big.NewInt(-1),
+ SerialNumber: big.NewInt(1),
Subject: pkix.Name{
CommonName: commonName,
Organization: []string{"Σ Acme Co"},
@@ -1798,16 +1794,18 @@ func TestInsecureAlgorithmErrorString(t *testing.T) {
}
// These CSR was generated with OpenSSL:
-// openssl req -out CSR.csr -new -sha256 -nodes -keyout privateKey.key -config openssl.cnf
+//
+// openssl req -out CSR.csr -new -sha256 -nodes -keyout privateKey.key -config openssl.cnf
//
// With openssl.cnf containing the following sections:
-// [ v3_req ]
-// basicConstraints = CA:FALSE
-// keyUsage = nonRepudiation, digitalSignature, keyEncipherment
-// subjectAltName = email:gopher@golang.org,DNS:test.example.com
-// [ req_attributes ]
-// challengePassword = ignored challenge
-// unstructuredName = ignored unstructured name
+//
+// [ v3_req ]
+// basicConstraints = CA:FALSE
+// keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+// subjectAltName = email:gopher@golang.org,DNS:test.example.com
+// [ req_attributes ]
+// challengePassword = ignored challenge
+// unstructuredName = ignored unstructured name
var csrBase64Array = [...]string{
// Just [ v3_req ]
"MIIDHDCCAgQCAQAwfjELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEUMBIGA1UEAwwLQ29tbW9uIE5hbWUxITAfBgkqhkiG9w0BCQEWEnRlc3RAZW1haWwuYWRkcmVzczCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK1GY4YFx2ujlZEOJxQVYmsjUnLsd5nFVnNpLE4cV+77sgv9NPNlB8uhn3MXt5leD34rm/2BisCHOifPucYlSrszo2beuKhvwn4+2FxDmWtBEMu/QA16L5IvoOfYZm/gJTsPwKDqvaR0tTU67a9OtxwNTBMI56YKtmwd/o8d3hYv9cg+9ZGAZ/gKONcg/OWYx/XRh6bd0g8DMbCikpWgXKDsvvK1Nk+VtkDO1JxuBaj4Lz/p/MifTfnHoqHxWOWl4EaTs4Ychxsv34/rSj1KD1tJqorIv5Xv2aqv4sjxfbrYzX4kvS5SC1goIovLnhj5UjmQ3Qy8u65eow/LLWw+YFcCAwEAAaBZMFcGCSqGSIb3DQEJDjFKMEgwCQYDVR0TBAIwADALBgNVHQ8EBAMCBeAwLgYDVR0RBCcwJYERZ29waGVyQGdvbGFuZy5vcmeCEHRlc3QuZXhhbXBsZS5jb20wDQYJKoZIhvcNAQELBQADggEBAB6VPMRrchvNW61Tokyq3ZvO6/NoGIbuwUn54q6l5VZW0Ep5Nq8juhegSSnaJ0jrovmUgKDN9vEo2KxuAtwG6udS6Ami3zP+hRd4k9Q8djJPb78nrjzWiindLK5Fps9U5mMoi1ER8ViveyAOTfnZt/jsKUaRsscY2FzE9t9/o5moE6LTcHUS4Ap1eheR+J72WOnQYn3cifYaemsA9MJuLko+kQ6xseqttbh9zjqd9fiCSh/LNkzos9c+mg2yMADitaZinAh+HZi50ooEbjaT3erNq9O6RqwJlgD00g6MQdoz9bTAryCUhCQfkIaepmQ7BxS0pqWNW3MMwfDwx/Snz6g=",
@@ -2631,24 +2629,24 @@ func TestCreateRevocationList(t *testing.T) {
return
}
- parsedCRL, err := ParseDERCRL(crl)
+ parsedCRL, err := ParseRevocationList(crl)
if err != nil {
t.Fatalf("Failed to parse generated CRL: %s", err)
}
if tc.template.SignatureAlgorithm != UnknownSignatureAlgorithm &&
- parsedCRL.SignatureAlgorithm.Algorithm.Equal(signatureAlgorithmDetails[tc.template.SignatureAlgorithm].oid) {
+ parsedCRL.SignatureAlgorithm != tc.template.SignatureAlgorithm {
t.Fatalf("SignatureAlgorithm mismatch: got %v; want %v.", parsedCRL.SignatureAlgorithm,
tc.template.SignatureAlgorithm)
}
- if !reflect.DeepEqual(parsedCRL.TBSCertList.RevokedCertificates, tc.template.RevokedCertificates) {
+ if !reflect.DeepEqual(parsedCRL.RevokedCertificates, tc.template.RevokedCertificates) {
t.Fatalf("RevokedCertificates mismatch: got %v; want %v.",
- parsedCRL.TBSCertList.RevokedCertificates, tc.template.RevokedCertificates)
+ parsedCRL.RevokedCertificates, tc.template.RevokedCertificates)
}
- if len(parsedCRL.TBSCertList.Extensions) != 2+len(tc.template.ExtraExtensions) {
- t.Fatalf("Generated CRL has wrong number of extensions, wanted: %d, got: %d", 2+len(tc.template.ExtraExtensions), len(parsedCRL.TBSCertList.Extensions))
+ if len(parsedCRL.Extensions) != 2+len(tc.template.ExtraExtensions) {
+ t.Fatalf("Generated CRL has wrong number of extensions, wanted: %d, got: %d", 2+len(tc.template.ExtraExtensions), len(parsedCRL.Extensions))
}
expectedAKI, err := asn1.Marshal(authKeyId{Id: tc.issuer.SubjectKeyId})
if err != nil {
@@ -2658,9 +2656,9 @@ func TestCreateRevocationList(t *testing.T) {
Id: oidExtensionAuthorityKeyId,
Value: expectedAKI,
}
- if !reflect.DeepEqual(parsedCRL.TBSCertList.Extensions[0], akiExt) {
+ if !reflect.DeepEqual(parsedCRL.Extensions[0], akiExt) {
t.Fatalf("Unexpected first extension: got %v, want %v",
- parsedCRL.TBSCertList.Extensions[0], akiExt)
+ parsedCRL.Extensions[0], akiExt)
}
expectedNum, err := asn1.Marshal(tc.template.Number)
if err != nil {
@@ -2670,18 +2668,18 @@ func TestCreateRevocationList(t *testing.T) {
Id: oidExtensionCRLNumber,
Value: expectedNum,
}
- if !reflect.DeepEqual(parsedCRL.TBSCertList.Extensions[1], crlExt) {
+ if !reflect.DeepEqual(parsedCRL.Extensions[1], crlExt) {
t.Fatalf("Unexpected second extension: got %v, want %v",
- parsedCRL.TBSCertList.Extensions[1], crlExt)
+ parsedCRL.Extensions[1], crlExt)
}
- if len(parsedCRL.TBSCertList.Extensions[2:]) == 0 && len(tc.template.ExtraExtensions) == 0 {
+ if len(parsedCRL.Extensions[2:]) == 0 && len(tc.template.ExtraExtensions) == 0 {
// If we don't have anything to check return early so we don't
// hit a [] != nil false positive below.
return
}
- if !reflect.DeepEqual(parsedCRL.TBSCertList.Extensions[2:], tc.template.ExtraExtensions) {
+ if !reflect.DeepEqual(parsedCRL.Extensions[2:], tc.template.ExtraExtensions) {
t.Fatalf("Extensions mismatch: got %v; want %v.",
- parsedCRL.TBSCertList.Extensions[2:], tc.template.ExtraExtensions)
+ parsedCRL.Extensions[2:], tc.template.ExtraExtensions)
}
})
}
@@ -3444,3 +3442,268 @@ func TestDisableSHA1ForCertOnly(t *testing.T) {
t.Errorf("unexpected error: %s", err)
}
}
+
+func TestParseRevocationList(t *testing.T) {
+ derBytes := fromBase64(derCRLBase64)
+ certList, err := ParseRevocationList(derBytes)
+ if err != nil {
+ t.Errorf("error parsing: %s", err)
+ return
+ }
+ numCerts := len(certList.RevokedCertificates)
+ expected := 88
+ if numCerts != expected {
+ t.Errorf("bad number of revoked certificates. got: %d want: %d", numCerts, expected)
+ }
+}
+
+func TestRevocationListCheckSignatureFrom(t *testing.T) {
+ goodKey, err := ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
+ if err != nil {
+ t.Fatalf("failed to generate test key: %s", err)
+ }
+ badKey, err := ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
+ if err != nil {
+ t.Fatalf("failed to generate test key: %s", err)
+ }
+ tests := []struct {
+ name string
+ issuer *Certificate
+ err string
+ }{
+ {
+ name: "valid",
+ issuer: &Certificate{
+ Version: 3,
+ BasicConstraintsValid: true,
+ IsCA: true,
+ PublicKeyAlgorithm: ECDSA,
+ PublicKey: goodKey.Public(),
+ },
+ },
+ {
+ name: "valid, key usage set",
+ issuer: &Certificate{
+ Version: 3,
+ BasicConstraintsValid: true,
+ IsCA: true,
+ PublicKeyAlgorithm: ECDSA,
+ PublicKey: goodKey.Public(),
+ KeyUsage: KeyUsageCRLSign,
+ },
+ },
+ {
+ name: "invalid issuer, wrong key usage",
+ issuer: &Certificate{
+ Version: 3,
+ BasicConstraintsValid: true,
+ IsCA: true,
+ PublicKeyAlgorithm: ECDSA,
+ PublicKey: goodKey.Public(),
+ KeyUsage: KeyUsageCertSign,
+ },
+ err: "x509: invalid signature: parent certificate cannot sign this kind of certificate",
+ },
+ {
+ name: "invalid issuer, no basic constraints/ca",
+ issuer: &Certificate{
+ Version: 3,
+ PublicKeyAlgorithm: ECDSA,
+ PublicKey: goodKey.Public(),
+ },
+ err: "x509: invalid signature: parent certificate cannot sign this kind of certificate",
+ },
+ {
+ name: "invalid issuer, unsupported public key type",
+ issuer: &Certificate{
+ Version: 3,
+ BasicConstraintsValid: true,
+ IsCA: true,
+ PublicKeyAlgorithm: UnknownPublicKeyAlgorithm,
+ PublicKey: goodKey.Public(),
+ },
+ err: "x509: cannot verify signature: algorithm unimplemented",
+ },
+ {
+ name: "wrong key",
+ issuer: &Certificate{
+ Version: 3,
+ BasicConstraintsValid: true,
+ IsCA: true,
+ PublicKeyAlgorithm: ECDSA,
+ PublicKey: badKey.Public(),
+ },
+ err: "x509: ECDSA verification failure",
+ },
+ }
+
+ crlIssuer := &Certificate{
+ BasicConstraintsValid: true,
+ IsCA: true,
+ PublicKeyAlgorithm: ECDSA,
+ PublicKey: goodKey.Public(),
+ KeyUsage: KeyUsageCRLSign,
+ SubjectKeyId: []byte{1, 2, 3},
+ }
+ for _, tc := range tests {
+ t.Run(tc.name, func(t *testing.T) {
+ crlDER, err := CreateRevocationList(rand.Reader, &RevocationList{Number: big.NewInt(1)}, crlIssuer, goodKey)
+ if err != nil {
+ t.Fatalf("failed to generate CRL: %s", err)
+ }
+ crl, err := ParseRevocationList(crlDER)
+ if err != nil {
+ t.Fatalf("failed to parse test CRL: %s", err)
+ }
+ err = crl.CheckSignatureFrom(tc.issuer)
+ if err != nil && err.Error() != tc.err {
+ t.Errorf("unexpected error: got %s, want %s", err, tc.err)
+ } else if err == nil && tc.err != "" {
+ t.Errorf("CheckSignatureFrom did not fail: want %s", tc.err)
+ }
+ })
+ }
+}
+
+func TestOmitEmptyExtensions(t *testing.T) {
+ k, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+ if err != nil {
+ t.Fatal(err)
+ }
+ tmpl := &Certificate{
+ SerialNumber: big.NewInt(1),
+ Subject: pkix.Name{
+ CommonName: ":)",
+ },
+ NotAfter: time.Now().Add(time.Hour),
+ NotBefore: time.Now().Add(-time.Hour),
+ }
+ der, err := CreateCertificate(rand.Reader, tmpl, tmpl, k.Public(), k)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ emptyExtSeq := []byte{0xA3, 0x02, 0x30, 0x00}
+ if bytes.Contains(der, emptyExtSeq) {
+ t.Error("DER encoding contains the an empty extensions SEQUENCE")
+ }
+}
+
+func TestCreateCertificateLongSerial(t *testing.T) {
+ k, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ serialBytes := make([]byte, 21)
+ serialBytes[0] = 0x80
+ serialBytes[20] = 1
+ tooLong := big.NewInt(0).SetBytes(serialBytes)
+
+ tmpl := &Certificate{
+ SerialNumber: tooLong,
+ Subject: pkix.Name{
+ CommonName: ":)",
+ },
+ NotAfter: time.Now().Add(time.Hour),
+ NotBefore: time.Now().Add(-time.Hour),
+ }
+
+ expectedErr := "x509: serial number exceeds 20 octets"
+
+ _, err = CreateCertificate(rand.Reader, tmpl, tmpl, k.Public(), k)
+ if err == nil || err.Error() != expectedErr {
+ t.Errorf("CreateCertificate returned unexpected error: want %q, got %q", expectedErr, err)
+ }
+
+ serialBytes = serialBytes[:20]
+ tmpl.SerialNumber = big.NewInt(0).SetBytes(serialBytes)
+
+ _, err = CreateCertificate(rand.Reader, tmpl, tmpl, k.Public(), k)
+ if err == nil || err.Error() != expectedErr {
+ t.Errorf("CreateCertificate returned unexpected error: want %q, got %q", expectedErr, err)
+ }
+}
+
+var negativeSerialCert = `-----BEGIN CERTIFICATE-----
+MIIBBTCBraADAgECAgH/MAoGCCqGSM49BAMCMA0xCzAJBgNVBAMTAjopMB4XDTIy
+MDQxNDIzNTYwNFoXDTIyMDQxNTAxNTYwNFowDTELMAkGA1UEAxMCOikwWTATBgcq
+hkjOPQIBBggqhkjOPQMBBwNCAAQ9ezsIsj+q17K87z/PXE/rfGRN72P/Wyn5d6oo
+5M0ZbSatuntMvfKdX79CQxXAxN4oXk3Aov4jVSG12AcDI8ShMAoGCCqGSM49BAMC
+A0cAMEQCIBzfBU5eMPT6m5lsR6cXaJILpAaiD9YxOl4v6dT3rzEjAiBHmjnHmAss
+RqUAyJKFzqZxOlK2q4j2IYnuj5+LrLGbQA==
+-----END CERTIFICATE-----`
+
+func TestParseNegativeSerial(t *testing.T) {
+ pemBlock, _ := pem.Decode([]byte(negativeSerialCert))
+ _, err := ParseCertificate(pemBlock.Bytes)
+ if err != nil {
+ t.Fatalf("failed to parse certificate: %s", err)
+ }
+}
+
+func TestCreateNegativeSerial(t *testing.T) {
+ k, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+ if err != nil {
+ t.Fatal(err)
+ }
+ tmpl := &Certificate{
+ SerialNumber: big.NewInt(-1),
+ Subject: pkix.Name{
+ CommonName: ":)",
+ },
+ NotAfter: time.Now().Add(time.Hour),
+ NotBefore: time.Now().Add(-time.Hour),
+ }
+ expectedErr := "x509: serial number must be positive"
+ _, err = CreateCertificate(rand.Reader, tmpl, tmpl, k.Public(), k)
+ if err == nil || err.Error() != expectedErr {
+ t.Errorf("CreateCertificate returned unexpected error: want %q, got %q", expectedErr, err)
+ }
+}
+
+const dupExtCert = `-----BEGIN CERTIFICATE-----
+MIIBrjCCARegAwIBAgIBATANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDEwR0ZXN0
+MCIYDzAwMDEwMTAxMDAwMDAwWhgPMDAwMTAxMDEwMDAwMDBaMA8xDTALBgNVBAMT
+BHRlc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMiFchnHms9l9NninAIz
+SkY9acwl9Bk2AtmJrNCenFpiA17AcOO5q8DJYwdXi6WPKlVgcyH+ysW8XMWkq+CP
+yhtF/+LMzl9odaUF2iUy3vgTC5gxGLWH5URVssx21Und2Pm2f4xyou5IVxbS9dxy
+jLvV9PEY9BIb0H+zFthjhihDAgMBAAGjFjAUMAgGAioDBAIFADAIBgIqAwQCBQAw
+DQYJKoZIhvcNAQELBQADgYEAlhQ4TQQKIQ8GUyzGiN/75TCtQtjhMGemxc0cNgre
+d9rmm4DjydH0t7/sMCB56lQrfhJNplguzsbjFW4l245KbNKHfLiqwEGUgZjBNKur
+ot6qX/skahLtt0CNOaFIge75HVKe/69OrWQGdp18dkay/KS4Glu8YMKIjOhfrUi1
+NZA=
+-----END CERTIFICATE-----`
+
+func TestDuplicateExtensionsCert(t *testing.T) {
+ b, _ := pem.Decode([]byte(dupExtCert))
+ if b == nil {
+ t.Fatalf("couldn't decode test certificate")
+ }
+ _, err := ParseCertificate(b.Bytes)
+ if err == nil {
+ t.Fatal("ParseCertificate should fail when parsing certificate with duplicate extensions")
+ }
+}
+
+const dupExtCSR = `-----BEGIN CERTIFICATE REQUEST-----
+MIIBczCB3QIBADAPMQ0wCwYDVQQDEwR0ZXN0MIGfMA0GCSqGSIb3DQEBAQUAA4GN
+ADCBiQKBgQC5PbxMGVJ8aLF9lq/EvGObXTRMB7ieiZL9N+DJZg1n/ECCnZLIvYrr
+ZmmDV7YZsClgxKGfjJB0RQFFyZElFM9EfHEs8NJdidDKCRdIhDXQWRyhXKevHvdm
+CQNKzUeoxvdHpU/uscSkw6BgUzPyLyTx9A6ye2ix94z8Y9hGOBO2DQIDAQABoCUw
+IwYJKoZIhvcNAQkOMRYwFDAIBgIqAwQCBQAwCAYCKgMEAgUAMA0GCSqGSIb3DQEB
+CwUAA4GBAHROEsE7URk1knXmBnQtIHwoq663vlMcX3Hes58pUy020rWP8QkocA+X
+VF18/phg3p5ILlS4fcbbP2bEeV0pePo2k00FDPsJEKCBAX2LKxbU7Vp2OuV2HM2+
+VLOVx0i+/Q7fikp3hbN1JwuMTU0v2KL/IKoUcZc02+5xiYrnOIt5
+-----END CERTIFICATE REQUEST-----`
+
+func TestDuplicateExtensionsCSR(t *testing.T) {
+ b, _ := pem.Decode([]byte(dupExtCSR))
+ if b == nil {
+ t.Fatalf("couldn't decode test certificate")
+ }
+ _, err := ParseCertificateRequest(b.Bytes)
+ if err == nil {
+ t.Fatal("ParseCertificate should fail when parsing certificate with duplicate extensions")
+ }
+}
diff --git a/src/database/sql/driver/driver.go b/src/database/sql/driver/driver.go
index 5342315d124..43fa579bda5 100644
--- a/src/database/sql/driver/driver.go
+++ b/src/database/sql/driver/driver.go
@@ -47,12 +47,12 @@ import (
// It is either nil, a type handled by a database driver's NamedValueChecker
// interface, or an instance of one of these types:
//
-// int64
-// float64
-// bool
-// []byte
-// string
-// time.Time
+// int64
+// float64
+// bool
+// []byte
+// string
+// time.Time
//
// If the driver supports cursors, a returned Value may also implement the Rows interface
// in this package. This is used, for example, when a user selects a cursor
@@ -481,12 +481,13 @@ type RowsColumnTypeDatabaseTypeName interface {
// not a variable length type ok should return false.
// If length is not limited other than system limits, it should return math.MaxInt64.
// The following are examples of returned values for various types:
-// TEXT (math.MaxInt64, true)
-// varchar(10) (10, true)
-// nvarchar(10) (10, true)
-// decimal (0, false)
-// int (0, false)
-// bytea(30) (30, true)
+//
+// TEXT (math.MaxInt64, true)
+// varchar(10) (10, true)
+// nvarchar(10) (10, true)
+// decimal (0, false)
+// int (0, false)
+// bytea(30) (30, true)
type RowsColumnTypeLength interface {
Rows
ColumnTypeLength(index int) (length int64, ok bool)
@@ -504,9 +505,10 @@ type RowsColumnTypeNullable interface {
// RowsColumnTypePrecisionScale may be implemented by Rows. It should return
// the precision and scale for decimal types. If not applicable, ok should be false.
// The following are examples of returned values for various types:
-// decimal(38, 4) (38, 4, true)
-// int (0, 0, false)
-// decimal (math.MaxInt64, math.MaxInt64, true)
+//
+// decimal(38, 4) (38, 4, true)
+// int (0, 0, false)
+// decimal (math.MaxInt64, math.MaxInt64, true)
type RowsColumnTypePrecisionScale interface {
Rows
ColumnTypePrecisionScale(index int) (precision, scale int64, ok bool)
diff --git a/src/database/sql/driver/types.go b/src/database/sql/driver/types.go
index 506ce6c2cd3..fa98df7acde 100644
--- a/src/database/sql/driver/types.go
+++ b/src/database/sql/driver/types.go
@@ -17,16 +17,16 @@ import (
// driver package to provide consistent implementations of conversions
// between drivers. The ValueConverters have several uses:
//
-// * converting from the Value types as provided by the sql package
-// into a database table's specific column type and making sure it
-// fits, such as making sure a particular int64 fits in a
-// table's uint16 column.
+// - converting from the Value types as provided by the sql package
+// into a database table's specific column type and making sure it
+// fits, such as making sure a particular int64 fits in a
+// table's uint16 column.
//
-// * converting a value as given from the database into one of the
-// driver Value types.
+// - converting a value as given from the database into one of the
+// driver Value types.
//
-// * by the sql package, for converting from a driver's Value type
-// to a user's type in a scan.
+// - by the sql package, for converting from a driver's Value type
+// to a user's type in a scan.
type ValueConverter interface {
// ConvertValue converts a value to a driver Value.
ConvertValue(v any) (Value, error)
@@ -45,13 +45,13 @@ type Valuer interface {
// Bool is a ValueConverter that converts input values to bools.
//
// The conversion rules are:
-// - booleans are returned unchanged
-// - for integer types,
-// 1 is true
-// 0 is false,
-// other integers are an error
-// - for strings and []byte, same rules as strconv.ParseBool
-// - all other types are an error
+// - booleans are returned unchanged
+// - for integer types,
+// 1 is true
+// 0 is false,
+// other integers are an error
+// - for strings and []byte, same rules as strconv.ParseBool
+// - all other types are an error
var Bool boolType
type boolType struct{}
diff --git a/src/database/sql/fakedb_test.go b/src/database/sql/fakedb_test.go
index d9b40ff53f7..050aed1ec81 100644
--- a/src/database/sql/fakedb_test.go
+++ b/src/database/sql/fakedb_test.go
@@ -26,12 +26,12 @@ import (
// syntactically different and simpler than SQL. The syntax is as
// follows:
//
-// WIPE
-// CREATE|<tablename>|<col>=<type>,<col>=<type>,...
-// where types are: "string", [u]int{8,16,32,64}, "bool"
-// INSERT|<tablename>|col=val,col2=val2,col3=?
-// SELECT|<tablename>|projectcol1,projectcol2|filtercol=?,filtercol2=?
-// SELECT|<tablename>|projectcol1,projectcol2|filtercol=?param1,filtercol2=?param2
+// WIPE
+// CREATE|<tablename>|<col>=<type>,<col>=<type>,...
+// where types are: "string", [u]int{8,16,32,64}, "bool"
+// INSERT|<tablename>|col=val,col2=val2,col3=?
+// SELECT|<tablename>|projectcol1,projectcol2|filtercol=?,filtercol2=?
+// SELECT|<tablename>|projectcol1,projectcol2|filtercol=?param1,filtercol2=?param2
//
// Any of these can be preceded by PANIC|<method>|, to cause the
// named method on fakeStmt to panic.
@@ -250,10 +250,11 @@ func setHookOpenErr(fn func() error) {
}
// Supports dsn forms:
-// <dbname>
-// <dbname>;<opts> (only currently supported option is `badConn`,
-// which causes driver.ErrBadConn to be returned on
-// every other conn.Begin())
+//
+// <dbname>
+// <dbname>;<opts> (only currently supported option is `badConn`,
+// which causes driver.ErrBadConn to be returned on
+// every other conn.Begin())
func (d *fakeDriver) Open(dsn string) (driver.Conn, error) {
hookOpenErr.Lock()
fn := hookOpenErr.fn
diff --git a/src/database/sql/sql.go b/src/database/sql/sql.go
index 9a879464d8f..f408a34234a 100644
--- a/src/database/sql/sql.go
+++ b/src/database/sql/sql.go
@@ -99,14 +99,14 @@ type NamedArg struct {
//
// Example usage:
//
-// db.ExecContext(ctx, `
-// delete from Invoice
-// where
-// TimeCreated < @end
-// and TimeCreated >= @start;`,
-// sql.Named("start", startTime),
-// sql.Named("end", endTime),
-// )
+// db.ExecContext(ctx, `
+// delete from Invoice
+// where
+// TimeCreated < @end
+// and TimeCreated >= @start;`,
+// sql.Named("start", startTime),
+// sql.Named("end", endTime),
+// )
func Named(name string, value any) NamedArg {
// This method exists because the go1compat promise
// doesn't guarantee that structs don't grow more fields,
@@ -176,14 +176,14 @@ type RawBytes []byte
// NullString implements the Scanner interface so
// it can be used as a scan destination:
//
-// var s NullString
-// err := db.QueryRow("SELECT name FROM foo WHERE id=?", id).Scan(&s)
-// ...
-// if s.Valid {
-// // use s.String
-// } else {
-// // NULL value
-// }
+// var s NullString
+// err := db.QueryRow("SELECT name FROM foo WHERE id=?", id).Scan(&s)
+// ...
+// if s.Valid {
+// // use s.String
+// } else {
+// // NULL value
+// }
type NullString struct {
String string
Valid bool // Valid is true if String is not NULL
@@ -420,8 +420,8 @@ type Scanner interface {
//
// Example usage:
//
-// var outArg string
-// _, err := db.ExecContext(ctx, "ProcName", sql.Named("Arg1", sql.Out{Dest: &outArg}))
+// var outArg string
+// _, err := db.ExecContext(ctx, "ProcName", sql.Named("Arg1", sql.Out{Dest: &outArg}))
type Out struct {
_Named_Fields_Required struct{}
@@ -2378,11 +2378,12 @@ func (tx *Tx) Prepare(query string) (*Stmt, error) {
// an existing statement.
//
// Example:
-// updateMoney, err := db.Prepare("UPDATE balance SET money=money+? WHERE id=?")
-// ...
-// tx, err := db.Begin()
-// ...
-// res, err := tx.StmtContext(ctx, updateMoney).Exec(123.45, 98293203)
+//
+// updateMoney, err := db.Prepare("UPDATE balance SET money=money+? WHERE id=?")
+// ...
+// tx, err := db.Begin()
+// ...
+// res, err := tx.StmtContext(ctx, updateMoney).Exec(123.45, 98293203)
//
// The provided context is used for the preparation of the statement, not for the
// execution of the statement.
@@ -2465,11 +2466,12 @@ func (tx *Tx) StmtContext(ctx context.Context, stmt *Stmt) *Stmt {
// an existing statement.
//
// Example:
-// updateMoney, err := db.Prepare("UPDATE balance SET money=money+? WHERE id=?")
-// ...
-// tx, err := db.Begin()
-// ...
-// res, err := tx.Stmt(updateMoney).Exec(123.45, 98293203)
+//
+// updateMoney, err := db.Prepare("UPDATE balance SET money=money+? WHERE id=?")
+// ...
+// tx, err := db.Begin()
+// ...
+// res, err := tx.Stmt(updateMoney).Exec(123.45, 98293203)
//
// The returned statement operates within the transaction and will be closed
// when the transaction has been committed or rolled back.
@@ -2857,8 +2859,8 @@ func (s *Stmt) QueryRowContext(ctx context.Context, args ...any) *Row {
//
// Example usage:
//
-// var name string
-// err := nameByUseridStmt.QueryRow(id).Scan(&name)
+// var name string
+// err := nameByUseridStmt.QueryRow(id).Scan(&name)
//
// QueryRow uses context.Background internally; to specify the context, use
// QueryRowContext.
@@ -3209,16 +3211,16 @@ func rowsColumnInfoSetupConnLocked(rowsi driver.Rows) []*ColumnType {
// Scan converts columns read from the database into the following
// common Go types and special types provided by the sql package:
//
-// *string
-// *[]byte
-// *int, *int8, *int16, *int32, *int64
-// *uint, *uint8, *uint16, *uint32, *uint64
-// *bool
-// *float32, *float64
-// *interface{}
-// *RawBytes
-// *Rows (cursor value)
-// any type implementing Scanner (see Scanner docs)
+// *string
+// *[]byte
+// *int, *int8, *int16, *int32, *int64
+// *uint, *uint8, *uint16, *uint32, *uint64
+// *bool
+// *float32, *float64
+// *interface{}
+// *RawBytes
+// *Rows (cursor value)
+// any type implementing Scanner (see Scanner docs)
//
// In the most simple case, if the type of the value from the source
// column is an integer, bool or string type T and dest is of type *T,
diff --git a/src/debug/dwarf/entry.go b/src/debug/dwarf/entry.go
index 9f3f4971e18..6f80d075037 100644
--- a/src/debug/dwarf/entry.go
+++ b/src/debug/dwarf/entry.go
@@ -241,21 +241,21 @@ type Entry struct {
// A value can be one of several "attribute classes" defined by DWARF.
// The Go types corresponding to each class are:
//
-// DWARF class Go type Class
-// ----------- ------- -----
-// address uint64 ClassAddress
-// block []byte ClassBlock
-// constant int64 ClassConstant
-// flag bool ClassFlag
-// reference
-// to info dwarf.Offset ClassReference
-// to type unit uint64 ClassReferenceSig
-// string string ClassString
-// exprloc []byte ClassExprLoc
-// lineptr int64 ClassLinePtr
-// loclistptr int64 ClassLocListPtr
-// macptr int64 ClassMacPtr
-// rangelistptr int64 ClassRangeListPtr
+// DWARF class Go type Class
+// ----------- ------- -----
+// address uint64 ClassAddress
+// block []byte ClassBlock
+// constant int64 ClassConstant
+// flag bool ClassFlag
+// reference
+// to info dwarf.Offset ClassReference
+// to type unit uint64 ClassReferenceSig
+// string string ClassString
+// exprloc []byte ClassExprLoc
+// lineptr int64 ClassLinePtr
+// loclistptr int64 ClassLocListPtr
+// macptr int64 ClassMacPtr
+// rangelistptr int64 ClassRangeListPtr
//
// For unrecognized or vendor-defined attributes, Class may be
// ClassUnknown.
@@ -380,6 +380,7 @@ func (i Class) GoString() string {
//
// A common idiom is to merge the check for nil return with
// the check that the value has the expected dynamic type, as in:
+//
// v, ok := e.Val(AttrSibling).(int64)
func (e *Entry) Val(a Attr) any {
if f := e.AttrField(a); f != nil {
@@ -789,7 +790,7 @@ func (b *buf) entry(cu *Entry, atab abbrevTable, ubase Offset, vers int) *Entry
return e
}
-// A Reader allows reading Entry structures from a DWARF ``info'' section.
+// A Reader allows reading Entry structures from a DWARF “info” section.
// The Entry structures are arranged in a tree. The Reader's Next function
// return successive entries from a pre-order traversal of the tree.
// If an entry has children, its Children field will be true, and the children
@@ -806,7 +807,7 @@ type Reader struct {
}
// Reader returns a new Reader for Data.
-// The reader is positioned at byte offset 0 in the DWARF ``info'' section.
+// The reader is positioned at byte offset 0 in the DWARF “info” section.
func (d *Data) Reader() *Reader {
r := &Reader{d: d}
r.Seek(0)
diff --git a/src/debug/dwarf/line.go b/src/debug/dwarf/line.go
index c4937ca7dd8..bb281fbdd93 100644
--- a/src/debug/dwarf/line.go
+++ b/src/debug/dwarf/line.go
@@ -152,7 +152,7 @@ func (d *Data) LineReader(cu *Entry) (*LineReader, error) {
// cu has no line table.
return nil, nil
}
- if off > int64(len(d.line)) {
+ if off < 0 || off > int64(len(d.line)) {
return nil, errors.New("AttrStmtList value out of range")
}
// AttrCompDir is optional if all file names are absolute. Use
diff --git a/src/debug/dwarf/line_test.go b/src/debug/dwarf/line_test.go
index 9c6b6ff5b03..163fc3bbb99 100644
--- a/src/debug/dwarf/line_test.go
+++ b/src/debug/dwarf/line_test.go
@@ -389,3 +389,32 @@ func TestPathJoin(t *testing.T) {
}
}
}
+
+func TestPathLineReaderMalformed(t *testing.T) {
+ // This test case drawn from issue #52354. What's happening
+ // here is that the stmtList attribute in the compilation
+ // unit is malformed (negative).
+ var aranges, frame, pubnames, ranges, str []byte
+ abbrev := []byte{0x10, 0x20, 0x20, 0x20, 0x21, 0x20, 0x10, 0x21, 0x61,
+ 0x0, 0x0, 0xff, 0x20, 0xff, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20}
+ info := []byte{0x0, 0x0, 0x0, 0x9, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0,
+ 0x20, 0x10, 0x10}
+ line := []byte{0x20}
+ Data0, err := New(abbrev, aranges, frame, info, line, pubnames, ranges, str)
+ if err != nil {
+ t.Fatalf("error unexpected: %v", err)
+ }
+ Reader0 := Data0.Reader()
+ Entry0, err := Reader0.Next()
+ if err != nil {
+ t.Fatalf("error unexpected: %v", err)
+ }
+ LineReader0, err := Data0.LineReader(Entry0)
+ if err == nil {
+ t.Fatalf("expected error")
+ }
+ if LineReader0 != nil {
+ t.Fatalf("expected nil line reader")
+ }
+}
diff --git a/src/debug/dwarf/type.go b/src/debug/dwarf/type.go
index 9c15cfb9206..2049f46d708 100644
--- a/src/debug/dwarf/type.go
+++ b/src/debug/dwarf/type.go
@@ -372,7 +372,7 @@ type typeReader interface {
AddressSize() int
}
-// Type reads the type at off in the DWARF ``info'' section.
+// Type reads the type at off in the DWARF “info” section.
func (d *Data) Type(off Offset) (Type, error) {
return d.readType("info", d.Reader(), off, d.typeCache, nil)
}
diff --git a/src/debug/pe/string.go b/src/debug/pe/string.go
index cab0366ade2..6d9023d8d6f 100644
--- a/src/debug/pe/string.go
+++ b/src/debug/pe/string.go
@@ -44,8 +44,29 @@ func readStringTable(fh *FileHeader, r io.ReadSeeker) (StringTable, error) {
return nil, nil
}
l -= 4
- buf := make([]byte, l)
- _, err = io.ReadFull(r, buf)
+
+ // If the string table is large, the file may be corrupt.
+ // Read in chunks to avoid crashing due to out of memory.
+ const chunk = 10 << 20 // 10M
+ var buf []byte
+ if l < chunk {
+ buf = make([]byte, l)
+ _, err = io.ReadFull(r, buf)
+ } else {
+ for l > 0 {
+ n := l
+ if n > chunk {
+ n = chunk
+ }
+ buf1 := make([]byte, n)
+ _, err = io.ReadFull(r, buf1)
+ if err != nil {
+ break
+ }
+ buf = append(buf, buf1...)
+ l -= n
+ }
+ }
if err != nil {
return nil, fmt.Errorf("fail to read string table: %v", err)
}
diff --git a/src/embed/embed.go b/src/embed/embed.go
index 9737ccdf6bb..cbe2d398fb9 100644
--- a/src/embed/embed.go
+++ b/src/embed/embed.go
@@ -36,7 +36,7 @@
// data, _ := f.ReadFile("hello.txt")
// print(string(data))
//
-// Directives
+// # Directives
//
// A //go:embed directive above a variable declaration specifies which files to embed,
// using one or more path.Match patterns.
@@ -96,7 +96,7 @@
//
// If any patterns are invalid or have invalid matches, the build will fail.
//
-// Strings and Bytes
+// # Strings and Bytes
//
// The //go:embed line for a variable of type string or []byte can have only a single pattern,
// and that pattern can match only a single file. The string or []byte is initialized with
@@ -105,7 +105,7 @@
// The //go:embed directive requires importing "embed", even when using a string or []byte.
// In source files that don't refer to embed.FS, use a blank import (import _ "embed").
//
-// File Systems
+// # File Systems
//
// For embedding a single file, a variable of type string or []byte is often best.
// The FS type enables embedding a tree of files, such as a directory of static
@@ -120,12 +120,11 @@
//
// template.ParseFS(content, "*.tmpl")
//
-// Tools
+// # Tools
//
// To support tools that analyze Go packages, the patterns found in //go:embed lines
// are available in “go list” output. See the EmbedPatterns, TestEmbedPatterns,
// and XTestEmbedPatterns fields in the “go help list” output.
-//
package embed
import (
diff --git a/src/encoding/asn1/asn1.go b/src/encoding/asn1/asn1.go
index cad1d7b08f8..c90bba47dcf 100644
--- a/src/encoding/asn1/asn1.go
+++ b/src/encoding/asn1/asn1.go
@@ -5,7 +5,7 @@
// Package asn1 implements parsing of DER-encoded ASN.1 data structures,
// as defined in ITU-T Rec X.690.
//
-// See also ``A Layman's Guide to a Subset of ASN.1, BER, and DER,''
+// See also “A Layman's Guide to a Subset of ASN.1, BER, and DER,”
// http://luca.ntop.org/Teaching/Appunti/asn1.html.
package asn1
diff --git a/src/encoding/base32/base32.go b/src/encoding/base32/base32.go
index 3feea9ba473..5f3af4c8bbe 100644
--- a/src/encoding/base32/base32.go
+++ b/src/encoding/base32/base32.go
@@ -56,7 +56,7 @@ func NewEncoding(encoder string) *Encoding {
// RFC 4648.
var StdEncoding = NewEncoding(encodeStd)
-// HexEncoding is the ``Extended Hex Alphabet'' defined in RFC 4648.
+// HexEncoding is the “Extended Hex Alphabet” defined in RFC 4648.
// It is typically used in DNS.
var HexEncoding = NewEncoding(encodeHex)
diff --git a/src/encoding/binary/varint.go b/src/encoding/binary/varint.go
index bfb4dd193e8..c807d15f442 100644
--- a/src/encoding/binary/varint.go
+++ b/src/encoding/binary/varint.go
@@ -36,6 +36,16 @@ const (
MaxVarintLen64 = 10
)
+// AppendUvarint appends the varint-encoded form of x,
+// as generated by PutUvarint, to buf and returns the extended buffer.
+func AppendUvarint(buf []byte, x uint64) []byte {
+ for x >= 0x80 {
+ buf = append(buf, byte(x)|0x80)
+ x >>= 7
+ }
+ return append(buf, byte(x))
+}
+
// PutUvarint encodes a uint64 into buf and returns the number of bytes written.
// If the buffer is too small, PutUvarint will panic.
func PutUvarint(buf []byte, x uint64) int {
@@ -53,9 +63,9 @@ func PutUvarint(buf []byte, x uint64) int {
// number of bytes read (> 0). If an error occurred, the value is 0
// and the number of bytes n is <= 0 meaning:
//
-// n == 0: buf too small
-// n < 0: value larger than 64 bits (overflow)
-// and -n is the number of bytes read
+// n == 0: buf too small
+// n < 0: value larger than 64 bits (overflow)
+// and -n is the number of bytes read
func Uvarint(buf []byte) (uint64, int) {
var x uint64
var s uint
@@ -77,6 +87,16 @@ func Uvarint(buf []byte) (uint64, int) {
return 0, 0
}
+// AppendVarint appends the varint-encoded form of x,
+// as generated by PutVarint, to buf and returns the extended buffer.
+func AppendVarint(buf []byte, x int64) []byte {
+ ux := uint64(x) << 1
+ if x < 0 {
+ ux = ^ux
+ }
+ return AppendUvarint(buf, ux)
+}
+
// PutVarint encodes an int64 into buf and returns the number of bytes written.
// If the buffer is too small, PutVarint will panic.
func PutVarint(buf []byte, x int64) int {
@@ -91,9 +111,9 @@ func PutVarint(buf []byte, x int64) int {
// number of bytes read (> 0). If an error occurred, the value is 0
// and the number of bytes n is <= 0 with the following meaning:
//
-// n == 0: buf too small
-// n < 0: value larger than 64 bits (overflow)
-// and -n is the number of bytes read
+// n == 0: buf too small
+// n < 0: value larger than 64 bits (overflow)
+// and -n is the number of bytes read
func Varint(buf []byte) (int64, int) {
ux, n := Uvarint(buf) // ok to continue in presence of error
x := int64(ux >> 1)
diff --git a/src/encoding/binary/varint_test.go b/src/encoding/binary/varint_test.go
index d025a67538c..080a2148f0b 100644
--- a/src/encoding/binary/varint_test.go
+++ b/src/encoding/binary/varint_test.go
@@ -36,6 +36,12 @@ func testVarint(t *testing.T, x int64) {
t.Errorf("Varint(%d): got n = %d; want %d", x, m, n)
}
+ buf2 := []byte("prefix")
+ buf2 = AppendVarint(buf2, x)
+ if string(buf2) != "prefix"+string(buf[:n]) {
+ t.Errorf("AppendVarint(%d): got %q, want %q", x, buf2, "prefix"+string(buf[:n]))
+ }
+
y, err := ReadVarint(bytes.NewReader(buf))
if err != nil {
t.Errorf("ReadVarint(%d): %s", x, err)
@@ -56,6 +62,12 @@ func testUvarint(t *testing.T, x uint64) {
t.Errorf("Uvarint(%d): got n = %d; want %d", x, m, n)
}
+ buf2 := []byte("prefix")
+ buf2 = AppendUvarint(buf2, x)
+ if string(buf2) != "prefix"+string(buf[:n]) {
+ t.Errorf("AppendUvarint(%d): got %q, want %q", x, buf2, "prefix"+string(buf[:n]))
+ }
+
y, err := ReadUvarint(bytes.NewReader(buf))
if err != nil {
t.Errorf("ReadUvarint(%d): %s", x, err)
diff --git a/src/encoding/gob/debug.go b/src/encoding/gob/debug.go
index b6d5a3e95c4..dc572fc11e0 100644
--- a/src/encoding/gob/debug.go
+++ b/src/encoding/gob/debug.go
@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file.
// Delete the next line to include in the gob package.
+//
//go:build ignore
package gob
@@ -214,6 +215,7 @@ func (deb *debugger) uint64() uint64 {
}
// GobStream:
+//
// DelimitedMessage* (until EOF)
func (deb *debugger) gobStream() {
// Make sure we're single-threaded through here.
@@ -225,6 +227,7 @@ func (deb *debugger) gobStream() {
}
// DelimitedMessage:
+//
// uint(lengthOfMessage) Message
func (deb *debugger) delimitedMessage(indent tab) bool {
for {
@@ -260,12 +263,19 @@ func (deb *debugger) loadBlock(eofOK bool) int {
}
// Message:
+//
// TypeSequence TypedValue
+//
// TypeSequence
+//
// (TypeDefinition DelimitedTypeDefinition*)?
+//
// DelimitedTypeDefinition:
+//
// uint(lengthOfTypeDefinition) TypeDefinition
+//
// TypedValue:
+//
// int(typeId) Value
func (deb *debugger) message(indent tab) bool {
for {
@@ -350,6 +360,7 @@ func (deb *debugger) delta(expect int) int {
}
// TypeDefinition:
+//
// [int(-typeId) (already read)] encodingOfWireType
func (deb *debugger) typeDefinition(indent tab, id typeId) {
deb.dump("type definition for id %d", id)
@@ -436,6 +447,7 @@ func (deb *debugger) typeDefinition(indent tab, id typeId) {
}
// Value:
+//
// SingletonValue | StructValue
func (deb *debugger) value(indent tab, id typeId) {
wire, ok := deb.wireType[id]
@@ -447,6 +459,7 @@ func (deb *debugger) value(indent tab, id typeId) {
}
// SingletonValue:
+//
// uint(0) FieldValue
func (deb *debugger) singletonValue(indent tab, id typeId) {
deb.dump("Singleton value")
@@ -464,6 +477,7 @@ func (deb *debugger) singletonValue(indent tab, id typeId) {
}
// InterfaceValue:
+//
// NilInterfaceValue | NonNilInterfaceValue
func (deb *debugger) interfaceValue(indent tab) {
deb.dump("Start of interface value")
@@ -475,6 +489,7 @@ func (deb *debugger) interfaceValue(indent tab) {
}
// NilInterfaceValue:
+//
// uint(0) [already read]
func (deb *debugger) nilInterfaceValue(indent tab) int {
fmt.Fprintf(os.Stderr, "%snil interface\n", indent)
@@ -482,12 +497,19 @@ func (deb *debugger) nilInterfaceValue(indent tab) int {
}
// NonNilInterfaceValue:
+//
// ConcreteTypeName TypeSequence InterfaceContents
+//
// ConcreteTypeName:
+//
// uint(lengthOfName) [already read=n] name
+//
// InterfaceContents:
+//
// int(concreteTypeId) DelimitedValue
+//
// DelimitedValue:
+//
// uint(length) Value
func (deb *debugger) nonNilInterfaceValue(indent tab, nameLen int) {
// ConcreteTypeName
@@ -548,6 +570,7 @@ func (deb *debugger) printWireType(indent tab, wire *wireType) {
// fieldValue prints a value of any type, such as a struct field.
// FieldValue:
+//
// builtinValue | ArrayValue | MapValue | SliceValue | StructValue | InterfaceValue
func (deb *debugger) fieldValue(indent tab, id typeId) {
_, ok := builtinIdToType[id]
@@ -621,6 +644,7 @@ func (deb *debugger) printBuiltin(indent tab, id typeId) {
}
// ArrayValue:
+//
// uint(n) FieldValue*n
func (deb *debugger) arrayValue(indent tab, wire *wireType) {
elemId := wire.ArrayT.Elem
@@ -635,6 +659,7 @@ func (deb *debugger) arrayValue(indent tab, wire *wireType) {
}
// MapValue:
+//
// uint(n) (FieldValue FieldValue)*n [n (key, value) pairs]
func (deb *debugger) mapValue(indent tab, wire *wireType) {
keyId := wire.MapT.Key
@@ -648,6 +673,7 @@ func (deb *debugger) mapValue(indent tab, wire *wireType) {
}
// SliceValue:
+//
// uint(n) (n FieldValue)
func (deb *debugger) sliceValue(indent tab, wire *wireType) {
elemId := wire.SliceT.Elem
@@ -661,6 +687,7 @@ func (deb *debugger) sliceValue(indent tab, wire *wireType) {
}
// StructValue:
+//
// (uint(fieldDelta) FieldValue)*
func (deb *debugger) structValue(indent tab, id typeId) {
deb.dump("Start of struct value of %q id=%d\n<<\n", id.name(), id)
@@ -691,6 +718,7 @@ func (deb *debugger) structValue(indent tab, id typeId) {
}
// GobEncoderValue:
+//
// uint(n) byte*n
func (deb *debugger) gobEncoderValue(indent tab, id typeId) {
len := deb.uint64()
diff --git a/src/encoding/gob/decoder.go b/src/encoding/gob/decoder.go
index 86f54b41932..9c4257eb3b5 100644
--- a/src/encoding/gob/decoder.go
+++ b/src/encoding/gob/decoder.go
@@ -132,7 +132,9 @@ func (dec *Decoder) nextUint() uint64 {
// decodeTypeSequence parses:
// TypeSequence
+//
// (TypeDefinition DelimitedTypeDefinition*)?
+//
// and returns the type id of the next value. It returns -1 at
// EOF. Upon return, the remainder of dec.buf is the value to be
// decoded. If this is an interface value, it can be ignored by
diff --git a/src/encoding/gob/doc.go b/src/encoding/gob/doc.go
index c7657071398..306d395d5bc 100644
--- a/src/encoding/gob/doc.go
+++ b/src/encoding/gob/doc.go
@@ -12,7 +12,7 @@ The implementation compiles a custom codec for each data type in the stream and
is most efficient when a single Encoder is used to transmit a stream of values,
amortizing the cost of compilation.
-Basics
+# Basics
A stream of gobs is self-describing. Each data item in the stream is preceded by
a specification of its type, expressed in terms of a small set of predefined
@@ -27,7 +27,7 @@ all type information is sent before it is needed. At the receive side, a
Decoder retrieves values from the encoded stream and unpacks them into local
variables.
-Types and Values
+# Types and Values
The source and destination values/types need not correspond exactly. For structs,
fields (identified by name) that are in the source but absent from the receiving
@@ -101,7 +101,7 @@ Gob can decode a value of any type implementing the GobDecoder or
encoding.BinaryUnmarshaler interfaces by calling the corresponding method,
again in that order of preference.
-Encoding Details
+# Encoding Details
This section documents the encoding, details that are not important for most
users. Details are presented bottom-up.
diff --git a/src/encoding/json/decode.go b/src/encoding/json/decode.go
index ce9675a62fc..4319918d1e5 100644
--- a/src/encoding/json/decode.go
+++ b/src/encoding/json/decode.go
@@ -85,7 +85,7 @@ import (
//
// The JSON null value unmarshals into an interface, map, pointer, or slice
// by setting that Go value to nil. Because null is often used in JSON to mean
-// ``not present,'' unmarshaling a JSON null into any other Go type has no effect
+// “not present,” unmarshaling a JSON null into any other Go type has no effect
// on the value and produces no error.
//
// When unmarshaling quoted strings, invalid UTF-8 or
diff --git a/src/encoding/json/encode.go b/src/encoding/json/encode.go
index fc865386ed3..5b67251fbb8 100644
--- a/src/encoding/json/encode.go
+++ b/src/encoding/json/encode.go
@@ -77,31 +77,31 @@ import (
//
// Examples of struct field tags and their meanings:
//
-// // Field appears in JSON as key "myName".
-// Field int `json:"myName"`
+// // Field appears in JSON as key "myName".
+// Field int `json:"myName"`
//
-// // Field appears in JSON as key "myName" and
-// // the field is omitted from the object if its value is empty,
-// // as defined above.
-// Field int `json:"myName,omitempty"`
+// // Field appears in JSON as key "myName" and
+// // the field is omitted from the object if its value is empty,
+// // as defined above.
+// Field int `json:"myName,omitempty"`
//
-// // Field appears in JSON as key "Field" (the default), but
-// // the field is skipped if empty.
-// // Note the leading comma.
-// Field int `json:",omitempty"`
+// // Field appears in JSON as key "Field" (the default), but
+// // the field is skipped if empty.
+// // Note the leading comma.
+// Field int `json:",omitempty"`
//
-// // Field is ignored by this package.
-// Field int `json:"-"`
+// // Field is ignored by this package.
+// Field int `json:"-"`
//
-// // Field appears in JSON as key "-".
-// Field int `json:"-,"`
+// // Field appears in JSON as key "-".
+// Field int `json:"-,"`
//
// The "string" option signals that a field is stored as JSON inside a
// JSON-encoded string. It applies only to fields of string, floating point,
// integer, or boolean types. This extra level of encoding is sometimes used
// when communicating with JavaScript programs:
//
-// Int64String int64 `json:",string"`
+// Int64String int64 `json:",string"`
//
// The key name will be used if it's a non-empty string consisting of
// only Unicode letters, digits, and ASCII punctuation except quotation
diff --git a/src/encoding/json/fold.go b/src/encoding/json/fold.go
index 9e170127dba..ab249b2bbed 100644
--- a/src/encoding/json/fold.go
+++ b/src/encoding/json/fold.go
@@ -24,8 +24,9 @@ const (
// 4) simpleLetterEqualFold, no specials, no non-letters.
//
// The letters S and K are special because they map to 3 runes, not just 2:
-// * S maps to s and to U+017F 'ſ' Latin small letter long s
-// * k maps to K and to U+212A 'K' Kelvin sign
+// - S maps to s and to U+017F 'ſ' Latin small letter long s
+// - k maps to K and to U+212A 'K' Kelvin sign
+//
// See https://play.golang.org/p/tTxjOc0OGo
//
// The returned function is specialized for matching against s and
diff --git a/src/encoding/pem/pem.go b/src/encoding/pem/pem.go
index e7adf883823..d26e4c83991 100644
--- a/src/encoding/pem/pem.go
+++ b/src/encoding/pem/pem.go
@@ -19,10 +19,12 @@ import (
// A Block represents a PEM encoded structure.
//
// The encoded form is:
-// -----BEGIN Type-----
-// Headers
-// base64-encoded Bytes
-// -----END Type-----
+//
+// -----BEGIN Type-----
+// Headers
+// base64-encoded Bytes
+// -----END Type-----
+//
// where Headers is a possibly empty sequence of Key: Value lines.
type Block struct {
Type string // The type, taken from the preamble (i.e. "RSA PRIVATE KEY").
@@ -88,122 +90,96 @@ func Decode(data []byte) (p *Block, rest []byte) {
// pemStart begins with a newline. However, at the very beginning of
// the byte array, we'll accept the start string without it.
rest = data
- if bytes.HasPrefix(data, pemStart[1:]) {
- rest = rest[len(pemStart)-1 : len(data)]
- } else if _, after, ok := bytes.Cut(data, pemStart); ok {
- rest = after
- } else {
- return nil, data
- }
-
- typeLine, rest := getLine(rest)
- if !bytes.HasSuffix(typeLine, pemEndOfLine) {
- return decodeError(data, rest)
- }
- typeLine = typeLine[0 : len(typeLine)-len(pemEndOfLine)]
-
- p = &Block{
- Headers: make(map[string]string),
- Type: string(typeLine),
- }
-
for {
- // This loop terminates because getLine's second result is
- // always smaller than its argument.
- if len(rest) == 0 {
+ if bytes.HasPrefix(rest, pemStart[1:]) {
+ rest = rest[len(pemStart)-1:]
+ } else if _, after, ok := bytes.Cut(rest, pemStart); ok {
+ rest = after
+ } else {
return nil, data
}
- line, next := getLine(rest)
- key, val, ok := bytes.Cut(line, colon)
- if !ok {
- break
+ var typeLine []byte
+ typeLine, rest = getLine(rest)
+ if !bytes.HasSuffix(typeLine, pemEndOfLine) {
+ continue
}
+ typeLine = typeLine[0 : len(typeLine)-len(pemEndOfLine)]
- // TODO(agl): need to cope with values that spread across lines.
- key = bytes.TrimSpace(key)
- val = bytes.TrimSpace(val)
- p.Headers[string(key)] = string(val)
- rest = next
- }
+ p = &Block{
+ Headers: make(map[string]string),
+ Type: string(typeLine),
+ }
- var endIndex, endTrailerIndex int
+ for {
+ // This loop terminates because getLine's second result is
+ // always smaller than its argument.
+ if len(rest) == 0 {
+ return nil, data
+ }
+ line, next := getLine(rest)
- // If there were no headers, the END line might occur
- // immediately, without a leading newline.
- if len(p.Headers) == 0 && bytes.HasPrefix(rest, pemEnd[1:]) {
- endIndex = 0
- endTrailerIndex = len(pemEnd) - 1
- } else {
- endIndex = bytes.Index(rest, pemEnd)
- endTrailerIndex = endIndex + len(pemEnd)
- }
+ key, val, ok := bytes.Cut(line, colon)
+ if !ok {
+ break
+ }
- if endIndex < 0 {
- return decodeError(data, rest)
- }
+ // TODO(agl): need to cope with values that spread across lines.
+ key = bytes.TrimSpace(key)
+ val = bytes.TrimSpace(val)
+ p.Headers[string(key)] = string(val)
+ rest = next
+ }
- // After the "-----" of the ending line, there should be the same type
- // and then a final five dashes.
- endTrailer := rest[endTrailerIndex:]
- endTrailerLen := len(typeLine) + len(pemEndOfLine)
- if len(endTrailer) < endTrailerLen {
- return decodeError(data, rest)
- }
+ var endIndex, endTrailerIndex int
- restOfEndLine := endTrailer[endTrailerLen:]
- endTrailer = endTrailer[:endTrailerLen]
- if !bytes.HasPrefix(endTrailer, typeLine) ||
- !bytes.HasSuffix(endTrailer, pemEndOfLine) {
- return decodeError(data, rest)
- }
+ // If there were no headers, the END line might occur
+ // immediately, without a leading newline.
+ if len(p.Headers) == 0 && bytes.HasPrefix(rest, pemEnd[1:]) {
+ endIndex = 0
+ endTrailerIndex = len(pemEnd) - 1
+ } else {
+ endIndex = bytes.Index(rest, pemEnd)
+ endTrailerIndex = endIndex + len(pemEnd)
+ }
- // The line must end with only whitespace.
- if s, _ := getLine(restOfEndLine); len(s) != 0 {
- return decodeError(data, rest)
- }
+ if endIndex < 0 {
+ continue
+ }
- base64Data := removeSpacesAndTabs(rest[:endIndex])
- p.Bytes = make([]byte, base64.StdEncoding.DecodedLen(len(base64Data)))
- n, err := base64.StdEncoding.Decode(p.Bytes, base64Data)
- if err != nil {
- return decodeError(data, rest)
- }
- p.Bytes = p.Bytes[:n]
+ // After the "-----" of the ending line, there should be the same type
+ // and then a final five dashes.
+ endTrailer := rest[endTrailerIndex:]
+ endTrailerLen := len(typeLine) + len(pemEndOfLine)
+ if len(endTrailer) < endTrailerLen {
+ continue
+ }
- // the -1 is because we might have only matched pemEnd without the
- // leading newline if the PEM block was empty.
- _, rest = getLine(rest[endIndex+len(pemEnd)-1:])
+ restOfEndLine := endTrailer[endTrailerLen:]
+ endTrailer = endTrailer[:endTrailerLen]
+ if !bytes.HasPrefix(endTrailer, typeLine) ||
+ !bytes.HasSuffix(endTrailer, pemEndOfLine) {
+ continue
+ }
- return
-}
+ // The line must end with only whitespace.
+ if s, _ := getLine(restOfEndLine); len(s) != 0 {
+ continue
+ }
+
+ base64Data := removeSpacesAndTabs(rest[:endIndex])
+ p.Bytes = make([]byte, base64.StdEncoding.DecodedLen(len(base64Data)))
+ n, err := base64.StdEncoding.Decode(p.Bytes, base64Data)
+ if err != nil {
+ continue
+ }
+ p.Bytes = p.Bytes[:n]
-func decodeError(data, rest []byte) (*Block, []byte) {
- // If we get here then we have rejected a likely looking, but
- // ultimately invalid PEM block. We need to start over from a new
- // position. We have consumed the preamble line and will have consumed
- // any lines which could be header lines. However, a valid preamble
- // line is not a valid header line, therefore we cannot have consumed
- // the preamble line for the any subsequent block. Thus, we will always
- // find any valid block, no matter what bytes precede it.
- //
- // For example, if the input is
- //
- // -----BEGIN MALFORMED BLOCK-----
- // junk that may look like header lines
- // or data lines, but no END line
- //
- // -----BEGIN ACTUAL BLOCK-----
- // realdata
- // -----END ACTUAL BLOCK-----
- //
- // we've failed to parse using the first BEGIN line
- // and now will try again, using the second BEGIN line.
- p, rest := Decode(rest)
- if p == nil {
- rest = data
+ // the -1 is because we might have only matched pemEnd without the
+ // leading newline if the PEM block was empty.
+ _, rest = getLine(rest[endIndex+len(pemEnd)-1:])
+ return p, rest
}
- return p, rest
}
const pemLineLength = 64
diff --git a/src/encoding/pem/pem_test.go b/src/encoding/pem/pem_test.go
index b2b6b15e736..c94b5ca53b5 100644
--- a/src/encoding/pem/pem_test.go
+++ b/src/encoding/pem/pem_test.go
@@ -107,6 +107,12 @@ const pemMissingEndingSpace = `
dGVzdA==
-----ENDBAR-----`
+const pemMissingEndLine = `
+-----BEGIN FOO-----
+Header: 1`
+
+var pemRepeatingBegin = strings.Repeat("-----BEGIN \n", 10)
+
var badPEMTests = []struct {
name string
input string
@@ -131,14 +137,34 @@ var badPEMTests = []struct {
"missing ending space",
pemMissingEndingSpace,
},
+ {
+ "repeating begin",
+ pemRepeatingBegin,
+ },
+ {
+ "missing end line",
+ pemMissingEndLine,
+ },
}
func TestBadDecode(t *testing.T) {
for _, test := range badPEMTests {
- result, _ := Decode([]byte(test.input))
+ result, rest := Decode([]byte(test.input))
if result != nil {
t.Errorf("unexpected success while parsing %q", test.name)
}
+ if string(rest) != test.input {
+ t.Errorf("unexpected rest: %q; want = %q", rest, test.input)
+ }
+ }
+}
+
+func TestCVE202224675(t *testing.T) {
+ // Prior to CVE-2022-24675, this input would cause a stack overflow.
+ input := []byte(strings.Repeat("-----BEGIN \n", 10000000))
+ result, rest := Decode(input)
+ if result != nil || !reflect.DeepEqual(rest, input) {
+ t.Errorf("Encode of %#v decoded as %#v", input, rest)
}
}
diff --git a/src/encoding/xml/marshal.go b/src/encoding/xml/marshal.go
index 7792ac77f8a..01f673a8516 100644
--- a/src/encoding/xml/marshal.go
+++ b/src/encoding/xml/marshal.go
@@ -32,39 +32,39 @@ const (
// elements containing the data.
//
// The name for the XML elements is taken from, in order of preference:
-// - the tag on the XMLName field, if the data is a struct
-// - the value of the XMLName field of type Name
-// - the tag of the struct field used to obtain the data
-// - the name of the struct field used to obtain the data
-// - the name of the marshaled type
+// - the tag on the XMLName field, if the data is a struct
+// - the value of the XMLName field of type Name
+// - the tag of the struct field used to obtain the data
+// - the name of the struct field used to obtain the data
+// - the name of the marshaled type
//
// The XML element for a struct contains marshaled elements for each of the
// exported fields of the struct, with these exceptions:
-// - the XMLName field, described above, is omitted.
-// - a field with tag "-" is omitted.
-// - a field with tag "name,attr" becomes an attribute with
-// the given name in the XML element.
-// - a field with tag ",attr" becomes an attribute with the
-// field name in the XML element.
-// - a field with tag ",chardata" is written as character data,
-// not as an XML element.
-// - a field with tag ",cdata" is written as character data
-// wrapped in one or more <![CDATA[ ... ]]> tags, not as an XML element.
-// - a field with tag ",innerxml" is written verbatim, not subject
-// to the usual marshaling procedure.
-// - a field with tag ",comment" is written as an XML comment, not
-// subject to the usual marshaling procedure. It must not contain
-// the "--" string within it.
-// - a field with a tag including the "omitempty" option is omitted
-// if the field value is empty. The empty values are false, 0, any
-// nil pointer or interface value, and any array, slice, map, or
-// string of length zero.
-// - an anonymous struct field is handled as if the fields of its
-// value were part of the outer struct.
-// - a field implementing Marshaler is written by calling its MarshalXML
-// method.
-// - a field implementing encoding.TextMarshaler is written by encoding the
-// result of its MarshalText method as text.
+// - the XMLName field, described above, is omitted.
+// - a field with tag "-" is omitted.
+// - a field with tag "name,attr" becomes an attribute with
+// the given name in the XML element.
+// - a field with tag ",attr" becomes an attribute with the
+// field name in the XML element.
+// - a field with tag ",chardata" is written as character data,
+// not as an XML element.
+// - a field with tag ",cdata" is written as character data
+// wrapped in one or more <![CDATA[ ... ]]> tags, not as an XML element.
+// - a field with tag ",innerxml" is written verbatim, not subject
+// to the usual marshaling procedure.
+// - a field with tag ",comment" is written as an XML comment, not
+// subject to the usual marshaling procedure. It must not contain
+// the "--" string within it.
+// - a field with a tag including the "omitempty" option is omitted
+// if the field value is empty. The empty values are false, 0, any
+// nil pointer or interface value, and any array, slice, map, or
+// string of length zero.
+// - an anonymous struct field is handled as if the fields of its
+// value were part of the outer struct.
+// - a field implementing Marshaler is written by calling its MarshalXML
+// method.
+// - a field implementing encoding.TextMarshaler is written by encoding the
+// result of its MarshalText method as text.
//
// If a field uses a tag "a>b>c", then the element c will be nested inside
// parent elements a and b. Fields that appear next to each other that name
diff --git a/src/encoding/xml/read.go b/src/encoding/xml/read.go
index 0701e186259..565d9a8bea6 100644
--- a/src/encoding/xml/read.go
+++ b/src/encoding/xml/read.go
@@ -35,62 +35,62 @@ import (
// In the rules, the tag of a field refers to the value associated with the
// key 'xml' in the struct field's tag (see the example above).
//
-// * If the struct has a field of type []byte or string with tag
-// ",innerxml", Unmarshal accumulates the raw XML nested inside the
-// element in that field. The rest of the rules still apply.
+// - If the struct has a field of type []byte or string with tag
+// ",innerxml", Unmarshal accumulates the raw XML nested inside the
+// element in that field. The rest of the rules still apply.
//
-// * If the struct has a field named XMLName of type Name,
-// Unmarshal records the element name in that field.
+// - If the struct has a field named XMLName of type Name,
+// Unmarshal records the element name in that field.
//
-// * If the XMLName field has an associated tag of the form
-// "name" or "namespace-URL name", the XML element must have
-// the given name (and, optionally, name space) or else Unmarshal
-// returns an error.
+// - If the XMLName field has an associated tag of the form
+// "name" or "namespace-URL name", the XML element must have
+// the given name (and, optionally, name space) or else Unmarshal
+// returns an error.
//
-// * If the XML element has an attribute whose name matches a
-// struct field name with an associated tag containing ",attr" or
-// the explicit name in a struct field tag of the form "name,attr",
-// Unmarshal records the attribute value in that field.
+// - If the XML element has an attribute whose name matches a
+// struct field name with an associated tag containing ",attr" or
+// the explicit name in a struct field tag of the form "name,attr",
+// Unmarshal records the attribute value in that field.
//
-// * If the XML element has an attribute not handled by the previous
-// rule and the struct has a field with an associated tag containing
-// ",any,attr", Unmarshal records the attribute value in the first
-// such field.
+// - If the XML element has an attribute not handled by the previous
+// rule and the struct has a field with an associated tag containing
+// ",any,attr", Unmarshal records the attribute value in the first
+// such field.
//
-// * If the XML element contains character data, that data is
-// accumulated in the first struct field that has tag ",chardata".
-// The struct field may have type []byte or string.
-// If there is no such field, the character data is discarded.
+// - If the XML element contains character data, that data is
+// accumulated in the first struct field that has tag ",chardata".
+// The struct field may have type []byte or string.
+// If there is no such field, the character data is discarded.
//
-// * If the XML element contains comments, they are accumulated in
-// the first struct field that has tag ",comment". The struct
-// field may have type []byte or string. If there is no such
-// field, the comments are discarded.
+// - If the XML element contains comments, they are accumulated in
+// the first struct field that has tag ",comment". The struct
+// field may have type []byte or string. If there is no such
+// field, the comments are discarded.
//
-// * If the XML element contains a sub-element whose name matches
-// the prefix of a tag formatted as "a" or "a>b>c", unmarshal
-// will descend into the XML structure looking for elements with the
-// given names, and will map the innermost elements to that struct
-// field. A tag starting with ">" is equivalent to one starting
-// with the field name followed by ">".
+// - If the XML element contains a sub-element whose name matches
+// the prefix of a tag formatted as "a" or "a>b>c", unmarshal
+// will descend into the XML structure looking for elements with the
+// given names, and will map the innermost elements to that struct
+// field. A tag starting with ">" is equivalent to one starting
+// with the field name followed by ">".
//
-// * If the XML element contains a sub-element whose name matches
-// a struct field's XMLName tag and the struct field has no
-// explicit name tag as per the previous rule, unmarshal maps
-// the sub-element to that struct field.
+// - If the XML element contains a sub-element whose name matches
+// a struct field's XMLName tag and the struct field has no
+// explicit name tag as per the previous rule, unmarshal maps
+// the sub-element to that struct field.
//
-// * If the XML element contains a sub-element whose name matches a
-// field without any mode flags (",attr", ",chardata", etc), Unmarshal
-// maps the sub-element to that struct field.
+// - If the XML element contains a sub-element whose name matches a
+// field without any mode flags (",attr", ",chardata", etc), Unmarshal
+// maps the sub-element to that struct field.
//
-// * If the XML element contains a sub-element that hasn't matched any
-// of the above rules and the struct has a field with tag ",any",
-// unmarshal maps the sub-element to that struct field.
+// - If the XML element contains a sub-element that hasn't matched any
+// of the above rules and the struct has a field with tag ",any",
+// unmarshal maps the sub-element to that struct field.
//
-// * An anonymous struct field is handled as if the fields of its
-// value were part of the outer struct.
+// - An anonymous struct field is handled as if the fields of its
+// value were part of the outer struct.
//
-// * A struct field with tag "-" is never unmarshaled into.
+// - A struct field with tag "-" is never unmarshaled into.
//
// If Unmarshal encounters a field type that implements the Unmarshaler
// interface, Unmarshal calls its UnmarshalXML method to produce the value from
diff --git a/src/expvar/expvar.go b/src/expvar/expvar.go
index 8bbf41b151d..5629f89353d 100644
--- a/src/expvar/expvar.go
+++ b/src/expvar/expvar.go
@@ -17,8 +17,8 @@
// The package is sometimes only imported for the side effect of
// registering its HTTP handler and the above variables. To use it
// this way, link this package into your program:
-// import _ "expvar"
//
+// import _ "expvar"
package expvar
import (
diff --git a/src/flag/flag.go b/src/flag/flag.go
index 15bcb6cea97..a0762441a58 100644
--- a/src/flag/flag.go
+++ b/src/flag/flag.go
@@ -5,29 +5,38 @@
/*
Package flag implements command-line flag parsing.
-Usage
+# Usage
Define flags using flag.String(), Bool(), Int(), etc.
This declares an integer flag, -n, stored in the pointer nFlag, with type *int:
+
import "flag"
var nFlag = flag.Int("n", 1234, "help message for flag n")
+
If you like, you can bind the flag to a variable using the Var() functions.
+
var flagvar int
func init() {
flag.IntVar(&flagvar, "flagname", 1234, "help message for flagname")
}
+
Or you can create custom flags that satisfy the Value interface (with
pointer receivers) and couple them to flag parsing by
+
flag.Var(&flagVal, "name", "help message for flagname")
+
For such flags, the default value is just the initial value of the variable.
After all flags are defined, call
+
flag.Parse()
+
to parse the command line into the defined flags.
Flags may then be used directly. If you're using the flags themselves,
they are all pointers; if you bind to variables, they're values.
+
fmt.Println("ip has value ", *ip)
fmt.Println("flagvar has value ", flagvar)
@@ -35,17 +44,20 @@ After parsing, the arguments following the flags are available as the
slice flag.Args() or individually as flag.Arg(i).
The arguments are indexed from 0 through flag.NArg()-1.
-Command line flag syntax
+# Command line flag syntax
The following forms are permitted:
-flag
-flag=x
-flag x // non-boolean flags only
+
One or two minus signs may be used; they are equivalent.
The last form is not permitted for boolean flags because the
meaning of the command
+
cmd -x *
+
where * is a Unix shell wildcard, will change if there is a file
called 0, false, etc. You must use the -flag=false form to turn
off a boolean flag.
@@ -55,7 +67,9 @@ Flag parsing stops just before the first non-flag argument
Integer flags accept 1234, 0664, 0x1234 and may be negative.
Boolean flags may be:
+
1, 0, t, f, T, F, true, false, TRUE, FALSE, True, False
+
Duration flags accept any input valid for time.ParseDuration.
The default set of command-line flags is controlled by
@@ -605,8 +619,10 @@ func (f *FlagSet) PrintDefaults() {
// a usage message showing the default settings of all defined
// command-line flags.
// For an integer valued flag x, the default output has the form
+//
// -x int
// usage-message-for-x (default 7)
+//
// The usage message will appear on a separate line for anything but
// a bool flag with a one-byte name. For bool flags, the type is
// omitted and if the flag name is one byte the usage message appears
@@ -616,8 +632,11 @@ func (f *FlagSet) PrintDefaults() {
// string; the first such item in the message is taken to be a parameter
// name to show in the message and the back quotes are stripped from
// the message when displayed. For instance, given
+//
// flag.String("I", "", "search `directory` for include files")
+//
// the output will be
+//
// -I directory
// search directory for include files.
//
diff --git a/src/fmt/doc.go b/src/fmt/doc.go
index 4151b39ea65..9785ed95269 100644
--- a/src/fmt/doc.go
+++ b/src/fmt/doc.go
@@ -7,12 +7,12 @@ Package fmt implements formatted I/O with functions analogous
to C's printf and scanf. The format 'verbs' are derived from C's but
are simpler.
-
-Printing
+# Printing
The verbs:
General:
+
%v the value in a default format
when printing structs, the plus flag (%+v) adds field names
%#v a Go-syntax representation of the value
@@ -20,8 +20,11 @@ General:
%% a literal percent sign; consumes no value
Boolean:
+
%t the word true or false
+
Integer:
+
%b base 2
%c the character represented by the corresponding Unicode code point
%d base 10
@@ -31,7 +34,9 @@ Integer:
%x base 16, with lower-case letters for a-f
%X base 16, with upper-case letters for A-F
%U Unicode format: U+1234; same as "U+%04X"
+
Floating-point and complex constituents:
+
%b decimalless scientific notation with exponent a power of two,
in the manner of strconv.FormatFloat with the 'b' format,
e.g. -123456p-78
@@ -43,19 +48,26 @@ Floating-point and complex constituents:
%G %E for large exponents, %F otherwise
%x hexadecimal notation (with decimal power of two exponent), e.g. -0x1.23abcp+20
%X upper-case hexadecimal notation, e.g. -0X1.23ABCP+20
+
String and slice of bytes (treated equivalently with these verbs):
+
%s the uninterpreted bytes of the string or slice
%q a double-quoted string safely escaped with Go syntax
%x base 16, lower-case, two characters per byte
%X base 16, upper-case, two characters per byte
+
Slice:
+
%p address of 0th element in base 16 notation, with leading 0x
+
Pointer:
+
%p base 16 notation, with leading 0x
The %b, %d, %o, %x and %X verbs also work with pointers,
formatting the value exactly as if it were an integer.
The default format for %v is:
+
bool: %t
int, int8 etc.: %d
uint, uint8 etc.: %d, %#x if printed with %#v
@@ -63,8 +75,10 @@ The default format for %v is:
string: %s
chan: %p
pointer: %p
+
For compound objects, the elements are printed using these rules, recursively,
laid out like this:
+
struct: {field0 field1 ...}
array, slice: [elem0 elem1 ...]
maps: map[key1:value1 key2:value2 ...]
@@ -76,6 +90,7 @@ Precision is specified after the (optional) width by a period followed by a
decimal number. If no period is present, a default precision is used.
A period with no following number specifies a precision of zero.
Examples:
+
%f default width, default precision
%9f width 9, default precision
%.2f default width, precision 2
@@ -144,8 +159,10 @@ operands and appends a newline.
Regardless of the verb, if an operand is an interface value,
the internal concrete value is used, not the interface itself.
Thus:
+
var i interface{} = 23
fmt.Printf("%v\n", i)
+
will print 23.
Except when printed using the verbs %T and %p, special
@@ -183,10 +200,14 @@ However, when printing a byte slice with a string-like verb
(%s %q %x %X), it is treated identically to a string, as a single item.
To avoid recursion in cases such as
+
type X string
func (x X) String() string { return Sprintf("<%s>", x) }
+
convert the value before recurring:
+
func (x X) String() string { return Sprintf("<%s>", string(x)) }
+
Infinite recursion can also be triggered by self-referential data
structures, such as a slice that contains itself as an element, if
that type has a String method. Such pathologies are rare, however,
@@ -195,7 +216,7 @@ and the package does not protect against them.
When printing a struct, fmt cannot and therefore does not invoke
formatting methods such as Error or String on unexported fields.
-Explicit argument indexes
+# Explicit argument indexes
In Printf, Sprintf, and Fprintf, the default behavior is for each
formatting verb to format successive arguments passed in the call.
@@ -206,18 +227,26 @@ the value. After processing a bracketed expression [n], subsequent verbs
will use arguments n+1, n+2, etc. unless otherwise directed.
For example,
+
fmt.Sprintf("%[2]d %[1]d\n", 11, 22)
+
will yield "22 11", while
+
fmt.Sprintf("%[3]*.[2]*[1]f", 12.0, 2, 6)
+
equivalent to
+
fmt.Sprintf("%6.2f", 12.0)
+
will yield " 12.00". Because an explicit index affects subsequent verbs,
this notation can be used to print the same values multiple times
by resetting the index for the first argument to be repeated:
+
fmt.Sprintf("%d %d %#[1]x %#x", 16, 17)
+
will yield "16 17 0x10 0x11".
-Format errors
+# Format errors
If an invalid argument is given for a verb, such as providing
a string to %d, the generated string will contain a
@@ -246,6 +275,7 @@ from the panic, decorating it with an indication that it came
through the fmt package. For example, if a String method
calls panic("bad"), the resulting formatted message will look
like
+
%!s(PANIC=bad)
The %!s just shows the print verb in use when the failure
@@ -253,7 +283,7 @@ occurred. If the panic is caused by a nil receiver to an Error
or String method, however, the output is the undecorated
string, "<nil>".
-Scanning
+# Scanning
An analogous set of functions scans formatted text to yield
values. Scan, Scanf and Scanln read from os.Stdin; Fscan,
@@ -314,9 +344,13 @@ syntax for scanning with a precision (no %5.2f, just %5f).
If width is provided, it applies after leading spaces are
trimmed and specifies the maximum number of runes to read
to satisfy the verb. For example,
- Sscanf(" 1234567 ", "%5s%d", &s, &i)
+
+ Sscanf(" 1234567 ", "%5s%d", &s, &i)
+
will set s to "12345" and i to 67 while
- Sscanf(" 12 34 567 ", "%5s%d", &s, &i)
+
+ Sscanf(" 12 34 567 ", "%5s%d", &s, &i)
+
will set s to "12" and i to 34.
In all the scanning functions, a carriage return followed
diff --git a/src/fmt/print.go b/src/fmt/print.go
index 1c37c3cb7b3..33f55416290 100644
--- a/src/fmt/print.go
+++ b/src/fmt/print.go
@@ -55,7 +55,7 @@ type Formatter interface {
}
// Stringer is implemented by any value that has a String method,
-// which defines the ``native'' format for that value.
+// which defines the “native” format for that value.
// The String method is used to print values passed as an operand
// to any format that accepts a string or to an unformatted printer
// such as Print.
diff --git a/src/go/ast/ast.go b/src/go/ast/ast.go
index 61855359f86..1e089b9e70f 100644
--- a/src/go/ast/ast.go
+++ b/src/go/ast/ast.go
@@ -4,7 +4,6 @@
// Package ast declares the types used to represent syntax trees for Go
// packages.
-//
package ast
import (
@@ -159,10 +158,13 @@ func (g *CommentGroup) Text() string {
}
// isDirective reports whether c is a comment directive.
+// This code is also in go/printer.
func isDirective(c string) bool {
// "//line " is a line directive.
+ // "//extern " is for gccgo.
+ // "//export " is for cgo.
// (The // has been removed.)
- if strings.HasPrefix(c, "line ") {
+ if strings.HasPrefix(c, "line ") || strings.HasPrefix(c, "extern ") || strings.HasPrefix(c, "export ") {
return true
}
diff --git a/src/go/ast/ast_test.go b/src/go/ast/ast_test.go
index 71b2d6ca4b8..66ae8848678 100644
--- a/src/go/ast/ast_test.go
+++ b/src/go/ast/ast_test.go
@@ -68,6 +68,9 @@ var isDirectiveTests = []struct {
{"go:", false},
{"go:*", false},
{"go:x*", true},
+ {"export foo", true},
+ {"extern foo", true},
+ {"expert foo", false},
}
func TestIsDirective(t *testing.T) {
diff --git a/src/go/build/build.go b/src/go/build/build.go
index f156078dbf6..c67d6d0cdc2 100644
--- a/src/go/build/build.go
+++ b/src/go/build/build.go
@@ -528,9 +528,9 @@ func nameExt(name string) string {
// In the directory containing the package, .go, .c, .h, and .s files are
// considered part of the package except for:
//
-// - .go files in package documentation
-// - files starting with _ or . (likely editor temporary files)
-// - files with build constraints not satisfied by the context
+// - .go files in package documentation
+// - files starting with _ or . (likely editor temporary files)
+// - files with build constraints not satisfied by the context
//
// If an error occurs, Import returns a non-nil error and a non-nil
// *Package containing partial information.
@@ -1380,7 +1380,6 @@ type fileInfo struct {
parseErr error
imports []fileImport
embeds []fileEmbed
- embedErr error
}
type fileImport struct {
@@ -1807,11 +1806,11 @@ func safeCgoName(s string) bool {
//
// For example, the following string:
//
-// a b:"c d" 'e''f' "g\""
+// a b:"c d" 'e''f' "g\""
//
// Would be parsed as:
//
-// []string{"a", "b:c d", "ef", `g"`}
+// []string{"a", "b:c d", "ef", `g"`}
func splitQuoted(s string) (r []string, err error) {
var args []string
arg := make([]rune, len(s))
@@ -1944,12 +1943,12 @@ func (ctxt *Context) matchTag(name string, allTags map[string]bool) bool {
// suffix which does not match the current system.
// The recognized name formats are:
//
-// name_$(GOOS).*
-// name_$(GOARCH).*
-// name_$(GOOS)_$(GOARCH).*
-// name_$(GOOS)_test.*
-// name_$(GOARCH)_test.*
-// name_$(GOOS)_$(GOARCH)_test.*
+// name_$(GOOS).*
+// name_$(GOARCH).*
+// name_$(GOOS)_$(GOARCH).*
+// name_$(GOOS)_test.*
+// name_$(GOARCH)_test.*
+// name_$(GOOS)_$(GOARCH)_test.*
//
// Exceptions:
// if GOOS=android, then files with GOOS=linux are also matched.
diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go
index f318f6e4325..651257a3577 100644
--- a/src/go/build/deps_test.go
+++ b/src/go/build/deps_test.go
@@ -31,7 +31,7 @@ import (
//
// The general syntax of a rule is:
//
-// a, b < c, d;
+// a, b < c, d;
//
// which means c and d come after a and b in the partial order
// (that is, c and d can import a and b),
@@ -39,12 +39,12 @@ import (
//
// The rules can chain together, as in:
//
-// e < f, g < h;
+// e < f, g < h;
//
// which is equivalent to
//
-// e < f, g;
-// f, g < h;
+// e < f, g;
+// f, g < h;
//
// Except for the special bottom element "NONE", each name
// must appear exactly once on the right-hand side of a rule.
@@ -56,7 +56,7 @@ import (
//
// Negative assertions double-check the partial order:
//
-// i !< j
+// i !< j
//
// means that it must NOT be the case that i < j.
// Negative assertions may appear anywhere in the rules,
@@ -288,13 +288,13 @@ var depsRules = `
< go/parser;
FMT
- < go/build/constraint;
+ < go/build/constraint, go/doc/comment;
- go/build/constraint, go/parser, text/tabwriter
+ go/build/constraint, go/doc/comment, go/parser, text/tabwriter
< go/printer
< go/format;
- go/parser, internal/lazyregexp, text/template
+ go/doc/comment, go/parser, internal/lazyregexp, text/template
< go/doc;
math/big, go/token
diff --git a/src/go/build/doc.go b/src/go/build/doc.go
index 778b4f40f7c..262f6709af7 100644
--- a/src/go/build/doc.go
+++ b/src/go/build/doc.go
@@ -4,7 +4,7 @@
// Package build gathers information about Go packages.
//
-// Go Path
+// # Go Path
//
// The Go path is a list of directory trees containing Go source code.
// It is consulted to resolve imports that cannot be found in the standard
@@ -55,11 +55,11 @@
// foo/
// bar.a (installed package object)
//
-// Build Constraints
+// # Build Constraints
//
// A build constraint, also known as a build tag, is a line comment that begins
//
-// //go:build
+// //go:build
//
// that lists the conditions under which a file should be included in the
// package. Build constraints may also be part of a file's name
@@ -69,7 +69,7 @@
// See 'go help buildconstraint'
// (https://golang.org/cmd/go/#hdr-Build_constraints) for details.
//
-// Binary-Only Packages
+// # Binary-Only Packages
//
// In Go 1.12 and earlier, it was possible to distribute packages in binary
// form without including the source code used for compiling the package.
@@ -94,5 +94,4 @@
// "go build" and other commands no longer support binary-only-packages.
// Import and ImportDir will still set the BinaryOnly flag in packages
// containing these comments for use in tools and error messages.
-//
package build
diff --git a/src/go/build/read.go b/src/go/build/read.go
index de5c33a4f84..52adfeab9ae 100644
--- a/src/go/build/read.go
+++ b/src/go/build/read.go
@@ -390,7 +390,7 @@ func readComments(f io.Reader) ([]byte, error) {
// readGoInfo expects a Go file as input and reads the file up to and including the import section.
// It records what it learned in *info.
// If info.fset is non-nil, readGoInfo parses the file and sets info.parsed, info.parseErr,
-// info.imports, info.embeds, and info.embedErr.
+// info.imports and info.embeds.
//
// It only returns an error if there are problems reading the file,
// not for syntax errors in the file itself.
diff --git a/src/go/constant/value.go b/src/go/constant/value.go
index 3daa4c28608..36c29d8c271 100644
--- a/src/go/constant/value.go
+++ b/src/go/constant/value.go
@@ -9,7 +9,6 @@
// is unknown due to an error. Operations on unknown
// values produce unknown values unless specified
// otherwise.
-//
package constant
import (
@@ -571,13 +570,13 @@ func Float64Val(x Value) (float64, bool) {
// interface, it is up to the caller to type assert the result to the expected
// type. The possible dynamic return types are:
//
-// x Kind type of result
-// -----------------------------------------
-// Bool bool
-// String string
-// Int int64 or *big.Int
-// Float *big.Float or *big.Rat
-// everything else nil
+// x Kind type of result
+// -----------------------------------------
+// Bool bool
+// String string
+// Int int64 or *big.Int
+// Float *big.Float or *big.Rat
+// everything else nil
func Val(x Value) any {
switch x := x.(type) {
case boolVal:
@@ -599,15 +598,15 @@ func Val(x Value) any {
// Make returns the Value for x.
//
-// type of x result Kind
-// ----------------------------
-// bool Bool
-// string String
-// int64 Int
-// *big.Int Int
-// *big.Float Float
-// *big.Rat Float
-// anything else Unknown
+// type of x result Kind
+// ----------------------------
+// bool Bool
+// string String
+// int64 Int
+// *big.Int Int
+// *big.Float Float
+// *big.Rat Float
+// anything else Unknown
func Make(x any) Value {
switch x := x.(type) {
case bool:
diff --git a/src/go/doc/comment.go b/src/go/doc/comment.go
index f1aa69d9740..4f73664ba31 100644
--- a/src/go/doc/comment.go
+++ b/src/go/doc/comment.go
@@ -2,515 +2,70 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// Godoc comment extraction and comment -> HTML formatting.
-
package doc
import (
- "bytes"
- "internal/lazyregexp"
+ "go/doc/comment"
"io"
- "strings"
- "text/template" // for HTMLEscape
- "unicode"
- "unicode/utf8"
-)
-
-const (
- ldquo = "&ldquo;"
- rdquo = "&rdquo;"
- ulquo = "“"
- urquo = "”"
)
-var (
- htmlQuoteReplacer = strings.NewReplacer(ulquo, ldquo, urquo, rdquo)
- unicodeQuoteReplacer = strings.NewReplacer("``", ulquo, "''", urquo)
-)
-
-// Escape comment text for HTML. If nice is set, also replace:
+// ToHTML converts comment text to formatted HTML.
//
-// `` -> &ldquo;
-// '' -> &rdquo;
+// Deprecated: ToHTML cannot identify documentation links
+// in the doc comment, because they depend on knowing what
+// package the text came from, which is not included in this API.
//
-func commentEscape(w io.Writer, text string, nice bool) {
- if nice {
- // In the first pass, we convert `` and '' into their unicode equivalents.
- // This prevents them from being escaped in HTMLEscape.
- text = convertQuotes(text)
- var buf bytes.Buffer
- template.HTMLEscape(&buf, []byte(text))
- // Now we convert the unicode quotes to their HTML escaped entities to maintain old behavior.
- // We need to use a temp buffer to read the string back and do the conversion,
- // otherwise HTMLEscape will escape & to &amp;
- htmlQuoteReplacer.WriteString(w, buf.String())
- return
- }
- template.HTMLEscape(w, []byte(text))
-}
-
-func convertQuotes(text string) string {
- return unicodeQuoteReplacer.Replace(text)
-}
-
-const (
- // Regexp for Go identifiers
- identRx = `[\pL_][\pL_0-9]*`
-
- // Regexp for URLs
- // Match parens, and check later for balance - see #5043, #22285
- // Match .,:;?! within path, but not at end - see #18139, #16565
- // This excludes some rare yet valid urls ending in common punctuation
- // in order to allow sentences ending in URLs.
-
- // protocol (required) e.g. http
- protoPart = `(https?|ftp|file|gopher|mailto|nntp)`
- // host (required) e.g. www.example.com or [::1]:8080
- hostPart = `([a-zA-Z0-9_@\-.\[\]:]+)`
- // path+query+fragment (optional) e.g. /path/index.html?q=foo#bar
- pathPart = `([.,:;?!]*[a-zA-Z0-9$'()*+&#=@~_/\-\[\]%])*`
-
- urlRx = protoPart + `://` + hostPart + pathPart
-)
-
-var matchRx = lazyregexp.New(`(` + urlRx + `)|(` + identRx + `)`)
-
-var (
- html_a = []byte(`<a href="`)
- html_aq = []byte(`">`)
- html_enda = []byte("</a>")
- html_i = []byte("<i>")
- html_endi = []byte("</i>")
- html_p = []byte("<p>\n")
- html_endp = []byte("</p>\n")
- html_pre = []byte("<pre>")
- html_endpre = []byte("</pre>\n")
- html_h = []byte(`<h3 id="`)
- html_hq = []byte(`">`)
- html_endh = []byte("</h3>\n")
-)
-
-// Emphasize and escape a line of text for HTML. URLs are converted into links;
-// if the URL also appears in the words map, the link is taken from the map (if
-// the corresponding map value is the empty string, the URL is not converted
-// into a link). Go identifiers that appear in the words map are italicized; if
-// the corresponding map value is not the empty string, it is considered a URL
-// and the word is converted into a link. If nice is set, the remaining text's
-// appearance is improved where it makes sense, such as replacing:
-//
-// `` -> &ldquo;
-// '' -> &rdquo;
-func emphasize(w io.Writer, line string, words map[string]string, nice bool) {
- for {
- m := matchRx.FindStringSubmatchIndex(line)
- if m == nil {
- break
- }
- // m >= 6 (two parenthesized sub-regexps in matchRx, 1st one is urlRx)
-
- // write text before match
- commentEscape(w, line[0:m[0]], nice)
-
- // adjust match for URLs
- match := line[m[0]:m[1]]
- if strings.Contains(match, "://") {
- m0, m1 := m[0], m[1]
- for _, s := range []string{"()", "{}", "[]"} {
- open, close := s[:1], s[1:] // E.g., "(" and ")"
- // require opening parentheses before closing parentheses (#22285)
- if i := strings.Index(match, close); i >= 0 && i < strings.Index(match, open) {
- m1 = m0 + i
- match = line[m0:m1]
- }
- // require balanced pairs of parentheses (#5043)
- for i := 0; strings.Count(match, open) != strings.Count(match, close) && i < 10; i++ {
- m1 = strings.LastIndexAny(line[:m1], s)
- match = line[m0:m1]
- }
- }
- if m1 != m[1] {
- // redo matching with shortened line for correct indices
- m = matchRx.FindStringSubmatchIndex(line[:m[0]+len(match)])
- }
- }
-
- // analyze match
- url := ""
- italics := false
- if words != nil {
- url, italics = words[match]
- }
- if m[2] >= 0 {
- // match against first parenthesized sub-regexp; must be match against urlRx
- if !italics {
- // no alternative URL in words list, use match instead
- url = match
- }
- italics = false // don't italicize URLs
- }
-
- // write match
- if len(url) > 0 {
- w.Write(html_a)
- template.HTMLEscape(w, []byte(url))
- w.Write(html_aq)
- }
- if italics {
- w.Write(html_i)
- }
- commentEscape(w, match, nice)
- if italics {
- w.Write(html_endi)
- }
- if len(url) > 0 {
- w.Write(html_enda)
- }
-
- // advance
- line = line[m[1]:]
- }
- commentEscape(w, line, nice)
-}
-
-func indentLen(s string) int {
- i := 0
- for i < len(s) && (s[i] == ' ' || s[i] == '\t') {
- i++
- }
- return i
-}
-
-func isBlank(s string) bool {
- return len(s) == 0 || (len(s) == 1 && s[0] == '\n')
-}
-
-func commonPrefix(a, b string) string {
- i := 0
- for i < len(a) && i < len(b) && a[i] == b[i] {
- i++
- }
- return a[0:i]
-}
-
-func unindent(block []string) {
- if len(block) == 0 {
- return
- }
-
- // compute maximum common white prefix
- prefix := block[0][0:indentLen(block[0])]
- for _, line := range block {
- if !isBlank(line) {
- prefix = commonPrefix(prefix, line[0:indentLen(line)])
- }
- }
- n := len(prefix)
-
- // remove
- for i, line := range block {
- if !isBlank(line) {
- block[i] = line[n:]
- }
- }
-}
-
-// heading returns the trimmed line if it passes as a section heading;
-// otherwise it returns the empty string.
-func heading(line string) string {
- line = strings.TrimSpace(line)
- if len(line) == 0 {
- return ""
- }
-
- // a heading must start with an uppercase letter
- r, _ := utf8.DecodeRuneInString(line)
- if !unicode.IsLetter(r) || !unicode.IsUpper(r) {
- return ""
- }
-
- // it must end in a letter or digit:
- r, _ = utf8.DecodeLastRuneInString(line)
- if !unicode.IsLetter(r) && !unicode.IsDigit(r) {
- return ""
- }
-
- // exclude lines with illegal characters. we allow "(),"
- if strings.ContainsAny(line, ";:!?+*/=[]{}_^°&§~%#@<\">\\") {
- return ""
- }
-
- // allow "'" for possessive "'s" only
- for b := line; ; {
- var ok bool
- if _, b, ok = strings.Cut(b, "'"); !ok {
- break
- }
- if b != "s" && !strings.HasPrefix(b, "s ") {
- return "" // ' not followed by s and then end-of-word
- }
- }
-
- // allow "." when followed by non-space
- for b := line; ; {
- var ok bool
- if _, b, ok = strings.Cut(b, "."); !ok {
- break
- }
- if b == "" || strings.HasPrefix(b, " ") {
- return "" // not followed by non-space
- }
- }
-
- return line
-}
-
-type op int
-
-const (
- opPara op = iota
- opHead
- opPre
-)
-
-type block struct {
- op op
- lines []string
-}
-
-var nonAlphaNumRx = lazyregexp.New(`[^a-zA-Z0-9]`)
-
-func anchorID(line string) string {
- // Add a "hdr-" prefix to avoid conflicting with IDs used for package symbols.
- return "hdr-" + nonAlphaNumRx.ReplaceAllString(line, "_")
-}
-
-// ToHTML converts comment text to formatted HTML.
-// The comment was prepared by DocReader,
-// so it is known not to have leading, trailing blank lines
-// nor to have trailing spaces at the end of lines.
-// The comment markers have already been removed.
+// Given the *[doc.Package] p where text was found,
+// ToHTML(w, text, nil) can be replaced by:
//
-// Each span of unindented non-blank lines is converted into
-// a single paragraph. There is one exception to the rule: a span that
-// consists of a single line, is followed by another paragraph span,
-// begins with a capital letter, and contains no punctuation
-// other than parentheses and commas is formatted as a heading.
+// w.Write(p.HTML(text))
//
-// A span of indented lines is converted into a <pre> block,
-// with the common indent prefix removed.
+// which is in turn shorthand for:
//
-// URLs in the comment text are converted into links; if the URL also appears
-// in the words map, the link is taken from the map (if the corresponding map
-// value is the empty string, the URL is not converted into a link).
+// w.Write(p.Printer().HTML(p.Parser().Parse(text)))
//
-// A pair of (consecutive) backticks (`) is converted to a unicode left quote (“), and a pair of (consecutive)
-// single quotes (') is converted to a unicode right quote (”).
+// If words may be non-nil, the longer replacement is:
//
-// Go identifiers that appear in the words map are italicized; if the corresponding
-// map value is not the empty string, it is considered a URL and the word is converted
-// into a link.
+// parser := p.Parser()
+// parser.Words = words
+// w.Write(p.Printer().HTML(parser.Parse(d)))
func ToHTML(w io.Writer, text string, words map[string]string) {
- for _, b := range blocks(text) {
- switch b.op {
- case opPara:
- w.Write(html_p)
- for _, line := range b.lines {
- emphasize(w, line, words, true)
- }
- w.Write(html_endp)
- case opHead:
- w.Write(html_h)
- id := ""
- for _, line := range b.lines {
- if id == "" {
- id = anchorID(line)
- w.Write([]byte(id))
- w.Write(html_hq)
- }
- commentEscape(w, line, true)
- }
- if id == "" {
- w.Write(html_hq)
- }
- w.Write(html_endh)
- case opPre:
- w.Write(html_pre)
- for _, line := range b.lines {
- emphasize(w, line, nil, false)
- }
- w.Write(html_endpre)
- }
- }
+ p := new(Package).Parser()
+ p.Words = words
+ d := p.Parse(text)
+ pr := new(comment.Printer)
+ w.Write(pr.HTML(d))
}
-func blocks(text string) []block {
- var (
- out []block
- para []string
-
- lastWasBlank = false
- lastWasHeading = false
- )
-
- close := func() {
- if para != nil {
- out = append(out, block{opPara, para})
- para = nil
- }
- }
-
- lines := strings.SplitAfter(text, "\n")
- unindent(lines)
- for i := 0; i < len(lines); {
- line := lines[i]
- if isBlank(line) {
- // close paragraph
- close()
- i++
- lastWasBlank = true
- continue
- }
- if indentLen(line) > 0 {
- // close paragraph
- close()
-
- // count indented or blank lines
- j := i + 1
- for j < len(lines) && (isBlank(lines[j]) || indentLen(lines[j]) > 0) {
- j++
- }
- // but not trailing blank lines
- for j > i && isBlank(lines[j-1]) {
- j--
- }
- pre := lines[i:j]
- i = j
-
- unindent(pre)
-
- // put those lines in a pre block
- out = append(out, block{opPre, pre})
- lastWasHeading = false
- continue
- }
-
- if lastWasBlank && !lastWasHeading && i+2 < len(lines) &&
- isBlank(lines[i+1]) && !isBlank(lines[i+2]) && indentLen(lines[i+2]) == 0 {
- // current line is non-blank, surrounded by blank lines
- // and the next non-blank line is not indented: this
- // might be a heading.
- if head := heading(line); head != "" {
- close()
- out = append(out, block{opHead, []string{head}})
- i += 2
- lastWasHeading = true
- continue
- }
- }
-
- // open paragraph
- lastWasBlank = false
- lastWasHeading = false
- para = append(para, lines[i])
- i++
- }
- close()
-
- return out
-}
-
-// ToText prepares comment text for presentation in textual output.
-// It wraps paragraphs of text to width or fewer Unicode code points
-// and then prefixes each line with the indent. In preformatted sections
-// (such as program text), it prefixes each non-blank line with preIndent.
+// ToText converts comment text to formatted text.
//
-// A pair of (consecutive) backticks (`) is converted to a unicode left quote (“), and a pair of (consecutive)
-// single quotes (') is converted to a unicode right quote (”).
-func ToText(w io.Writer, text string, indent, preIndent string, width int) {
- l := lineWrapper{
- out: w,
- width: width,
- indent: indent,
- }
- for _, b := range blocks(text) {
- switch b.op {
- case opPara:
- // l.write will add leading newline if required
- for _, line := range b.lines {
- line = convertQuotes(line)
- l.write(line)
- }
- l.flush()
- case opHead:
- w.Write(nl)
- for _, line := range b.lines {
- line = convertQuotes(line)
- l.write(line + "\n")
- }
- l.flush()
- case opPre:
- w.Write(nl)
- for _, line := range b.lines {
- if isBlank(line) {
- w.Write([]byte("\n"))
- } else {
- w.Write([]byte(preIndent))
- w.Write([]byte(line))
- }
- }
- }
- }
-}
-
-type lineWrapper struct {
- out io.Writer
- printed bool
- width int
- indent string
- n int
- pendSpace int
-}
-
-var nl = []byte("\n")
-var space = []byte(" ")
-var prefix = []byte("// ")
-
-func (l *lineWrapper) write(text string) {
- if l.n == 0 && l.printed {
- l.out.Write(nl) // blank line before new paragraph
- }
- l.printed = true
-
- needsPrefix := false
- isComment := strings.HasPrefix(text, "//")
- for _, f := range strings.Fields(text) {
- w := utf8.RuneCountInString(f)
- // wrap if line is too long
- if l.n > 0 && l.n+l.pendSpace+w > l.width {
- l.out.Write(nl)
- l.n = 0
- l.pendSpace = 0
- needsPrefix = isComment && !strings.HasPrefix(f, "//")
- }
- if l.n == 0 {
- l.out.Write([]byte(l.indent))
- }
- if needsPrefix {
- l.out.Write(prefix)
- needsPrefix = false
- }
- l.out.Write(space[:l.pendSpace])
- l.out.Write([]byte(f))
- l.n += l.pendSpace + w
- l.pendSpace = 1
- }
-}
-
-func (l *lineWrapper) flush() {
- if l.n == 0 {
- return
- }
- l.out.Write(nl)
- l.pendSpace = 0
- l.n = 0
+// Deprecated: ToText cannot identify documentation links
+// in the doc comment, because they depend on knowing what
+// package the text came from, which is not included in this API.
+//
+// Given the *[doc.Package] p where text was found,
+// ToText(w, text, "", "\t", 80) can be replaced by:
+//
+// w.Write(p.Text(text))
+//
+// In the general case, ToText(w, text, prefix, codePrefix, width)
+// can be replaced by:
+//
+// d := p.Parser().Parse(text)
+// pr := p.Printer()
+// pr.TextPrefix = prefix
+// pr.TextCodePrefix = codePrefix
+// pr.TextWidth = width
+// w.Write(pr.Text(d))
+//
+// See the documentation for [Package.Text] and [comment.Printer.Text]
+// for more details.
+func ToText(w io.Writer, text string, prefix, codePrefix string, width int) {
+ d := new(Package).Parser().Parse(text)
+ pr := &comment.Printer{
+ TextPrefix: prefix,
+ TextCodePrefix: codePrefix,
+ TextWidth: width,
+ }
+ w.Write(pr.Text(d))
}
diff --git a/src/go/doc/comment/html.go b/src/go/doc/comment/html.go
new file mode 100644
index 00000000000..bc076f6a584
--- /dev/null
+++ b/src/go/doc/comment/html.go
@@ -0,0 +1,169 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package comment
+
+import (
+ "bytes"
+ "fmt"
+ "strconv"
+)
+
+// An htmlPrinter holds the state needed for printing a Doc as HTML.
+type htmlPrinter struct {
+ *Printer
+ tight bool
+}
+
+// HTML returns an HTML formatting of the Doc.
+// See the [Printer] documentation for ways to customize the HTML output.
+func (p *Printer) HTML(d *Doc) []byte {
+ hp := &htmlPrinter{Printer: p}
+ var out bytes.Buffer
+ for _, x := range d.Content {
+ hp.block(&out, x)
+ }
+ return out.Bytes()
+}
+
+// block prints the block x to out.
+func (p *htmlPrinter) block(out *bytes.Buffer, x Block) {
+ switch x := x.(type) {
+ default:
+ fmt.Fprintf(out, "?%T", x)
+
+ case *Paragraph:
+ if !p.tight {
+ out.WriteString("<p>")
+ }
+ p.text(out, x.Text)
+ out.WriteString("\n")
+
+ case *Heading:
+ out.WriteString("<h")
+ h := strconv.Itoa(p.headingLevel())
+ out.WriteString(h)
+ if id := p.headingID(x); id != "" {
+ out.WriteString(` id="`)
+ p.escape(out, id)
+ out.WriteString(`"`)
+ }
+ out.WriteString(">")
+ p.text(out, x.Text)
+ out.WriteString("</h")
+ out.WriteString(h)
+ out.WriteString(">\n")
+
+ case *Code:
+ out.WriteString("<pre>")
+ p.escape(out, x.Text)
+ out.WriteString("</pre>\n")
+
+ case *List:
+ kind := "ol>\n"
+ if x.Items[0].Number == "" {
+ kind = "ul>\n"
+ }
+ out.WriteString("<")
+ out.WriteString(kind)
+ next := "1"
+ for _, item := range x.Items {
+ out.WriteString("<li")
+ if n := item.Number; n != "" {
+ if n != next {
+ out.WriteString(` value="`)
+ out.WriteString(n)
+ out.WriteString(`"`)
+ next = n
+ }
+ next = inc(next)
+ }
+ out.WriteString(">")
+ p.tight = !x.BlankBetween()
+ for _, blk := range item.Content {
+ p.block(out, blk)
+ }
+ p.tight = false
+ }
+ out.WriteString("</")
+ out.WriteString(kind)
+ }
+}
+
+// inc increments the decimal string s.
+// For example, inc("1199") == "1200".
+func inc(s string) string {
+ b := []byte(s)
+ for i := len(b) - 1; i >= 0; i-- {
+ if b[i] < '9' {
+ b[i]++
+ return string(b)
+ }
+ b[i] = '0'
+ }
+ return "1" + string(b)
+}
+
+// text prints the text sequence x to out.
+func (p *htmlPrinter) text(out *bytes.Buffer, x []Text) {
+ for _, t := range x {
+ switch t := t.(type) {
+ case Plain:
+ p.escape(out, string(t))
+ case Italic:
+ out.WriteString("<i>")
+ p.escape(out, string(t))
+ out.WriteString("</i>")
+ case *Link:
+ out.WriteString(`<a href="`)
+ p.escape(out, t.URL)
+ out.WriteString(`">`)
+ p.text(out, t.Text)
+ out.WriteString("</a>")
+ case *DocLink:
+ url := p.docLinkURL(t)
+ if url != "" {
+ out.WriteString(`<a href="`)
+ p.escape(out, url)
+ out.WriteString(`">`)
+ }
+ p.text(out, t.Text)
+ if url != "" {
+ out.WriteString("</a>")
+ }
+ }
+ }
+}
+
+// escape prints s to out as plain text,
+// escaping < & " ' and > to avoid being misinterpreted
+// in larger HTML constructs.
+func (p *htmlPrinter) escape(out *bytes.Buffer, s string) {
+ start := 0
+ for i := 0; i < len(s); i++ {
+ switch s[i] {
+ case '<':
+ out.WriteString(s[start:i])
+ out.WriteString("&lt;")
+ start = i + 1
+ case '&':
+ out.WriteString(s[start:i])
+ out.WriteString("&amp;")
+ start = i + 1
+ case '"':
+ out.WriteString(s[start:i])
+ out.WriteString("&quot;")
+ start = i + 1
+ case '\'':
+ out.WriteString(s[start:i])
+ out.WriteString("&apos;")
+ start = i + 1
+ case '>':
+ out.WriteString(s[start:i])
+ out.WriteString("&gt;")
+ start = i + 1
+ }
+ }
+ out.WriteString(s[start:])
+}
diff --git a/src/go/doc/comment/markdown.go b/src/go/doc/comment/markdown.go
new file mode 100644
index 00000000000..d8550f2e39d
--- /dev/null
+++ b/src/go/doc/comment/markdown.go
@@ -0,0 +1,188 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package comment
+
+import (
+ "bytes"
+ "fmt"
+ "strings"
+)
+
+// An mdPrinter holds the state needed for printing a Doc as Markdown.
+type mdPrinter struct {
+ *Printer
+ headingPrefix string
+ raw bytes.Buffer
+}
+
+// Markdown returns a Markdown formatting of the Doc.
+// See the [Printer] documentation for ways to customize the Markdown output.
+func (p *Printer) Markdown(d *Doc) []byte {
+ mp := &mdPrinter{
+ Printer: p,
+ headingPrefix: strings.Repeat("#", p.headingLevel()) + " ",
+ }
+
+ var out bytes.Buffer
+ for i, x := range d.Content {
+ if i > 0 {
+ out.WriteByte('\n')
+ }
+ mp.block(&out, x)
+ }
+ return out.Bytes()
+}
+
+// block prints the block x to out.
+func (p *mdPrinter) block(out *bytes.Buffer, x Block) {
+ switch x := x.(type) {
+ default:
+ fmt.Fprintf(out, "?%T", x)
+
+ case *Paragraph:
+ p.text(out, x.Text)
+ out.WriteString("\n")
+
+ case *Heading:
+ out.WriteString(p.headingPrefix)
+ p.text(out, x.Text)
+ if id := p.headingID(x); id != "" {
+ out.WriteString(" {#")
+ out.WriteString(id)
+ out.WriteString("}")
+ }
+ out.WriteString("\n")
+
+ case *Code:
+ md := x.Text
+ for md != "" {
+ var line string
+ line, md, _ = strings.Cut(md, "\n")
+ if line != "" {
+ out.WriteString("\t")
+ out.WriteString(line)
+ }
+ out.WriteString("\n")
+ }
+
+ case *List:
+ loose := x.BlankBetween()
+ for i, item := range x.Items {
+ if i > 0 && loose {
+ out.WriteString("\n")
+ }
+ if n := item.Number; n != "" {
+ out.WriteString(" ")
+ out.WriteString(n)
+ out.WriteString(". ")
+ } else {
+ out.WriteString(" - ") // SP SP - SP
+ }
+ for i, blk := range item.Content {
+ const fourSpace = " "
+ if i > 0 {
+ out.WriteString("\n" + fourSpace)
+ }
+ p.text(out, blk.(*Paragraph).Text)
+ out.WriteString("\n")
+ }
+ }
+ }
+}
+
+// text prints the text sequence x to out.
+func (p *mdPrinter) text(out *bytes.Buffer, x []Text) {
+ p.raw.Reset()
+ p.rawText(&p.raw, x)
+ line := bytes.TrimSpace(p.raw.Bytes())
+ if len(line) == 0 {
+ return
+ }
+ switch line[0] {
+ case '+', '-', '*', '#':
+ // Escape what would be the start of an unordered list or heading.
+ out.WriteByte('\\')
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ i := 1
+ for i < len(line) && '0' <= line[i] && line[i] <= '9' {
+ i++
+ }
+ if i < len(line) && (line[i] == '.' || line[i] == ')') {
+ // Escape what would be the start of an ordered list.
+ out.Write(line[:i])
+ out.WriteByte('\\')
+ line = line[i:]
+ }
+ }
+ out.Write(line)
+}
+
+// rawText prints the text sequence x to out,
+// without worrying about escaping characters
+// that have special meaning at the start of a Markdown line.
+func (p *mdPrinter) rawText(out *bytes.Buffer, x []Text) {
+ for _, t := range x {
+ switch t := t.(type) {
+ case Plain:
+ p.escape(out, string(t))
+ case Italic:
+ out.WriteString("*")
+ p.escape(out, string(t))
+ out.WriteString("*")
+ case *Link:
+ out.WriteString("[")
+ p.rawText(out, t.Text)
+ out.WriteString("](")
+ out.WriteString(t.URL)
+ out.WriteString(")")
+ case *DocLink:
+ url := p.docLinkURL(t)
+ if url != "" {
+ out.WriteString("[")
+ }
+ p.rawText(out, t.Text)
+ if url != "" {
+ out.WriteString("](")
+ url = strings.ReplaceAll(url, "(", "%28")
+ url = strings.ReplaceAll(url, ")", "%29")
+ out.WriteString(url)
+ out.WriteString(")")
+ }
+ }
+ }
+}
+
+// escape prints s to out as plain text,
+// escaping special characters to avoid being misinterpreted
+// as Markdown markup sequences.
+func (p *mdPrinter) escape(out *bytes.Buffer, s string) {
+ start := 0
+ for i := 0; i < len(s); i++ {
+ switch s[i] {
+ case '\n':
+ // Turn all \n into spaces, for a few reasons:
+ // - Avoid introducing paragraph breaks accidentally.
+ // - Avoid the need to reindent after the newline.
+ // - Avoid problems with Markdown renderers treating
+ // every mid-paragraph newline as a <br>.
+ out.WriteString(s[start:i])
+ out.WriteByte(' ')
+ start = i + 1
+ continue
+ case '`', '_', '*', '[', '<', '\\':
+ // Not all of these need to be escaped all the time,
+ // but is valid and easy to do so.
+ // We assume the Markdown is being passed to a
+ // Markdown renderer, not edited by a person,
+ // so it's fine to have escapes that are not strictly
+ // necessary in some cases.
+ out.WriteString(s[start:i])
+ out.WriteByte('\\')
+ out.WriteByte(s[i])
+ start = i + 1
+ }
+ }
+ out.WriteString(s[start:])
+}
diff --git a/src/go/doc/comment/mkstd.sh b/src/go/doc/comment/mkstd.sh
new file mode 100755
index 00000000000..c9dee8c55e6
--- /dev/null
+++ b/src/go/doc/comment/mkstd.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+# Copyright 2022 The Go 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 could be a good use for embed but go/doc/comment
+# is built into the bootstrap go command, so it can't use embed.
+# Also not using embed lets us emit a string array directly
+# and avoid init-time work.
+
+(
+echo "// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Code generated by 'go generate' DO NOT EDIT.
+//go:generate ./mkstd.sh
+
+package comment
+
+var stdPkgs = []string{"
+go list std | grep -v / | sort | sed 's/.*/"&",/'
+echo "}"
+) | gofmt >std.go.tmp && mv std.go.tmp std.go
diff --git a/src/go/doc/comment/old_test.go b/src/go/doc/comment/old_test.go
new file mode 100644
index 00000000000..944f94d16d9
--- /dev/null
+++ b/src/go/doc/comment/old_test.go
@@ -0,0 +1,80 @@
+// 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.
+
+// These tests are carried forward from the old go/doc implementation.
+
+package comment
+
+import "testing"
+
+var oldHeadingTests = []struct {
+ line string
+ ok bool
+}{
+ {"Section", true},
+ {"A typical usage", true},
+ {"ΔΛΞ is Greek", true},
+ {"Foo 42", true},
+ {"", false},
+ {"section", false},
+ {"A typical usage:", false},
+ {"This code:", false},
+ {"δ is Greek", false},
+ {"Foo §", false},
+ {"Fermat's Last Sentence", true},
+ {"Fermat's", true},
+ {"'sX", false},
+ {"Ted 'Too' Bar", false},
+ {"Use n+m", false},
+ {"Scanning:", false},
+ {"N:M", false},
+}
+
+func TestIsOldHeading(t *testing.T) {
+ for _, tt := range oldHeadingTests {
+ if isOldHeading(tt.line, []string{"Text.", "", tt.line, "", "Text."}, 2) != tt.ok {
+ t.Errorf("isOldHeading(%q) = %v, want %v", tt.line, !tt.ok, tt.ok)
+ }
+ }
+}
+
+var autoURLTests = []struct {
+ in, out string
+}{
+ {"", ""},
+ {"http://[::1]:8080/foo.txt", "http://[::1]:8080/foo.txt"},
+ {"https://www.google.com) after", "https://www.google.com"},
+ {"https://www.google.com:30/x/y/z:b::c. After", "https://www.google.com:30/x/y/z:b::c"},
+ {"http://www.google.com/path/:;!-/?query=%34b#093124", "http://www.google.com/path/:;!-/?query=%34b#093124"},
+ {"http://www.google.com/path/:;!-/?query=%34bar#093124", "http://www.google.com/path/:;!-/?query=%34bar#093124"},
+ {"http://www.google.com/index.html! After", "http://www.google.com/index.html"},
+ {"http://www.google.com/", "http://www.google.com/"},
+ {"https://www.google.com/", "https://www.google.com/"},
+ {"http://www.google.com/path.", "http://www.google.com/path"},
+ {"http://en.wikipedia.org/wiki/Camellia_(cipher)", "http://en.wikipedia.org/wiki/Camellia_(cipher)"},
+ {"http://www.google.com/)", "http://www.google.com/"},
+ {"http://gmail.com)", "http://gmail.com"},
+ {"http://gmail.com))", "http://gmail.com"},
+ {"http://gmail.com ((http://gmail.com)) ()", "http://gmail.com"},
+ {"http://example.com/ quux!", "http://example.com/"},
+ {"http://example.com/%2f/ /world.", "http://example.com/%2f/"},
+ {"http: ipsum //host/path", ""},
+ {"javascript://is/not/linked", ""},
+ {"http://foo", "http://foo"},
+ {"https://www.example.com/person/][Person Name]]", "https://www.example.com/person/"},
+ {"http://golang.org/)", "http://golang.org/"},
+ {"http://golang.org/hello())", "http://golang.org/hello()"},
+ {"http://git.qemu.org/?p=qemu.git;a=blob;f=qapi-schema.json;hb=HEAD", "http://git.qemu.org/?p=qemu.git;a=blob;f=qapi-schema.json;hb=HEAD"},
+ {"https://foo.bar/bal/x(])", "https://foo.bar/bal/x"}, // inner ] causes (]) to be cut off from URL
+ {"http://bar(])", "http://bar"}, // same
+}
+
+func TestAutoURL(t *testing.T) {
+ for _, tt := range autoURLTests {
+ url, ok := autoURL(tt.in)
+ if url != tt.out || ok != (tt.out != "") {
+ t.Errorf("autoURL(%q) = %q, %v, want %q, %v", tt.in, url, ok, tt.out, tt.out != "")
+ }
+ }
+}
diff --git a/src/go/doc/comment/parse.go b/src/go/doc/comment/parse.go
new file mode 100644
index 00000000000..83b37c32c53
--- /dev/null
+++ b/src/go/doc/comment/parse.go
@@ -0,0 +1,1151 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package comment
+
+import (
+ "sort"
+ "strings"
+ "unicode"
+ "unicode/utf8"
+)
+
+// A Doc is a parsed Go doc comment.
+type Doc struct {
+ // Content is the sequence of content blocks in the comment.
+ Content []Block
+
+ // Links is the link definitions in the comment.
+ Links []*LinkDef
+}
+
+// A LinkDef is a single link definition.
+type LinkDef struct {
+ Text string // the link text
+ URL string // the link URL
+ Used bool // whether the comment uses the definition
+}
+
+// A Block is block-level content in a doc comment,
+// one of [*Code], [*Heading], [*List], or [*Paragraph].
+type Block interface {
+ block()
+}
+
+// A Heading is a doc comment heading.
+type Heading struct {
+ Text []Text // the heading text
+}
+
+func (*Heading) block() {}
+
+// A List is a numbered or bullet list.
+// Lists are always non-empty: len(Items) > 0.
+// In a numbered list, every Items[i].Number is a non-empty string.
+// In a bullet list, every Items[i].Number is an empty string.
+type List struct {
+ // Items is the list items.
+ Items []*ListItem
+
+ // ForceBlankBefore indicates that the list must be
+ // preceded by a blank line when reformatting the comment,
+ // overriding the usual conditions. See the BlankBefore method.
+ //
+ // The comment parser sets ForceBlankBefore for any list
+ // that is preceded by a blank line, to make sure
+ // the blank line is preserved when printing.
+ ForceBlankBefore bool
+
+ // ForceBlankBetween indicates that list items must be
+ // separated by blank lines when reformatting the comment,
+ // overriding the usual conditions. See the BlankBetween method.
+ //
+ // The comment parser sets ForceBlankBetween for any list
+ // that has a blank line between any two of its items, to make sure
+ // the blank lines are preserved when printing.
+ ForceBlankBetween bool
+}
+
+func (*List) block() {}
+
+// BlankBefore reports whether a reformatting of the comment
+// should include a blank line before the list.
+// The default rule is the same as for [BlankBetween]:
+// if the list item content contains any blank lines
+// (meaning at least one item has multiple paragraphs)
+// then the list itself must be preceded by a blank line.
+// A preceding blank line can be forced by setting [List].ForceBlankBefore.
+func (l *List) BlankBefore() bool {
+ return l.ForceBlankBefore || l.BlankBetween()
+}
+
+// BlankBetween reports whether a reformatting of the comment
+// should include a blank line between each pair of list items.
+// The default rule is that if the list item content contains any blank lines
+// (meaning at least one item has multiple paragraphs)
+// then list items must themselves be separated by blank lines.
+// Blank line separators can be forced by setting [List].ForceBlankBetween.
+func (l *List) BlankBetween() bool {
+ if l.ForceBlankBetween {
+ return true
+ }
+ for _, item := range l.Items {
+ if len(item.Content) != 1 {
+ // Unreachable for parsed comments today,
+ // since the only way to get multiple item.Content
+ // is multiple paragraphs, which must have been
+ // separated by a blank line.
+ return true
+ }
+ }
+ return false
+}
+
+// A ListItem is a single item in a numbered or bullet list.
+type ListItem struct {
+ // Number is a decimal string in a numbered list
+ // or an empty string in a bullet list.
+ Number string // "1", "2", ...; "" for bullet list
+
+ // Content is the list content.
+ // Currently, restrictions in the parser and printer
+ // require every element of Content to be a *Paragraph.
+ Content []Block // Content of this item.
+}
+
+// A Paragraph is a paragraph of text.
+type Paragraph struct {
+ Text []Text
+}
+
+func (*Paragraph) block() {}
+
+// A Code is a preformatted code block.
+type Code struct {
+ // Text is the preformatted text, ending with a newline character.
+ // It may be multiple lines, each of which ends with a newline character.
+ // It is never empty, nor does it start or end with a blank line.
+ Text string
+}
+
+func (*Code) block() {}
+
+// A Text is text-level content in a doc comment,
+// one of [Plain], [Italic], [*Link], or [*DocLink].
+type Text interface {
+ text()
+}
+
+// A Plain is a string rendered as plain text (not italicized).
+type Plain string
+
+func (Plain) text() {}
+
+// An Italic is a string rendered as italicized text.
+type Italic string
+
+func (Italic) text() {}
+
+// A Link is a link to a specific URL.
+type Link struct {
+ Auto bool // is this an automatic (implicit) link of a literal URL?
+ Text []Text // text of link
+ URL string // target URL of link
+}
+
+func (*Link) text() {}
+
+// A DocLink is a link to documentation for a Go package or symbol.
+type DocLink struct {
+ Text []Text // text of link
+
+ // ImportPath, Recv, and Name identify the Go package or symbol
+ // that is the link target. The potential combinations of
+ // non-empty fields are:
+ // - ImportPath: a link to another package
+ // - ImportPath, Name: a link to a const, func, type, or var in another package
+ // - ImportPath, Recv, Name: a link to a method in another package
+ // - Name: a link to a const, func, type, or var in this package
+ // - Recv, Name: a link to a method in this package
+ ImportPath string // import path
+ Recv string // receiver type, without any pointer star, for methods
+ Name string // const, func, type, var, or method name
+}
+
+func (*DocLink) text() {}
+
+// A Parser is a doc comment parser.
+// The fields in the struct can be filled in before calling Parse
+// in order to customize the details of the parsing process.
+type Parser struct {
+ // Words is a map of Go identifier words that
+ // should be italicized and potentially linked.
+ // If Words[w] is the empty string, then the word w
+ // is only italicized. Otherwise it is linked, using
+ // Words[w] as the link target.
+ // Words corresponds to the [go/doc.ToHTML] words parameter.
+ Words map[string]string
+
+ // LookupPackage resolves a package name to an import path.
+ //
+ // If LookupPackage(name) returns ok == true, then [name]
+ // (or [name.Sym] or [name.Sym.Method])
+ // is considered a documentation link to importPath's package docs.
+ // It is valid to return "", true, in which case name is considered
+ // to refer to the current package.
+ //
+ // If LookupPackage(name) returns ok == false,
+ // then [name] (or [name.Sym] or [name.Sym.Method])
+ // will not be considered a documentation link,
+ // except in the case where name is the full (but single-element) import path
+ // of a package in the standard library, such as in [math] or [io.Reader].
+ // LookupPackage is still called for such names,
+ // in order to permit references to imports of other packages
+ // with the same package names.
+ //
+ // Setting LookupPackage to nil is equivalent to setting it to
+ // a function that always returns "", false.
+ LookupPackage func(name string) (importPath string, ok bool)
+
+ // LookupSym reports whether a symbol name or method name
+ // exists in the current package.
+ //
+ // If LookupSym("", "Name") returns true, then [Name]
+ // is considered a documentation link for a const, func, type, or var.
+ //
+ // Similarly, if LookupSym("Recv", "Name") returns true,
+ // then [Recv.Name] is considered a documentation link for
+ // type Recv's method Name.
+ //
+ // Setting LookupSym to nil is equivalent to setting it to a function
+ // that always returns false.
+ LookupSym func(recv, name string) (ok bool)
+}
+
+// parseDoc is parsing state for a single doc comment.
+type parseDoc struct {
+ *Parser
+ *Doc
+ links map[string]*LinkDef
+ lines []string
+ lookupSym func(recv, name string) bool
+}
+
+// lookupPkg is called to look up the pkg in [pkg], [pkg.Name], and [pkg.Name.Recv].
+// If pkg has a slash, it is assumed to be the full import path and is returned with ok = true.
+//
+// Otherwise, pkg is probably a simple package name like "rand" (not "crypto/rand" or "math/rand").
+// d.LookupPackage provides a way for the caller to allow resolving such names with reference
+// to the imports in the surrounding package.
+//
+// There is one collision between these two cases: single-element standard library names
+// like "math" are full import paths but don't contain slashes. We let d.LookupPackage have
+// the first chance to resolve it, in case there's a different package imported as math,
+// and otherwise we refer to a built-in list of single-element standard library package names.
+func (d *parseDoc) lookupPkg(pkg string) (importPath string, ok bool) {
+ if strings.Contains(pkg, "/") { // assume a full import path
+ if validImportPath(pkg) {
+ return pkg, true
+ }
+ return "", false
+ }
+ if d.LookupPackage != nil {
+ // Give LookupPackage a chance.
+ if path, ok := d.LookupPackage(pkg); ok {
+ return path, true
+ }
+ }
+ return DefaultLookupPackage(pkg)
+}
+
+func isStdPkg(path string) bool {
+ // TODO(rsc): Use sort.Find.
+ i := sort.Search(len(stdPkgs), func(i int) bool { return stdPkgs[i] >= path })
+ return i < len(stdPkgs) && stdPkgs[i] == path
+}
+
+// DefaultLookupPackage is the default package lookup
+// function, used when [Parser].LookupPackage is nil.
+// It recognizes names of the packages from the standard
+// library with single-element import paths, such as math,
+// which would otherwise be impossible to name.
+//
+// Note that the go/doc package provides a more sophisticated
+// lookup based on the imports used in the current package.
+func DefaultLookupPackage(name string) (importPath string, ok bool) {
+ if isStdPkg(name) {
+ return name, true
+ }
+ return "", false
+}
+
+// Parse parses the doc comment text and returns the *Doc form.
+// Comment markers (/* // and */) in the text must have already been removed.
+func (p *Parser) Parse(text string) *Doc {
+ lines := unindent(strings.Split(text, "\n"))
+ d := &parseDoc{
+ Parser: p,
+ Doc: new(Doc),
+ links: make(map[string]*LinkDef),
+ lines: lines,
+ lookupSym: func(recv, name string) bool { return false },
+ }
+ if p.LookupSym != nil {
+ d.lookupSym = p.LookupSym
+ }
+
+ // First pass: break into block structure and collect known links.
+ // The text is all recorded as Plain for now.
+ // TODO: Break into actual block structure.
+ didHeading := false
+ all := lines
+ for len(lines) > 0 {
+ line := lines[0]
+ n := len(lines)
+ var b Block
+
+ switch {
+ case line == "":
+ // emit nothing
+
+ case isList(line):
+ prevWasBlank := len(lines) < len(all) && all[len(all)-len(lines)-1] == ""
+ b, lines = d.list(lines, prevWasBlank)
+
+ case isIndented(line):
+ b, lines = d.code(lines)
+
+ case (len(lines) == 1 || lines[1] == "") && !didHeading && isOldHeading(line, all, len(all)-n):
+ b = d.oldHeading(line)
+ didHeading = true
+
+ case (len(lines) == 1 || lines[1] == "") && isHeading(line):
+ b = d.heading(line)
+ didHeading = true
+
+ default:
+ b, lines = d.paragraph(lines)
+ didHeading = false
+ }
+
+ if b != nil {
+ d.Content = append(d.Content, b)
+ }
+ if len(lines) == n {
+ lines = lines[1:]
+ }
+ }
+
+ // Second pass: interpret all the Plain text now that we know the links.
+ for _, b := range d.Content {
+ switch b := b.(type) {
+ case *Paragraph:
+ b.Text = d.parseLinkedText(string(b.Text[0].(Plain)))
+ }
+ }
+
+ return d.Doc
+}
+
+// unindent removes any common space/tab prefix
+// from each line in lines, returning a copy of lines in which
+// those prefixes have been trimmed from each line.
+func unindent(lines []string) []string {
+ // Trim leading and trailing blank lines.
+ for len(lines) > 0 && isBlank(lines[0]) {
+ lines = lines[1:]
+ }
+ for len(lines) > 0 && isBlank(lines[len(lines)-1]) {
+ lines = lines[:len(lines)-1]
+ }
+ if len(lines) == 0 {
+ return nil
+ }
+
+ // Compute and remove common indentation.
+ prefix := leadingSpace(lines[0])
+ for _, line := range lines[1:] {
+ if !isBlank(line) {
+ prefix = commonPrefix(prefix, leadingSpace(line))
+ }
+ }
+
+ out := make([]string, len(lines))
+ for i, line := range lines {
+ line = strings.TrimPrefix(line, prefix)
+ if strings.TrimSpace(line) == "" {
+ line = ""
+ }
+ out[i] = line
+ }
+ for len(out) > 0 && out[0] == "" {
+ out = out[1:]
+ }
+ for len(out) > 0 && out[len(out)-1] == "" {
+ out = out[:len(out)-1]
+ }
+ return out
+}
+
+// isBlank reports whether s is a blank line.
+func isBlank(s string) bool {
+ return len(s) == 0 || (len(s) == 1 && s[0] == '\n')
+}
+
+// commonPrefix returns the longest common prefix of a and b.
+func commonPrefix(a, b string) string {
+ i := 0
+ for i < len(a) && i < len(b) && a[i] == b[i] {
+ i++
+ }
+ return a[0:i]
+}
+
+// leadingSpace returns the longest prefix of s consisting of spaces and tabs.
+func leadingSpace(s string) string {
+ i := 0
+ for i < len(s) && (s[i] == ' ' || s[i] == '\t') {
+ i++
+ }
+ return s[:i]
+}
+
+// isOldHeading reports whether line is an old-style section heading.
+// line is all[off].
+func isOldHeading(line string, all []string, off int) bool {
+ if off <= 0 || all[off-1] != "" || off+2 >= len(all) || all[off+1] != "" || leadingSpace(all[off+2]) != "" {
+ return false
+ }
+
+ line = strings.TrimSpace(line)
+
+ // a heading must start with an uppercase letter
+ r, _ := utf8.DecodeRuneInString(line)
+ if !unicode.IsLetter(r) || !unicode.IsUpper(r) {
+ return false
+ }
+
+ // it must end in a letter or digit:
+ r, _ = utf8.DecodeLastRuneInString(line)
+ if !unicode.IsLetter(r) && !unicode.IsDigit(r) {
+ return false
+ }
+
+ // exclude lines with illegal characters. we allow "(),"
+ if strings.ContainsAny(line, ";:!?+*/=[]{}_^°&§~%#@<\">\\") {
+ return false
+ }
+
+ // allow "'" for possessive "'s" only
+ for b := line; ; {
+ var ok bool
+ if _, b, ok = strings.Cut(b, "'"); !ok {
+ break
+ }
+ if b != "s" && !strings.HasPrefix(b, "s ") {
+ return false // ' not followed by s and then end-of-word
+ }
+ }
+
+ // allow "." when followed by non-space
+ for b := line; ; {
+ var ok bool
+ if _, b, ok = strings.Cut(b, "."); !ok {
+ break
+ }
+ if b == "" || strings.HasPrefix(b, " ") {
+ return false // not followed by non-space
+ }
+ }
+
+ return true
+}
+
+// oldHeading returns the *Heading for the given old-style section heading line.
+func (d *parseDoc) oldHeading(line string) Block {
+ return &Heading{Text: []Text{Plain(strings.TrimSpace(line))}}
+}
+
+// isHeading reports whether line is a new-style section heading.
+func isHeading(line string) bool {
+ return len(line) >= 2 &&
+ line[0] == '#' &&
+ (line[1] == ' ' || line[1] == '\t') &&
+ strings.TrimSpace(line) != "#"
+}
+
+// heading returns the *Heading for the given new-style section heading line.
+func (d *parseDoc) heading(line string) Block {
+ return &Heading{Text: []Text{Plain(strings.TrimSpace(line[1:]))}}
+}
+
+// code returns a code block built from the indented text
+// at the start of lines, along with the remainder of the lines.
+// If there is no indented text at the start, or if the indented
+// text consists only of empty lines, code returns a nil Block.
+func (d *parseDoc) code(lines []string) (b Block, rest []string) {
+ lines, rest = indented(lines)
+ body := unindent(lines)
+ if len(body) == 0 {
+ return nil, rest
+ }
+ body = append(body, "") // to get final \n from Join
+ return &Code{Text: strings.Join(body, "\n")}, rest
+}
+
+// isIndented reports whether the line is indented,
+// meaning it starts with a space or tab.
+func isIndented(line string) bool {
+ return line != "" && (line[0] == ' ' || line[0] == '\t')
+}
+
+// indented splits lines into an initial indented section
+// and the remaining lines, returning the two halves.
+func indented(lines []string) (indented, rest []string) {
+ // Blank lines mid-run are OK, but not at the end.
+ i := 0
+ for i < len(lines) && (isIndented(lines[i]) || lines[i] == "") {
+ i++
+ }
+ for i > 0 && lines[i-1] == "" {
+ i--
+ }
+ return lines[:i], lines[i:]
+}
+
+// paragraph returns a paragraph block built from the
+// unindented text at the start of lines, along with the remainder of the lines.
+// If there is no unindented text at the start of lines,
+// then paragraph returns a nil Block.
+func (d *parseDoc) paragraph(lines []string) (b Block, rest []string) {
+ // Paragraph is interrupted by any indented line,
+ // which is either a list or a code block,
+ // and of course by a blank line.
+ // It is not interrupted by a # line - headings must stand alone.
+ i := 0
+ for i < len(lines) && lines[i] != "" && !isIndented(lines[i]) {
+ i++
+ }
+ lines, rest = lines[:i], lines[i:]
+ if len(lines) == 0 {
+ return nil, rest
+ }
+
+ // Is this a block of known links? Handle.
+ var defs []*LinkDef
+ for _, line := range lines {
+ def, ok := parseLink(line)
+ if !ok {
+ goto NoDefs
+ }
+ defs = append(defs, def)
+ }
+ for _, def := range defs {
+ d.Links = append(d.Links, def)
+ if d.links[def.Text] == nil {
+ d.links[def.Text] = def
+ }
+ }
+ return nil, rest
+NoDefs:
+
+ return &Paragraph{Text: []Text{Plain(strings.Join(lines, "\n"))}}, rest
+}
+
+// parseLink parses a single link definition line:
+//
+// [text]: url
+//
+// It returns the link definition and whether the line was well formed.
+func parseLink(line string) (*LinkDef, bool) {
+ if line == "" || line[0] != '[' {
+ return nil, false
+ }
+ i := strings.Index(line, "]:")
+ if i < 0 || i+3 >= len(line) || (line[i+2] != ' ' && line[i+2] != '\t') {
+ return nil, false
+ }
+
+ text := line[1:i]
+ url := strings.TrimSpace(line[i+3:])
+ j := strings.Index(url, "://")
+ if j < 0 || !isScheme(url[:j]) {
+ return nil, false
+ }
+
+ // Line has right form and has valid scheme://.
+ // That's good enough for us - we are not as picky
+ // about the characters beyond the :// as we are
+ // when extracting inline URLs from text.
+ return &LinkDef{Text: text, URL: url}, true
+}
+
+// list returns a list built from the indented text at the start of lines,
+// using forceBlankBefore as the value of the List's ForceBlankBefore field.
+// The caller is responsible for ensuring that the first line of lines
+// satisfies isList.
+// list returns the *List as a Block along with the remaining lines.
+func (d *parseDoc) list(lines []string, forceBlankBefore bool) (b Block, rest []string) {
+ lines, rest = indented(lines)
+
+ num, _, _ := listMarker(lines[0])
+ var (
+ list *List = &List{ForceBlankBefore: forceBlankBefore}
+ item *ListItem
+ text []string
+ )
+ flush := func() {
+ if item != nil {
+ if para, _ := d.paragraph(text); para != nil {
+ item.Content = append(item.Content, para)
+ }
+ }
+ text = nil
+ }
+
+ for _, line := range lines {
+ if n, after, ok := listMarker(line); ok && (n != "") == (num != "") {
+ // start new list item
+ flush()
+
+ item = &ListItem{Number: n}
+ list.Items = append(list.Items, item)
+ line = after
+ }
+ line = strings.TrimSpace(line)
+ if line == "" {
+ list.ForceBlankBetween = true
+ flush()
+ continue
+ }
+ text = append(text, strings.TrimSpace(line))
+ }
+ flush()
+ return list, rest
+}
+
+// listMarker parses the line as an indented line beginning with a list marker.
+// If it can do that, it returns the numeric marker ("" for a bullet list),
+// the rest of the line, and ok == true.
+// Otherwise, it returns "", "", false.
+func listMarker(line string) (num, rest string, ok bool) {
+ if !isIndented(line) {
+ return "", "", false
+ }
+ line = strings.TrimSpace(line)
+ if line == "" {
+ return "", "", false
+ }
+
+ // Can we find a marker?
+ if r, n := utf8.DecodeRuneInString(line); r == '•' || r == '*' || r == '+' || r == '-' {
+ num, rest = "", line[n:]
+ } else if '0' <= line[0] && line[0] <= '9' {
+ n := 1
+ for n < len(line) && '0' <= line[n] && line[n] <= '9' {
+ n++
+ }
+ if n >= len(line) || (line[n] != '.' && line[n] != ')') {
+ return "", "", false
+ }
+ num, rest = line[:n], line[n+1:]
+ } else {
+ return "", "", false
+ }
+
+ if !isIndented(rest) || strings.TrimSpace(rest) == "" {
+ return "", "", false
+ }
+
+ return num, rest, true
+}
+
+// isList reports whether the line is the first line of a list,
+// meaning is indented and starts with a list marker.
+func isList(line string) bool {
+ _, _, ok := listMarker(line)
+ return ok
+}
+
+// parseLinkedText parses text that is allowed to contain explicit links,
+// such as [math.Sin] or [Go home page], into a slice of Text items.
+//
+// A “pkg” is only assumed to be a full import path if it starts with
+// a domain name (a path element with a dot) or is one of the packages
+// from the standard library (“[os]”, “[encoding/json]”, and so on).
+// To avoid problems with maps, generics, and array types, doc links
+// must be both preceded and followed by punctuation, spaces, tabs,
+// or the start or end of a line. An example problem would be treating
+// map[ast.Expr]TypeAndValue as containing a link.
+func (d *parseDoc) parseLinkedText(text string) []Text {
+ var out []Text
+ wrote := 0
+ flush := func(i int) {
+ if wrote < i {
+ out = d.parseText(out, text[wrote:i], true)
+ wrote = i
+ }
+ }
+
+ start := -1
+ var buf []byte
+ for i := 0; i < len(text); i++ {
+ c := text[i]
+ if c == '\n' || c == '\t' {
+ c = ' '
+ }
+ switch c {
+ case '[':
+ start = i
+ case ']':
+ if start >= 0 {
+ if def, ok := d.links[string(buf)]; ok {
+ def.Used = true
+ flush(start)
+ out = append(out, &Link{
+ Text: d.parseText(nil, text[start+1:i], false),
+ URL: def.URL,
+ })
+ wrote = i + 1
+ } else if link, ok := d.docLink(text[start+1:i], text[:start], text[i+1:]); ok {
+ flush(start)
+ link.Text = d.parseText(nil, text[start+1:i], false)
+ out = append(out, link)
+ wrote = i + 1
+ }
+ }
+ start = -1
+ buf = buf[:0]
+ }
+ if start >= 0 && i != start {
+ buf = append(buf, c)
+ }
+ }
+
+ flush(len(text))
+ return out
+}
+
+// docLink parses text, which was found inside [ ] brackets,
+// as a doc link if possible, returning the DocLink and ok == true
+// or else nil, false.
+// The before and after strings are the text before the [ and after the ]
+// on the same line. Doc links must be preceded and followed by
+// punctuation, spaces, tabs, or the start or end of a line.
+func (d *parseDoc) docLink(text, before, after string) (link *DocLink, ok bool) {
+ if before != "" {
+ r, _ := utf8.DecodeLastRuneInString(before)
+ if !unicode.IsPunct(r) && r != ' ' && r != '\t' && r != '\n' {
+ return nil, false
+ }
+ }
+ if after != "" {
+ r, _ := utf8.DecodeRuneInString(after)
+ if !unicode.IsPunct(r) && r != ' ' && r != '\t' && r != '\n' {
+ return nil, false
+ }
+ }
+ if strings.HasPrefix(text, "*") {
+ text = text[1:]
+ }
+ pkg, name, ok := splitDocName(text)
+ var recv string
+ if ok {
+ pkg, recv, _ = splitDocName(pkg)
+ }
+ if pkg != "" {
+ if pkg, ok = d.lookupPkg(pkg); !ok {
+ return nil, false
+ }
+ } else {
+ if ok = d.lookupSym(recv, name); !ok {
+ return nil, false
+ }
+ }
+ link = &DocLink{
+ ImportPath: pkg,
+ Recv: recv,
+ Name: name,
+ }
+ return link, true
+}
+
+// If text is of the form before.Name, where Name is a capitalized Go identifier,
+// then splitDocName returns before, name, true.
+// Otherwise it returns text, "", false.
+func splitDocName(text string) (before, name string, foundDot bool) {
+ i := strings.LastIndex(text, ".")
+ name = text[i+1:]
+ if !isName(name) {
+ return text, "", false
+ }
+ if i >= 0 {
+ before = text[:i]
+ }
+ return before, name, true
+}
+
+// parseText parses s as text and returns the result of appending
+// those parsed Text elements to out.
+// parseText does not handle explicit links like [math.Sin] or [Go home page]:
+// those are handled by parseLinkedText.
+// If autoLink is true, then parseText recognizes URLs and words from d.Words
+// and converts those to links as appropriate.
+func (d *parseDoc) parseText(out []Text, s string, autoLink bool) []Text {
+ var w strings.Builder
+ wrote := 0
+ writeUntil := func(i int) {
+ w.WriteString(s[wrote:i])
+ wrote = i
+ }
+ flush := func(i int) {
+ writeUntil(i)
+ if w.Len() > 0 {
+ out = append(out, Plain(w.String()))
+ w.Reset()
+ }
+ }
+ for i := 0; i < len(s); {
+ t := s[i:]
+ if autoLink {
+ if url, ok := autoURL(t); ok {
+ flush(i)
+ // Note: The old comment parser would look up the URL in words
+ // and replace the target with words[URL] if it was non-empty.
+ // That would allow creating links that display as one URL but
+ // when clicked go to a different URL. Not sure what the point
+ // of that is, so we're not doing that lookup here.
+ out = append(out, &Link{Auto: true, Text: []Text{Plain(url)}, URL: url})
+ i += len(url)
+ wrote = i
+ continue
+ }
+ if id, ok := ident(t); ok {
+ url, italics := d.Words[id]
+ if !italics {
+ i += len(id)
+ continue
+ }
+ flush(i)
+ if url == "" {
+ out = append(out, Italic(id))
+ } else {
+ out = append(out, &Link{Auto: true, Text: []Text{Italic(id)}, URL: url})
+ }
+ i += len(id)
+ wrote = i
+ continue
+ }
+ }
+ switch {
+ case strings.HasPrefix(t, "``"):
+ writeUntil(i)
+ w.WriteRune('“')
+ i += 2
+ wrote = i
+ case strings.HasPrefix(t, "''"):
+ writeUntil(i)
+ w.WriteRune('”')
+ i += 2
+ wrote = i
+ default:
+ i++
+ }
+ }
+ flush(len(s))
+ return out
+}
+
+// autoURL checks whether s begins with a URL that should be hyperlinked.
+// If so, it returns the URL, which is a prefix of s, and ok == true.
+// Otherwise it returns "", false.
+// The caller should skip over the first len(url) bytes of s
+// before further processing.
+func autoURL(s string) (url string, ok bool) {
+ // Find the ://. Fast path to pick off non-URL,
+ // since we call this at every position in the string.
+ // The shortest possible URL is ftp://x, 7 bytes.
+ var i int
+ switch {
+ case len(s) < 7:
+ return "", false
+ case s[3] == ':':
+ i = 3
+ case s[4] == ':':
+ i = 4
+ case s[5] == ':':
+ i = 5
+ case s[6] == ':':
+ i = 6
+ default:
+ return "", false
+ }
+ if i+3 > len(s) || s[i:i+3] != "://" {
+ return "", false
+ }
+
+ // Check valid scheme.
+ if !isScheme(s[:i]) {
+ return "", false
+ }
+
+ // Scan host part. Must have at least one byte,
+ // and must start and end in non-punctuation.
+ i += 3
+ if i >= len(s) || !isHost(s[i]) || isPunct(s[i]) {
+ return "", false
+ }
+ i++
+ end := i
+ for i < len(s) && isHost(s[i]) {
+ if !isPunct(s[i]) {
+ end = i + 1
+ }
+ i++
+ }
+ i = end
+
+ // At this point we are definitely returning a URL (scheme://host).
+ // We just have to find the longest path we can add to it.
+ // Heuristics abound.
+ // We allow parens, braces, and brackets,
+ // but only if they match (#5043, #22285).
+ // We allow .,:;?! in the path but not at the end,
+ // to avoid end-of-sentence punctuation (#18139, #16565).
+ stk := []byte{}
+ end = i
+Path:
+ for ; i < len(s); i++ {
+ if isPunct(s[i]) {
+ continue
+ }
+ if !isPath(s[i]) {
+ break
+ }
+ switch s[i] {
+ case '(':
+ stk = append(stk, ')')
+ case '{':
+ stk = append(stk, '}')
+ case '[':
+ stk = append(stk, ']')
+ case ')', '}', ']':
+ if len(stk) == 0 || stk[len(stk)-1] != s[i] {
+ break Path
+ }
+ stk = stk[:len(stk)-1]
+ }
+ if len(stk) == 0 {
+ end = i + 1
+ }
+ }
+
+ return s[:end], true
+}
+
+// isScheme reports whether s is a recognized URL scheme.
+// Note that if strings of new length (beyond 3-7)
+// are added here, the fast path at the top of autoURL will need updating.
+func isScheme(s string) bool {
+ switch s {
+ case "file",
+ "ftp",
+ "gopher",
+ "http",
+ "https",
+ "mailto",
+ "nntp":
+ return true
+ }
+ return false
+}
+
+// isHost reports whether c is a byte that can appear in a URL host,
+// like www.example.com or user@[::1]:8080
+func isHost(c byte) bool {
+ // mask is a 128-bit bitmap with 1s for allowed bytes,
+ // so that the byte c can be tested with a shift and an and.
+ // If c > 128, then 1<<c and 1<<(c-64) will both be zero,
+ // and this function will return false.
+ const mask = 0 |
+ (1<<26-1)<<'A' |
+ (1<<26-1)<<'a' |
+ (1<<10-1)<<'0' |
+ 1<<'_' |
+ 1<<'@' |
+ 1<<'-' |
+ 1<<'.' |
+ 1<<'[' |
+ 1<<']' |
+ 1<<':'
+
+ return ((uint64(1)<<c)&(mask&(1<<64-1)) |
+ (uint64(1)<<(c-64))&(mask>>64)) != 0
+}
+
+// isPunct reports whether c is a punctuation byte that can appear
+// inside a path but not at the end.
+func isPunct(c byte) bool {
+ // mask is a 128-bit bitmap with 1s for allowed bytes,
+ // so that the byte c can be tested with a shift and an and.
+ // If c > 128, then 1<<c and 1<<(c-64) will both be zero,
+ // and this function will return false.
+ const mask = 0 |
+ 1<<'.' |
+ 1<<',' |
+ 1<<':' |
+ 1<<';' |
+ 1<<'?' |
+ 1<<'!'
+
+ return ((uint64(1)<<c)&(mask&(1<<64-1)) |
+ (uint64(1)<<(c-64))&(mask>>64)) != 0
+}
+
+// isPath reports whether c is a (non-punctuation) path byte.
+func isPath(c byte) bool {
+ // mask is a 128-bit bitmap with 1s for allowed bytes,
+ // so that the byte c can be tested with a shift and an and.
+ // If c > 128, then 1<<c and 1<<(c-64) will both be zero,
+ // and this function will return false.
+ const mask = 0 |
+ (1<<26-1)<<'A' |
+ (1<<26-1)<<'a' |
+ (1<<10-1)<<'0' |
+ 1<<'$' |
+ 1<<'\'' |
+ 1<<'(' |
+ 1<<')' |
+ 1<<'*' |
+ 1<<'+' |
+ 1<<'&' |
+ 1<<'#' |
+ 1<<'=' |
+ 1<<'@' |
+ 1<<'~' |
+ 1<<'_' |
+ 1<<'/' |
+ 1<<'-' |
+ 1<<'[' |
+ 1<<']' |
+ 1<<'{' |
+ 1<<'}' |
+ 1<<'%'
+
+ return ((uint64(1)<<c)&(mask&(1<<64-1)) |
+ (uint64(1)<<(c-64))&(mask>>64)) != 0
+}
+
+// isName reports whether s is a capitalized Go identifier (like Name).
+func isName(s string) bool {
+ t, ok := ident(s)
+ if !ok || t != s {
+ return false
+ }
+ r, _ := utf8.DecodeRuneInString(s)
+ return unicode.IsUpper(r)
+}
+
+// ident checks whether s begins with a Go identifier.
+// If so, it returns the identifier, which is a prefix of s, and ok == true.
+// Otherwise it returns "", false.
+// The caller should skip over the first len(id) bytes of s
+// before further processing.
+func ident(s string) (id string, ok bool) {
+ // Scan [\pL_][\pL_0-9]*
+ n := 0
+ for n < len(s) {
+ if c := s[n]; c < utf8.RuneSelf {
+ if isIdentASCII(c) && (n > 0 || c < '0' || c > '9') {
+ n++
+ continue
+ }
+ break
+ }
+ r, nr := utf8.DecodeRuneInString(s[n:])
+ if unicode.IsLetter(r) {
+ n += nr
+ continue
+ }
+ break
+ }
+ return s[:n], n > 0
+}
+
+// isIdentASCII reports whether c is an ASCII identifier byte.
+func isIdentASCII(c byte) bool {
+ // mask is a 128-bit bitmap with 1s for allowed bytes,
+ // so that the byte c can be tested with a shift and an and.
+ // If c > 128, then 1<<c and 1<<(c-64) will both be zero,
+ // and this function will return false.
+ const mask = 0 |
+ (1<<26-1)<<'A' |
+ (1<<26-1)<<'a' |
+ (1<<10-1)<<'0' |
+ 1<<'_'
+
+ return ((uint64(1)<<c)&(mask&(1<<64-1)) |
+ (uint64(1)<<(c-64))&(mask>>64)) != 0
+}
+
+// validImportPath reports whether path is a valid import path.
+// It is a lightly edited copy of golang.org/x/mod/module.CheckImportPath.
+func validImportPath(path string) bool {
+ if !utf8.ValidString(path) {
+ return false
+ }
+ if path == "" {
+ return false
+ }
+ if path[0] == '-' {
+ return false
+ }
+ if strings.Contains(path, "//") {
+ return false
+ }
+ if path[len(path)-1] == '/' {
+ return false
+ }
+ elemStart := 0
+ for i, r := range path {
+ if r == '/' {
+ if !validImportPathElem(path[elemStart:i]) {
+ return false
+ }
+ elemStart = i + 1
+ }
+ }
+ return validImportPathElem(path[elemStart:])
+}
+
+func validImportPathElem(elem string) bool {
+ if elem == "" || elem[0] == '.' || elem[len(elem)-1] == '.' {
+ return false
+ }
+ for i := 0; i < len(elem); i++ {
+ if !importPathOK(elem[i]) {
+ return false
+ }
+ }
+ return true
+}
+
+func importPathOK(c byte) bool {
+ // mask is a 128-bit bitmap with 1s for allowed bytes,
+ // so that the byte c can be tested with a shift and an and.
+ // If c > 128, then 1<<c and 1<<(c-64) will both be zero,
+ // and this function will return false.
+ const mask = 0 |
+ (1<<26-1)<<'A' |
+ (1<<26-1)<<'a' |
+ (1<<10-1)<<'0' |
+ 1<<'-' |
+ 1<<'.' |
+ 1<<'~' |
+ 1<<'_' |
+ 1<<'+'
+
+ return ((uint64(1)<<c)&(mask&(1<<64-1)) |
+ (uint64(1)<<(c-64))&(mask>>64)) != 0
+}
diff --git a/src/go/doc/comment/parse_test.go b/src/go/doc/comment/parse_test.go
new file mode 100644
index 00000000000..bce733eaae5
--- /dev/null
+++ b/src/go/doc/comment/parse_test.go
@@ -0,0 +1,12 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package comment
+
+import "testing"
+
+// See https://golang.org/issue/52353
+func Test52353(t *testing.T) {
+ ident("𫕐ﯯ")
+}
diff --git a/src/go/doc/comment/print.go b/src/go/doc/comment/print.go
new file mode 100644
index 00000000000..4e9da3d1e85
--- /dev/null
+++ b/src/go/doc/comment/print.go
@@ -0,0 +1,290 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package comment
+
+import (
+ "bytes"
+ "fmt"
+ "strings"
+)
+
+// A Printer is a doc comment printer.
+// The fields in the struct can be filled in before calling
+// any of the printing methods
+// in order to customize the details of the printing process.
+type Printer struct {
+ // HeadingLevel is the nesting level used for
+ // HTML and Markdown headings.
+ // If HeadingLevel is zero, it defaults to level 3,
+ // meaning to use <h3> and ###.
+ HeadingLevel int
+
+ // HeadingID is a function that computes the heading ID
+ // (anchor tag) to use for the heading h when generating
+ // HTML and Markdown. If HeadingID returns an empty string,
+ // then the heading ID is omitted.
+ // If HeadingID is nil, h.DefaultID is used.
+ HeadingID func(h *Heading) string
+
+ // DocLinkURL is a function that computes the URL for the given DocLink.
+ // If DocLinkURL is nil, then link.DefaultURL(p.DocLinkBaseURL) is used.
+ DocLinkURL func(link *DocLink) string
+
+ // DocLinkBaseURL is used when DocLinkURL is nil,
+ // passed to [DocLink.DefaultURL] to construct a DocLink's URL.
+ // See that method's documentation for details.
+ DocLinkBaseURL string
+
+ // TextPrefix is a prefix to print at the start of every line
+ // when generating text output using the Text method.
+ TextPrefix string
+
+ // TextCodePrefix is the prefix to print at the start of each
+ // preformatted (code block) line when generating text output,
+ // instead of (not in addition to) TextPrefix.
+ // If TextCodePrefix is the empty string, it defaults to TextPrefix+"\t".
+ TextCodePrefix string
+
+ // TextWidth is the maximum width text line to generate,
+ // measured in Unicode code points,
+ // excluding TextPrefix and the newline character.
+ // If TextWidth is zero, it defaults to 80 minus the number of code points in TextPrefix.
+ // If TextWidth is negative, there is no limit.
+ TextWidth int
+}
+
+func (p *Printer) headingLevel() int {
+ if p.HeadingLevel <= 0 {
+ return 3
+ }
+ return p.HeadingLevel
+}
+
+func (p *Printer) headingID(h *Heading) string {
+ if p.HeadingID == nil {
+ return h.DefaultID()
+ }
+ return p.HeadingID(h)
+}
+
+func (p *Printer) docLinkURL(link *DocLink) string {
+ if p.DocLinkURL != nil {
+ return p.DocLinkURL(link)
+ }
+ return link.DefaultURL(p.DocLinkBaseURL)
+}
+
+// DefaultURL constructs and returns the documentation URL for l,
+// using baseURL as a prefix for links to other packages.
+//
+// The possible forms returned by DefaultURL are:
+// - baseURL/ImportPath, for a link to another package
+// - baseURL/ImportPath#Name, for a link to a const, func, type, or var in another package
+// - baseURL/ImportPath#Recv.Name, for a link to a method in another package
+// - #Name, for a link to a const, func, type, or var in this package
+// - #Recv.Name, for a link to a method in this package
+//
+// If baseURL ends in a trailing slash, then DefaultURL inserts
+// a slash between ImportPath and # in the anchored forms.
+// For example, here are some baseURL values and URLs they can generate:
+//
+// "/pkg/" → "/pkg/math/#Sqrt"
+// "/pkg" → "/pkg/math#Sqrt"
+// "/" → "/math/#Sqrt"
+// "" → "/math#Sqrt"
+func (l *DocLink) DefaultURL(baseURL string) string {
+ if l.ImportPath != "" {
+ slash := ""
+ if strings.HasSuffix(baseURL, "/") {
+ slash = "/"
+ } else {
+ baseURL += "/"
+ }
+ switch {
+ case l.Name == "":
+ return baseURL + l.ImportPath + slash
+ case l.Recv != "":
+ return baseURL + l.ImportPath + slash + "#" + l.Recv + "." + l.Name
+ default:
+ return baseURL + l.ImportPath + slash + "#" + l.Name
+ }
+ }
+ if l.Recv != "" {
+ return "#" + l.Recv + "." + l.Name
+ }
+ return "#" + l.Name
+}
+
+// DefaultID returns the default anchor ID for the heading h.
+//
+// The default anchor ID is constructed by converting every
+// rune that is not alphanumeric ASCII to an underscore
+// and then adding the prefix “hdr-”.
+// For example, if the heading text is “Go Doc Comments”,
+// the default ID is “hdr-Go_Doc_Comments”.
+func (h *Heading) DefaultID() string {
+ // Note: The “hdr-” prefix is important to avoid DOM clobbering attacks.
+ // See https://pkg.go.dev/github.com/google/safehtml#Identifier.
+ var out strings.Builder
+ var p textPrinter
+ p.oneLongLine(&out, h.Text)
+ s := strings.TrimSpace(out.String())
+ if s == "" {
+ return ""
+ }
+ out.Reset()
+ out.WriteString("hdr-")
+ for _, r := range s {
+ if r < 0x80 && isIdentASCII(byte(r)) {
+ out.WriteByte(byte(r))
+ } else {
+ out.WriteByte('_')
+ }
+ }
+ return out.String()
+}
+
+type commentPrinter struct {
+ *Printer
+ headingPrefix string
+ needDoc map[string]bool
+}
+
+// Comment returns the standard Go formatting of the Doc,
+// without any comment markers.
+func (p *Printer) Comment(d *Doc) []byte {
+ cp := &commentPrinter{Printer: p}
+ var out bytes.Buffer
+ for i, x := range d.Content {
+ if i > 0 && blankBefore(x) {
+ out.WriteString("\n")
+ }
+ cp.block(&out, x)
+ }
+
+ // Print one block containing all the link definitions that were used,
+ // and then a second block containing all the unused ones.
+ // This makes it easy to clean up the unused ones: gofmt and
+ // delete the final block. And it's a nice visual signal without
+ // affecting the way the comment formats for users.
+ for i := 0; i < 2; i++ {
+ used := i == 0
+ first := true
+ for _, def := range d.Links {
+ if def.Used == used {
+ if first {
+ out.WriteString("\n")
+ first = false
+ }
+ out.WriteString("[")
+ out.WriteString(def.Text)
+ out.WriteString("]: ")
+ out.WriteString(def.URL)
+ out.WriteString("\n")
+ }
+ }
+ }
+
+ return out.Bytes()
+}
+
+// blankBefore reports whether the block x requires a blank line before it.
+// All blocks do, except for Lists that return false from x.BlankBefore().
+func blankBefore(x Block) bool {
+ if x, ok := x.(*List); ok {
+ return x.BlankBefore()
+ }
+ return true
+}
+
+// block prints the block x to out.
+func (p *commentPrinter) block(out *bytes.Buffer, x Block) {
+ switch x := x.(type) {
+ default:
+ fmt.Fprintf(out, "?%T", x)
+
+ case *Paragraph:
+ p.text(out, "", x.Text)
+ out.WriteString("\n")
+
+ case *Heading:
+ out.WriteString("# ")
+ p.text(out, "", x.Text)
+ out.WriteString("\n")
+
+ case *Code:
+ md := x.Text
+ for md != "" {
+ var line string
+ line, md, _ = strings.Cut(md, "\n")
+ if line != "" {
+ out.WriteString("\t")
+ out.WriteString(line)
+ }
+ out.WriteString("\n")
+ }
+
+ case *List:
+ loose := x.BlankBetween()
+ for i, item := range x.Items {
+ if i > 0 && loose {
+ out.WriteString("\n")
+ }
+ out.WriteString(" ")
+ if item.Number == "" {
+ out.WriteString(" - ")
+ } else {
+ out.WriteString(item.Number)
+ out.WriteString(". ")
+ }
+ for i, blk := range item.Content {
+ const fourSpace = " "
+ if i > 0 {
+ out.WriteString("\n" + fourSpace)
+ }
+ p.text(out, fourSpace, blk.(*Paragraph).Text)
+ out.WriteString("\n")
+ }
+ }
+ }
+}
+
+// text prints the text sequence x to out.
+func (p *commentPrinter) text(out *bytes.Buffer, indent string, x []Text) {
+ for _, t := range x {
+ switch t := t.(type) {
+ case Plain:
+ p.indent(out, indent, string(t))
+ case Italic:
+ p.indent(out, indent, string(t))
+ case *Link:
+ if t.Auto {
+ p.text(out, indent, t.Text)
+ } else {
+ out.WriteString("[")
+ p.text(out, indent, t.Text)
+ out.WriteString("]")
+ }
+ case *DocLink:
+ out.WriteString("[")
+ p.text(out, indent, t.Text)
+ out.WriteString("]")
+ }
+ }
+}
+
+// indent prints s to out, indenting with the indent string
+// after each newline in s.
+func (p *commentPrinter) indent(out *bytes.Buffer, indent, s string) {
+ for s != "" {
+ line, rest, ok := strings.Cut(s, "\n")
+ out.WriteString(line)
+ if ok {
+ out.WriteString("\n")
+ out.WriteString(indent)
+ }
+ s = rest
+ }
+}
diff --git a/src/go/doc/comment/std.go b/src/go/doc/comment/std.go
new file mode 100644
index 00000000000..71f15f47b1c
--- /dev/null
+++ b/src/go/doc/comment/std.go
@@ -0,0 +1,44 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Code generated by 'go generate' DO NOT EDIT.
+//go:generate ./mkstd.sh
+
+package comment
+
+var stdPkgs = []string{
+ "bufio",
+ "bytes",
+ "context",
+ "crypto",
+ "embed",
+ "encoding",
+ "errors",
+ "expvar",
+ "flag",
+ "fmt",
+ "hash",
+ "html",
+ "image",
+ "io",
+ "log",
+ "math",
+ "mime",
+ "net",
+ "os",
+ "path",
+ "plugin",
+ "reflect",
+ "regexp",
+ "runtime",
+ "sort",
+ "strconv",
+ "strings",
+ "sync",
+ "syscall",
+ "testing",
+ "time",
+ "unicode",
+ "unsafe",
+}
diff --git a/src/go/doc/comment/std_test.go b/src/go/doc/comment/std_test.go
new file mode 100644
index 00000000000..ae32dcd984a
--- /dev/null
+++ b/src/go/doc/comment/std_test.go
@@ -0,0 +1,35 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package comment
+
+import (
+ "internal/diff"
+ "internal/testenv"
+ "os/exec"
+ "sort"
+ "strings"
+ "testing"
+)
+
+func TestStd(t *testing.T) {
+ out, err := exec.Command(testenv.GoToolPath(t), "list", "std").CombinedOutput()
+ if err != nil {
+ t.Fatalf("%v\n%s", err, out)
+ }
+
+ var list []string
+ for _, pkg := range strings.Fields(string(out)) {
+ if !strings.Contains(pkg, "/") {
+ list = append(list, pkg)
+ }
+ }
+ sort.Strings(list)
+
+ have := strings.Join(stdPkgs, "\n") + "\n"
+ want := strings.Join(list, "\n") + "\n"
+ if have != want {
+ t.Errorf("stdPkgs is out of date: regenerate with 'go generate'\n%s", diff.Diff("stdPkgs", []byte(have), "want", []byte(want)))
+ }
+}
diff --git a/src/go/doc/comment/testdata/README.md b/src/go/doc/comment/testdata/README.md
new file mode 100644
index 00000000000..d6f2c549605
--- /dev/null
+++ b/src/go/doc/comment/testdata/README.md
@@ -0,0 +1,42 @@
+This directory contains test files (*.txt) for the comment parser.
+
+The files are in [txtar format](https://pkg.go.dev/golang.org/x/tools/txtar).
+Consider this example:
+
+ -- input --
+ Hello.
+ -- gofmt --
+ Hello.
+ -- html --
+ <p>Hello.
+ -- markdown --
+ Hello.
+ -- text --
+ Hello.
+
+Each `-- name --` line introduces a new file with the given name.
+The file named “input” must be first and contains the input to
+[comment.Parser](https://pkg.go.dev/go/doc/comment/#Parser).
+
+The remaining files contain the expected output for the named format generated by
+[comment.Printer](https://pkg.go.dev/go/doc/comment/#Printer):
+“gofmt” for Printer.Comment (Go comment format, as used by gofmt),
+“html” for Printer.HTML, “markdown” for Printer.Markdown, and “text” for Printer.Text.
+The format can also be “dump” for a textual dump of the raw data structures.
+
+The text before the `-- input --` line, if present, is JSON to be unmarshalled
+to initialize a comment.Printer. For example, this test case sets the Printer's
+TextWidth field to 20:
+
+ {"TextWidth": 20}
+ -- input --
+ Package gob manages streams of gobs - binary values exchanged between an
+ Encoder (transmitter) and a Decoder (receiver).
+ -- text --
+ Package gob
+ manages streams
+ of gobs - binary
+ values exchanged
+ between an Encoder
+ (transmitter) and a
+ Decoder (receiver).
diff --git a/src/go/doc/comment/testdata/blank.txt b/src/go/doc/comment/testdata/blank.txt
new file mode 100644
index 00000000000..9049fde76ed
--- /dev/null
+++ b/src/go/doc/comment/testdata/blank.txt
@@ -0,0 +1,12 @@
+-- input --
+ $
+ Blank line at start and end.
+ $
+-- gofmt --
+Blank line at start and end.
+-- text --
+Blank line at start and end.
+-- markdown --
+Blank line at start and end.
+-- html --
+<p>Blank line at start and end.
diff --git a/src/go/doc/comment/testdata/code.txt b/src/go/doc/comment/testdata/code.txt
new file mode 100644
index 00000000000..06b1519574c
--- /dev/null
+++ b/src/go/doc/comment/testdata/code.txt
@@ -0,0 +1,94 @@
+-- input --
+Text.
+ A tab-indented
+ (no, not eight-space indented)
+ code block and haiku.
+More text.
+ One space
+ is
+ enough
+ to
+ start
+ a
+ block.
+More text.
+
+ Blocks
+ can
+
+ have
+ blank
+ lines.
+-- gofmt --
+Text.
+
+ A tab-indented
+ (no, not eight-space indented)
+ code block and haiku.
+
+More text.
+
+ One space
+ is
+ enough
+ to
+ start
+ a
+ block.
+
+More text.
+
+ Blocks
+ can
+
+ have
+ blank
+ lines.
+-- markdown --
+Text.
+
+ A tab-indented
+ (no, not eight-space indented)
+ code block and haiku.
+
+More text.
+
+ One space
+ is
+ enough
+ to
+ start
+ a
+ block.
+
+More text.
+
+ Blocks
+ can
+
+ have
+ blank
+ lines.
+-- html --
+<p>Text.
+<pre>A tab-indented
+(no, not eight-space indented)
+code block and haiku.
+</pre>
+<p>More text.
+<pre>One space
+ is
+ enough
+ to
+ start
+ a
+ block.
+</pre>
+<p>More text.
+<pre> Blocks
+ can
+
+have
+ blank
+ lines.
+</pre>
diff --git a/src/go/doc/comment/testdata/code2.txt b/src/go/doc/comment/testdata/code2.txt
new file mode 100644
index 00000000000..0810bed41c2
--- /dev/null
+++ b/src/go/doc/comment/testdata/code2.txt
@@ -0,0 +1,31 @@
+-- input --
+Text.
+
+ A tab-indented
+ (no, not eight-space indented)
+ code block and haiku.
+
+More text.
+-- gofmt --
+Text.
+
+ A tab-indented
+ (no, not eight-space indented)
+ code block and haiku.
+
+More text.
+-- markdown --
+Text.
+
+ A tab-indented
+ (no, not eight-space indented)
+ code block and haiku.
+
+More text.
+-- html --
+<p>Text.
+<pre>A tab-indented
+(no, not eight-space indented)
+code block and haiku.
+</pre>
+<p>More text.
diff --git a/src/go/doc/comment/testdata/code3.txt b/src/go/doc/comment/testdata/code3.txt
new file mode 100644
index 00000000000..4a96a0e9ab9
--- /dev/null
+++ b/src/go/doc/comment/testdata/code3.txt
@@ -0,0 +1,33 @@
+-- input --
+Text.
+
+ $
+ A tab-indented
+ (surrounded by more blank lines)
+ code block and haiku.
+ $
+
+More text.
+-- gofmt --
+Text.
+
+ A tab-indented
+ (surrounded by more blank lines)
+ code block and haiku.
+
+More text.
+-- markdown --
+Text.
+
+ A tab-indented
+ (surrounded by more blank lines)
+ code block and haiku.
+
+More text.
+-- html --
+<p>Text.
+<pre>A tab-indented
+(surrounded by more blank lines)
+code block and haiku.
+</pre>
+<p>More text.
diff --git a/src/go/doc/comment/testdata/doclink.txt b/src/go/doc/comment/testdata/doclink.txt
new file mode 100644
index 00000000000..a9323471fd4
--- /dev/null
+++ b/src/go/doc/comment/testdata/doclink.txt
@@ -0,0 +1,21 @@
+-- input --
+In this package, see [Doc] and [Parser.Parse].
+There is no [Undef] or [Undef.Method].
+See also the [comment] package,
+especially [comment.Doc] and [comment.Parser.Parse].
+-- gofmt --
+In this package, see [Doc] and [Parser.Parse].
+There is no [Undef] or [Undef.Method].
+See also the [comment] package,
+especially [comment.Doc] and [comment.Parser.Parse].
+-- text --
+In this package, see Doc and Parser.Parse. There is no [Undef] or
+[Undef.Method]. See also the comment package, especially comment.Doc and
+comment.Parser.Parse.
+-- markdown --
+In this package, see [Doc](#Doc) and [Parser.Parse](#Parser.Parse). There is no \[Undef] or \[Undef.Method]. See also the [comment](/go/doc/comment) package, especially [comment.Doc](/go/doc/comment#Doc) and [comment.Parser.Parse](/go/doc/comment#Parser.Parse).
+-- html --
+<p>In this package, see <a href="#Doc">Doc</a> and <a href="#Parser.Parse">Parser.Parse</a>.
+There is no [Undef] or [Undef.Method].
+See also the <a href="/go/doc/comment">comment</a> package,
+especially <a href="/go/doc/comment#Doc">comment.Doc</a> and <a href="/go/doc/comment#Parser.Parse">comment.Parser.Parse</a>.
diff --git a/src/go/doc/comment/testdata/doclink2.txt b/src/go/doc/comment/testdata/doclink2.txt
new file mode 100644
index 00000000000..ecd8e4e0bce
--- /dev/null
+++ b/src/go/doc/comment/testdata/doclink2.txt
@@ -0,0 +1,8 @@
+-- input --
+We use [io.Reader] a lot, and also a few map[io.Reader]string.
+
+Never [io.Reader]int or Slice[io.Reader] though.
+-- markdown --
+We use [io.Reader](/io#Reader) a lot, and also a few map\[io.Reader]string.
+
+Never \[io.Reader]int or Slice\[io.Reader] though.
diff --git a/src/go/doc/comment/testdata/doclink3.txt b/src/go/doc/comment/testdata/doclink3.txt
new file mode 100644
index 00000000000..0ccfb3df708
--- /dev/null
+++ b/src/go/doc/comment/testdata/doclink3.txt
@@ -0,0 +1,8 @@
+-- input --
+[encoding/json.Marshal] is a doc link.
+
+[rot13.Marshal] is not.
+-- markdown --
+[encoding/json.Marshal](/encoding/json#Marshal) is a doc link.
+
+\[rot13.Marshal] is not.
diff --git a/src/go/doc/comment/testdata/doclink4.txt b/src/go/doc/comment/testdata/doclink4.txt
new file mode 100644
index 00000000000..c7095276bfe
--- /dev/null
+++ b/src/go/doc/comment/testdata/doclink4.txt
@@ -0,0 +1,7 @@
+-- input --
+[io] at start of comment.
+[io] at start of line.
+At end of line: [io]
+At end of comment: [io]
+-- markdown --
+[io](/io) at start of comment. [io](/io) at start of line. At end of line: [io](/io) At end of comment: [io](/io)
diff --git a/src/go/doc/comment/testdata/doclink5.txt b/src/go/doc/comment/testdata/doclink5.txt
new file mode 100644
index 00000000000..ac7b3ae100d
--- /dev/null
+++ b/src/go/doc/comment/testdata/doclink5.txt
@@ -0,0 +1,5 @@
+{"DocLinkBaseURL": "https://pkg.go.dev"}
+-- input --
+[encoding/json.Marshal] is a doc link.
+-- markdown --
+[encoding/json.Marshal](https://pkg.go.dev/encoding/json#Marshal) is a doc link.
diff --git a/src/go/doc/comment/testdata/doclink6.txt b/src/go/doc/comment/testdata/doclink6.txt
new file mode 100644
index 00000000000..1acd03b6163
--- /dev/null
+++ b/src/go/doc/comment/testdata/doclink6.txt
@@ -0,0 +1,5 @@
+{"DocLinkBaseURL": "https://go.dev/pkg/"}
+-- input --
+[encoding/json.Marshal] is a doc link, and so is [rsc.io/quote.NonExist].
+-- markdown --
+[encoding/json.Marshal](https://go.dev/pkg/encoding/json/#Marshal) is a doc link, and so is [rsc.io/quote.NonExist](https://go.dev/pkg/rsc.io/quote/#NonExist).
diff --git a/src/go/doc/comment/testdata/doclink7.txt b/src/go/doc/comment/testdata/doclink7.txt
new file mode 100644
index 00000000000..d34979a3854
--- /dev/null
+++ b/src/go/doc/comment/testdata/doclink7.txt
@@ -0,0 +1,4 @@
+-- input --
+You see more [*bytes.Buffer] than [bytes.Buffer].
+-- markdown --
+You see more [\*bytes.Buffer](/bytes#Buffer) than [bytes.Buffer](/bytes#Buffer).
diff --git a/src/go/doc/comment/testdata/escape.txt b/src/go/doc/comment/testdata/escape.txt
new file mode 100644
index 00000000000..f54663f5c3e
--- /dev/null
+++ b/src/go/doc/comment/testdata/escape.txt
@@ -0,0 +1,55 @@
+-- input --
+What the ~!@#$%^&*()_+-=`{}|[]\:";',./<>?
+
++ Line
+
+- Line
+
+* Line
+
+999. Line
+
+## Line
+-- gofmt --
+What the ~!@#$%^&*()_+-=`{}|[]\:";',./<>?
+
++ Line
+
+- Line
+
+* Line
+
+999. Line
+
+## Line
+-- text --
+What the ~!@#$%^&*()_+-=`{}|[]\:";',./<>?
+
++ Line
+
+- Line
+
+* Line
+
+999. Line
+
+## Line
+-- markdown --
+What the ~!@#$%^&\*()\_+-=\`{}|\[]\\:";',./\<>?
+
+\+ Line
+
+\- Line
+
+\* Line
+
+999\. Line
+
+\## Line
+-- html --
+<p>What the ~!@#$%^&amp;*()_+-=`{}|[]\:&quot;;&apos;,./&lt;&gt;?
+<p>+ Line
+<p>- Line
+<p>* Line
+<p>999. Line
+<p>## Line
diff --git a/src/go/doc/comment/testdata/head.txt b/src/go/doc/comment/testdata/head.txt
new file mode 100644
index 00000000000..b99a8c59f3f
--- /dev/null
+++ b/src/go/doc/comment/testdata/head.txt
@@ -0,0 +1,92 @@
+-- input --
+Some text.
+
+An Old Heading
+
+Not An Old Heading.
+
+And some text.
+
+# A New Heading.
+
+And some more text.
+
+# Not a heading,
+because text follows it.
+
+Because text precedes it,
+# not a heading.
+
+## Not a heading either.
+
+-- gofmt --
+Some text.
+
+# An Old Heading
+
+Not An Old Heading.
+
+And some text.
+
+# A New Heading.
+
+And some more text.
+
+# Not a heading,
+because text follows it.
+
+Because text precedes it,
+# not a heading.
+
+## Not a heading either.
+
+-- text --
+Some text.
+
+# An Old Heading
+
+Not An Old Heading.
+
+And some text.
+
+# A New Heading.
+
+And some more text.
+
+# Not a heading, because text follows it.
+
+Because text precedes it, # not a heading.
+
+## Not a heading either.
+
+-- markdown --
+Some text.
+
+### An Old Heading {#hdr-An_Old_Heading}
+
+Not An Old Heading.
+
+And some text.
+
+### A New Heading. {#hdr-A_New_Heading_}
+
+And some more text.
+
+\# Not a heading, because text follows it.
+
+Because text precedes it, # not a heading.
+
+\## Not a heading either.
+
+-- html --
+<p>Some text.
+<h3 id="hdr-An_Old_Heading">An Old Heading</h3>
+<p>Not An Old Heading.
+<p>And some text.
+<h3 id="hdr-A_New_Heading_">A New Heading.</h3>
+<p>And some more text.
+<p># Not a heading,
+because text follows it.
+<p>Because text precedes it,
+# not a heading.
+<p>## Not a heading either.
diff --git a/src/go/doc/comment/testdata/head2.txt b/src/go/doc/comment/testdata/head2.txt
new file mode 100644
index 00000000000..d3576325e04
--- /dev/null
+++ b/src/go/doc/comment/testdata/head2.txt
@@ -0,0 +1,36 @@
+-- input --
+✦
+
+Almost a+heading
+
+✦
+
+Don't be a heading
+
+✦
+
+A.b is a heading
+
+✦
+
+A. b is not a heading
+
+✦
+-- gofmt --
+✦
+
+Almost a+heading
+
+✦
+
+Don't be a heading
+
+✦
+
+# A.b is a heading
+
+✦
+
+A. b is not a heading
+
+✦
diff --git a/src/go/doc/comment/testdata/head3.txt b/src/go/doc/comment/testdata/head3.txt
new file mode 100644
index 00000000000..dbb7cb3ffb8
--- /dev/null
+++ b/src/go/doc/comment/testdata/head3.txt
@@ -0,0 +1,7 @@
+{"HeadingLevel": 5}
+-- input --
+# Heading
+-- markdown --
+##### Heading {#hdr-Heading}
+-- html --
+<h5 id="hdr-Heading">Heading</h5>
diff --git a/src/go/doc/comment/testdata/hello.txt b/src/go/doc/comment/testdata/hello.txt
new file mode 100644
index 00000000000..fb07f1eb757
--- /dev/null
+++ b/src/go/doc/comment/testdata/hello.txt
@@ -0,0 +1,35 @@
+-- input --
+ Hello,
+ world
+
+ This is
+ a test.
+-- dump --
+Doc
+ Paragraph
+ Plain
+ "Hello,\n"
+ "world"
+ Paragraph
+ Plain
+ "This is\n"
+ "a test."
+-- gofmt --
+Hello,
+world
+
+This is
+a test.
+-- html --
+<p>Hello,
+world
+<p>This is
+a test.
+-- markdown --
+Hello, world
+
+This is a test.
+-- text --
+Hello, world
+
+This is a test.
diff --git a/src/go/doc/comment/testdata/link.txt b/src/go/doc/comment/testdata/link.txt
new file mode 100644
index 00000000000..551e3065ce7
--- /dev/null
+++ b/src/go/doc/comment/testdata/link.txt
@@ -0,0 +1,17 @@
+-- input --
+The Go home page is https://go.dev/.
+It used to be https://golang.org.
+
+-- gofmt --
+The Go home page is https://go.dev/.
+It used to be https://golang.org.
+
+-- text --
+The Go home page is https://go.dev/. It used to be https://golang.org.
+
+-- markdown --
+The Go home page is [https://go.dev/](https://go.dev/). It used to be [https://golang.org](https://golang.org).
+
+-- html --
+<p>The Go home page is <a href="https://go.dev/">https://go.dev/</a>.
+It used to be <a href="https://golang.org">https://golang.org</a>.
diff --git a/src/go/doc/comment/testdata/link2.txt b/src/go/doc/comment/testdata/link2.txt
new file mode 100644
index 00000000000..8637a32f01a
--- /dev/null
+++ b/src/go/doc/comment/testdata/link2.txt
@@ -0,0 +1,31 @@
+-- input --
+The Go home page is https://go.dev/.
+It used to be https://golang.org.
+https:// is not a link.
+Nor is https://
+https://☺ is not a link.
+https://:80 is not a link.
+
+-- gofmt --
+The Go home page is https://go.dev/.
+It used to be https://golang.org.
+https:// is not a link.
+Nor is https://
+https://☺ is not a link.
+https://:80 is not a link.
+
+-- text --
+The Go home page is https://go.dev/. It used to be https://golang.org. https://
+is not a link. Nor is https:// https://☺ is not a link. https://:80 is not a
+link.
+
+-- markdown --
+The Go home page is [https://go.dev/](https://go.dev/). It used to be [https://golang.org](https://golang.org). https:// is not a link. Nor is https:// https://☺ is not a link. https://:80 is not a link.
+
+-- html --
+<p>The Go home page is <a href="https://go.dev/">https://go.dev/</a>.
+It used to be <a href="https://golang.org">https://golang.org</a>.
+https:// is not a link.
+Nor is https://
+https://☺ is not a link.
+https://:80 is not a link.
diff --git a/src/go/doc/comment/testdata/link3.txt b/src/go/doc/comment/testdata/link3.txt
new file mode 100644
index 00000000000..5a115b5cb71
--- /dev/null
+++ b/src/go/doc/comment/testdata/link3.txt
@@ -0,0 +1,14 @@
+-- input --
+Doc text.
+
+[Go home page]: https://go.dev
+-- gofmt --
+Doc text.
+
+[Go home page]: https://go.dev
+-- text --
+Doc text.
+-- markdown --
+Doc text.
+-- html --
+<p>Doc text.
diff --git a/src/go/doc/comment/testdata/link4.txt b/src/go/doc/comment/testdata/link4.txt
new file mode 100644
index 00000000000..75f194c845c
--- /dev/null
+++ b/src/go/doc/comment/testdata/link4.txt
@@ -0,0 +1,77 @@
+-- input --
+These are not links.
+
+[x
+
+[x]:
+
+[x]:https://go.dev
+
+[x]https://go.dev
+
+[x]: surprise://go.dev
+
+[x]: surprise!
+
+But this is, with a tab (although it's unused).
+
+[z]: https://go.dev
+-- gofmt --
+These are not links.
+
+[x
+
+[x]:
+
+[x]:https://go.dev
+
+[x]https://go.dev
+
+[x]: surprise://go.dev
+
+[x]: surprise!
+
+But this is, with a tab (although it's unused).
+
+[z]: https://go.dev
+-- text --
+These are not links.
+
+[x
+
+[x]:
+
+[x]:https://go.dev
+
+[x]https://go.dev
+
+[x]: surprise://go.dev
+
+[x]: surprise!
+
+But this is, with a tab (although it's unused).
+-- markdown --
+These are not links.
+
+\[x
+
+\[x]:
+
+\[x]:[https://go.dev](https://go.dev)
+
+\[x][https://go.dev](https://go.dev)
+
+\[x]: surprise://go.dev
+
+\[x]: surprise!
+
+But this is, with a tab (although it's unused).
+-- html --
+<p>These are not links.
+<p>[x
+<p>[x]:
+<p>[x]:<a href="https://go.dev">https://go.dev</a>
+<p>[x]<a href="https://go.dev">https://go.dev</a>
+<p>[x]: surprise://go.dev
+<p>[x]: surprise!
+<p>But this is, with a tab (although it&apos;s unused).
diff --git a/src/go/doc/comment/testdata/link5.txt b/src/go/doc/comment/testdata/link5.txt
new file mode 100644
index 00000000000..b4fb5889f47
--- /dev/null
+++ b/src/go/doc/comment/testdata/link5.txt
@@ -0,0 +1,36 @@
+-- input --
+See the [Go home page] and the [pkg
+site].
+
+[Go home page]: https://go.dev/
+[pkg site]: https://pkg.go.dev
+[Go home page]: https://duplicate.ignored
+
+They're really great!
+
+-- gofmt --
+See the [Go home page] and the [pkg
+site].
+
+They're really great!
+
+[Go home page]: https://go.dev/
+[pkg site]: https://pkg.go.dev
+
+[Go home page]: https://duplicate.ignored
+
+-- text --
+See the Go home page and the pkg site.
+
+They're really great!
+
+[Go home page]: https://go.dev/
+[pkg site]: https://pkg.go.dev
+-- markdown --
+See the [Go home page](https://go.dev/) and the [pkg site](https://pkg.go.dev).
+
+They're really great!
+-- html --
+<p>See the <a href="https://go.dev/">Go home page</a> and the <a href="https://pkg.go.dev">pkg
+site</a>.
+<p>They&apos;re really great!
diff --git a/src/go/doc/comment/testdata/link6.txt b/src/go/doc/comment/testdata/link6.txt
new file mode 100644
index 00000000000..ff629b45736
--- /dev/null
+++ b/src/go/doc/comment/testdata/link6.txt
@@ -0,0 +1,50 @@
+-- input --
+URLs with punctuation are hard.
+We don't want to consume the end-of-sentence punctuation.
+
+For example, https://en.wikipedia.org/wiki/John_Adams_(miniseries).
+And https://example.com/[foo]/bar{.
+And https://example.com/(foo)/bar!
+And https://example.com/{foo}/bar{.
+And https://example.com/)baz{foo}.
+
+[And https://example.com/].
+
+-- gofmt --
+URLs with punctuation are hard.
+We don't want to consume the end-of-sentence punctuation.
+
+For example, https://en.wikipedia.org/wiki/John_Adams_(miniseries).
+And https://example.com/[foo]/bar{.
+And https://example.com/(foo)/bar!
+And https://example.com/{foo}/bar{.
+And https://example.com/)baz{foo}.
+
+[And https://example.com/].
+
+-- text --
+URLs with punctuation are hard. We don't want to consume the end-of-sentence
+punctuation.
+
+For example, https://en.wikipedia.org/wiki/John_Adams_(miniseries).
+And https://example.com/[foo]/bar{. And https://example.com/(foo)/bar! And
+https://example.com/{foo}/bar{. And https://example.com/)baz{foo}.
+
+[And https://example.com/].
+
+-- markdown --
+URLs with punctuation are hard. We don't want to consume the end-of-sentence punctuation.
+
+For example, [https://en.wikipedia.org/wiki/John\_Adams\_(miniseries)](https://en.wikipedia.org/wiki/John_Adams_(miniseries)). And [https://example.com/\[foo]/bar](https://example.com/[foo]/bar){. And [https://example.com/(foo)/bar](https://example.com/(foo)/bar)! And [https://example.com/{foo}/bar](https://example.com/{foo}/bar){. And [https://example.com/](https://example.com/))baz{foo}.
+
+\[And [https://example.com/](https://example.com/)].
+
+-- html --
+<p>URLs with punctuation are hard.
+We don&apos;t want to consume the end-of-sentence punctuation.
+<p>For example, <a href="https://en.wikipedia.org/wiki/John_Adams_(miniseries)">https://en.wikipedia.org/wiki/John_Adams_(miniseries)</a>.
+And <a href="https://example.com/[foo]/bar">https://example.com/[foo]/bar</a>{.
+And <a href="https://example.com/(foo)/bar">https://example.com/(foo)/bar</a>!
+And <a href="https://example.com/{foo}/bar">https://example.com/{foo}/bar</a>{.
+And <a href="https://example.com/">https://example.com/</a>)baz{foo}.
+<p>[And <a href="https://example.com/">https://example.com/</a>].
diff --git a/src/go/doc/comment/testdata/link7.txt b/src/go/doc/comment/testdata/link7.txt
new file mode 100644
index 00000000000..89a8b3170e0
--- /dev/null
+++ b/src/go/doc/comment/testdata/link7.txt
@@ -0,0 +1,25 @@
+-- input --
+[math] is a package but this is not a doc link.
+
+[io] is a doc link.
+
+[math]: https://example.com
+-- gofmt --
+[math] is a package but this is not a doc link.
+
+[io] is a doc link.
+
+[math]: https://example.com
+-- text --
+math is a package but this is not a doc link.
+
+io is a doc link.
+
+[math]: https://example.com
+-- markdown --
+[math](https://example.com) is a package but this is not a doc link.
+
+[io](/io) is a doc link.
+-- html --
+<p><a href="https://example.com">math</a> is a package but this is not a doc link.
+<p><a href="/io">io</a> is a doc link.
diff --git a/src/go/doc/comment/testdata/list.txt b/src/go/doc/comment/testdata/list.txt
new file mode 100644
index 00000000000..455782f864f
--- /dev/null
+++ b/src/go/doc/comment/testdata/list.txt
@@ -0,0 +1,48 @@
+-- input --
+Text.
+- Not a list.
+ - Here is the list.
+ • Using multiple bullets.
+ * Indentation does not matter.
+ + Lots of bullets.
+More text.
+
+-- gofmt --
+Text.
+- Not a list.
+ - Here is the list.
+ - Using multiple bullets.
+ - Indentation does not matter.
+ - Lots of bullets.
+
+More text.
+
+-- text --
+Text. - Not a list.
+ - Here is the list.
+ - Using multiple bullets.
+ - Indentation does not matter.
+ - Lots of bullets.
+
+More text.
+
+-- markdown --
+Text. - Not a list.
+
+ - Here is the list.
+ - Using multiple bullets.
+ - Indentation does not matter.
+ - Lots of bullets.
+
+More text.
+
+-- html --
+<p>Text.
+- Not a list.
+<ul>
+<li>Here is the list.
+<li>Using multiple bullets.
+<li>Indentation does not matter.
+<li>Lots of bullets.
+</ul>
+<p>More text.
diff --git a/src/go/doc/comment/testdata/list2.txt b/src/go/doc/comment/testdata/list2.txt
new file mode 100644
index 00000000000..c390b3d59a5
--- /dev/null
+++ b/src/go/doc/comment/testdata/list2.txt
@@ -0,0 +1,57 @@
+-- input --
+Text.
+ 1. Uno
+ 2) Dos
+ 3. Tres
+ 5. Cinco
+ 7. Siete
+ 11. Once
+ 12. Doce
+ 13. Trece.
+
+-- gofmt --
+Text.
+ 1. Uno
+ 2. Dos
+ 3. Tres
+ 5. Cinco
+ 7. Siete
+ 11. Once
+ 12. Doce
+ 13. Trece.
+
+-- text --
+Text.
+ 1. Uno
+ 2. Dos
+ 3. Tres
+ 5. Cinco
+ 7. Siete
+ 11. Once
+ 12. Doce
+ 13. Trece.
+
+-- markdown --
+Text.
+
+ 1. Uno
+ 2. Dos
+ 3. Tres
+ 5. Cinco
+ 7. Siete
+ 11. Once
+ 12. Doce
+ 13. Trece.
+
+-- html --
+<p>Text.
+<ol>
+<li>Uno
+<li>Dos
+<li>Tres
+<li value="5">Cinco
+<li value="7">Siete
+<li value="11">Once
+<li>Doce
+<li>Trece.
+</ol>
diff --git a/src/go/doc/comment/testdata/list3.txt b/src/go/doc/comment/testdata/list3.txt
new file mode 100644
index 00000000000..d7d345d2d3c
--- /dev/null
+++ b/src/go/doc/comment/testdata/list3.txt
@@ -0,0 +1,32 @@
+-- input --
+Text.
+
+ 1. Uno
+ 1. Dos
+ 1. Tres
+ 1. Quatro
+
+-- gofmt --
+Text.
+
+ 1. Uno
+ 1. Dos
+ 1. Tres
+ 1. Quatro
+
+-- markdown --
+Text.
+
+ 1. Uno
+ 1. Dos
+ 1. Tres
+ 1. Quatro
+
+-- html --
+<p>Text.
+<ol>
+<li>Uno
+<li value="1">Dos
+<li value="1">Tres
+<li value="1">Quatro
+</ol>
diff --git a/src/go/doc/comment/testdata/list4.txt b/src/go/doc/comment/testdata/list4.txt
new file mode 100644
index 00000000000..9c28d65b6c3
--- /dev/null
+++ b/src/go/doc/comment/testdata/list4.txt
@@ -0,0 +1,38 @@
+-- input --
+Text.
+ 1. List
+2. Not indented, not a list.
+ 3. Another list.
+
+-- gofmt --
+Text.
+ 1. List
+
+2. Not indented, not a list.
+ 3. Another list.
+
+-- text --
+Text.
+ 1. List
+
+2. Not indented, not a list.
+ 3. Another list.
+
+-- markdown --
+Text.
+
+ 1. List
+
+2\. Not indented, not a list.
+
+ 3. Another list.
+
+-- html --
+<p>Text.
+<ol>
+<li>List
+</ol>
+<p>2. Not indented, not a list.
+<ol>
+<li value="3">Another list.
+</ol>
diff --git a/src/go/doc/comment/testdata/list5.txt b/src/go/doc/comment/testdata/list5.txt
new file mode 100644
index 00000000000..a5128e5b7c0
--- /dev/null
+++ b/src/go/doc/comment/testdata/list5.txt
@@ -0,0 +1,40 @@
+-- input --
+Text.
+
+ 1. One
+ 999999999999999999999. Big
+ 1000000000000000000000. Bigger
+ 1000000000000000000001. Biggest
+
+-- gofmt --
+Text.
+
+ 1. One
+ 999999999999999999999. Big
+ 1000000000000000000000. Bigger
+ 1000000000000000000001. Biggest
+
+-- text --
+Text.
+
+ 1. One
+ 999999999999999999999. Big
+ 1000000000000000000000. Bigger
+ 1000000000000000000001. Biggest
+
+-- markdown --
+Text.
+
+ 1. One
+ 999999999999999999999. Big
+ 1000000000000000000000. Bigger
+ 1000000000000000000001. Biggest
+
+-- html --
+<p>Text.
+<ol>
+<li>One
+<li value="999999999999999999999">Big
+<li>Bigger
+<li>Biggest
+</ol>
diff --git a/src/go/doc/comment/testdata/list6.txt b/src/go/doc/comment/testdata/list6.txt
new file mode 100644
index 00000000000..ffc0122f52a
--- /dev/null
+++ b/src/go/doc/comment/testdata/list6.txt
@@ -0,0 +1,129 @@
+-- input --
+Text.
+ - List immediately after.
+ - Another.
+
+More text.
+
+ - List after blank line.
+ - Another.
+
+Even more text.
+ - List immediately after.
+
+ - Blank line between items.
+
+Yet more text.
+
+ - Another list after blank line.
+
+ - Blank line between items.
+
+Still more text.
+ - One list item.
+
+ Multiple paragraphs.
+-- dump --
+Doc
+ Paragraph
+ Plain "Text."
+ List ForceBlankBefore=false ForceBlankBetween=false
+ Item Number=""
+ Paragraph
+ Plain "List immediately after."
+ Item Number=""
+ Paragraph
+ Plain "Another."
+ Paragraph
+ Plain "More text."
+ List ForceBlankBefore=true ForceBlankBetween=false
+ Item Number=""
+ Paragraph
+ Plain "List after blank line."
+ Item Number=""
+ Paragraph
+ Plain "Another."
+ Paragraph
+ Plain "Even more text."
+ List ForceBlankBefore=false ForceBlankBetween=true
+ Item Number=""
+ Paragraph
+ Plain "List immediately after."
+ Item Number=""
+ Paragraph
+ Plain "Blank line between items."
+ Paragraph
+ Plain "Yet more text."
+ List ForceBlankBefore=true ForceBlankBetween=true
+ Item Number=""
+ Paragraph
+ Plain "Another list after blank line."
+ Item Number=""
+ Paragraph
+ Plain "Blank line between items."
+ Paragraph
+ Plain "Still more text."
+ List ForceBlankBefore=false ForceBlankBetween=true
+ Item Number=""
+ Paragraph
+ Plain "One list item."
+ Paragraph
+ Plain "Multiple paragraphs."
+
+-- gofmt --
+Text.
+ - List immediately after.
+ - Another.
+
+More text.
+
+ - List after blank line.
+ - Another.
+
+Even more text.
+
+ - List immediately after.
+
+ - Blank line between items.
+
+Yet more text.
+
+ - Another list after blank line.
+
+ - Blank line between items.
+
+Still more text.
+
+ - One list item.
+
+ Multiple paragraphs.
+
+-- markdown --
+Text.
+
+ - List immediately after.
+ - Another.
+
+More text.
+
+ - List after blank line.
+ - Another.
+
+Even more text.
+
+ - List immediately after.
+
+ - Blank line between items.
+
+Yet more text.
+
+ - Another list after blank line.
+
+ - Blank line between items.
+
+Still more text.
+
+ - One list item.
+
+ Multiple paragraphs.
+
diff --git a/src/go/doc/comment/testdata/list7.txt b/src/go/doc/comment/testdata/list7.txt
new file mode 100644
index 00000000000..446605061f6
--- /dev/null
+++ b/src/go/doc/comment/testdata/list7.txt
@@ -0,0 +1,98 @@
+-- input --
+Almost list markers (but not quite):
+
+ -
+
+❦
+
+ - $
+
+❦
+
+ - $
+
+❦
+
+ $
+ $
+
+❦
+
+ 1! List.
+
+❦
+-- gofmt --
+Almost list markers (but not quite):
+
+ -
+
+❦
+
+ - $
+
+❦
+
+ - $
+
+❦
+
+❦
+
+ 1! List.
+
+❦
+-- text --
+Almost list markers (but not quite):
+
+ -
+
+❦
+
+ -
+
+❦
+
+ -
+
+❦
+
+❦
+
+ 1! List.
+
+❦
+-- markdown --
+Almost list markers (but not quite):
+
+ -
+
+❦
+
+ - $
+
+❦
+
+ - $
+
+❦
+
+❦
+
+ 1! List.
+
+❦
+-- html --
+<p>Almost list markers (but not quite):
+<pre>-
+</pre>
+<p>❦
+<pre>- $
+</pre>
+<p>❦
+<pre>- $
+</pre>
+<p>❦
+<p>❦
+<pre>1! List.
+</pre>
+<p>❦
diff --git a/src/go/doc/comment/testdata/list8.txt b/src/go/doc/comment/testdata/list8.txt
new file mode 100644
index 00000000000..fc46b0d8351
--- /dev/null
+++ b/src/go/doc/comment/testdata/list8.txt
@@ -0,0 +1,56 @@
+-- input --
+Loose lists.
+ - A
+
+ B
+ - C
+ D
+ - E
+ - F
+-- gofmt --
+Loose lists.
+
+ - A
+
+ B
+
+ - C
+ D
+
+ - E
+
+ - F
+-- text --
+Loose lists.
+
+ - A
+
+ B
+
+ - C D
+
+ - E
+
+ - F
+-- markdown --
+Loose lists.
+
+ - A
+
+ B
+
+ - C D
+
+ - E
+
+ - F
+-- html --
+<p>Loose lists.
+<ul>
+<li><p>A
+<p>B
+<li><p>C
+D
+<li><p>E
+<li><p>F
+</ul>
diff --git a/src/go/doc/comment/testdata/para.txt b/src/go/doc/comment/testdata/para.txt
new file mode 100644
index 00000000000..2355fa8172a
--- /dev/null
+++ b/src/go/doc/comment/testdata/para.txt
@@ -0,0 +1,17 @@
+-- input --
+Hello, world.
+This is a paragraph.
+
+-- gofmt --
+Hello, world.
+This is a paragraph.
+
+-- text --
+Hello, world. This is a paragraph.
+
+-- markdown --
+Hello, world. This is a paragraph.
+
+-- html --
+<p>Hello, world.
+This is a paragraph.
diff --git a/src/go/doc/comment/testdata/quote.txt b/src/go/doc/comment/testdata/quote.txt
new file mode 100644
index 00000000000..799663af802
--- /dev/null
+++ b/src/go/doc/comment/testdata/quote.txt
@@ -0,0 +1,12 @@
+-- input --
+Doubled single quotes like `` and '' turn into Unicode double quotes,
+but single quotes ` and ' do not.
+-- gofmt --
+Doubled single quotes like “ and ” turn into Unicode double quotes,
+but single quotes ` and ' do not.
+-- text --
+Doubled single quotes like “ and ” turn into Unicode double quotes, but single
+quotes ` and ' do not.
+-- html --
+<p>Doubled single quotes like “ and ” turn into Unicode double quotes,
+but single quotes ` and &apos; do not.
diff --git a/src/go/doc/comment/testdata/text.txt b/src/go/doc/comment/testdata/text.txt
new file mode 100644
index 00000000000..c4de6e20d22
--- /dev/null
+++ b/src/go/doc/comment/testdata/text.txt
@@ -0,0 +1,62 @@
+{"TextPrefix":"|", "TextCodePrefix": "@"}
+-- input --
+Hello, world
+ Code block here.
+More text.
+Tight list
+ - one
+ - two
+ - three
+Loose list
+ - one
+
+ - two
+
+ - three
+
+# Heading
+
+More text.
+-- gofmt --
+Hello, world
+
+ Code block here.
+
+More text.
+Tight list
+ - one
+ - two
+ - three
+
+Loose list
+
+ - one
+
+ - two
+
+ - three
+
+# Heading
+
+More text.
+-- text --
+|Hello, world
+|
+@Code block here.
+|
+|More text. Tight list
+| - one
+| - two
+| - three
+|
+|Loose list
+|
+| - one
+|
+| - two
+|
+| - three
+|
+|# Heading
+|
+|More text.
diff --git a/src/go/doc/comment/testdata/text2.txt b/src/go/doc/comment/testdata/text2.txt
new file mode 100644
index 00000000000..a099d0b8c68
--- /dev/null
+++ b/src/go/doc/comment/testdata/text2.txt
@@ -0,0 +1,14 @@
+{"TextWidth": -1}
+-- input --
+Package gob manages streams of gobs - binary values exchanged between an
+Encoder (transmitter) and a Decoder (receiver). A typical use is
+transporting arguments and results of remote procedure calls (RPCs) such as
+those provided by package "net/rpc".
+
+The implementation compiles a custom codec for each data type in the stream
+and is most efficient when a single Encoder is used to transmit a stream of
+values, amortizing the cost of compilation.
+-- text --
+Package gob manages streams of gobs - binary values exchanged between an Encoder (transmitter) and a Decoder (receiver). A typical use is transporting arguments and results of remote procedure calls (RPCs) such as those provided by package "net/rpc".
+
+The implementation compiles a custom codec for each data type in the stream and is most efficient when a single Encoder is used to transmit a stream of values, amortizing the cost of compilation.
diff --git a/src/go/doc/comment/testdata/text3.txt b/src/go/doc/comment/testdata/text3.txt
new file mode 100644
index 00000000000..75d2c3765cc
--- /dev/null
+++ b/src/go/doc/comment/testdata/text3.txt
@@ -0,0 +1,28 @@
+{"TextWidth": 30}
+-- input --
+Package gob manages streams of gobs - binary values exchanged between an
+Encoder (transmitter) and a Decoder (receiver). A typical use is
+transporting arguments and results of remote procedure calls (RPCs) such as
+those provided by package "net/rpc".
+
+The implementation compiles a custom codec for each data type in the stream
+and is most efficient when a single Encoder is used to transmit a stream of
+values, amortizing the cost of compilation.
+-- text --
+Package gob manages streams
+of gobs - binary values
+exchanged between an Encoder
+(transmitter) and a Decoder
+(receiver). A typical use is
+transporting arguments and
+results of remote procedure
+calls (RPCs) such as those
+provided by package "net/rpc".
+
+The implementation compiles
+a custom codec for each data
+type in the stream and is
+most efficient when a single
+Encoder is used to transmit a
+stream of values, amortizing
+the cost of compilation.
diff --git a/src/go/doc/comment/testdata/text4.txt b/src/go/doc/comment/testdata/text4.txt
new file mode 100644
index 00000000000..e429985077e
--- /dev/null
+++ b/src/go/doc/comment/testdata/text4.txt
@@ -0,0 +1,29 @@
+{"TextWidth": 29}
+-- input --
+Package gob manages streams of gobs - binary values exchanged between an
+Encoder (transmitter) and a Decoder (receiver). A typical use is
+transporting arguments and results of remote procedure calls (RPCs) such as
+those provided by package "net/rpc".
+
+The implementation compiles a custom codec for each data type in the stream
+and is most efficient when a single Encoder is used to transmit a stream of
+values, amortizing the cost of compilation.
+-- text --
+Package gob manages streams
+of gobs - binary values
+exchanged between an Encoder
+(transmitter) and a Decoder
+(receiver). A typical use
+is transporting arguments
+and results of remote
+procedure calls (RPCs) such
+as those provided by package
+"net/rpc".
+
+The implementation compiles
+a custom codec for each data
+type in the stream and is
+most efficient when a single
+Encoder is used to transmit a
+stream of values, amortizing
+the cost of compilation.
diff --git a/src/go/doc/comment/testdata/text5.txt b/src/go/doc/comment/testdata/text5.txt
new file mode 100644
index 00000000000..2408fc559d3
--- /dev/null
+++ b/src/go/doc/comment/testdata/text5.txt
@@ -0,0 +1,38 @@
+{"TextWidth": 20}
+-- input --
+Package gob manages streams of gobs - binary values exchanged between an
+Encoder (transmitter) and a Decoder (receiver). A typical use is
+transporting arguments and results of remote procedure calls (RPCs) such as
+those provided by package "net/rpc".
+
+The implementation compiles a custom codec for each data type in the stream
+and is most efficient when a single Encoder is used to transmit a stream of
+values, amortizing the cost of compilation.
+-- text --
+Package gob
+manages streams
+of gobs - binary
+values exchanged
+between an Encoder
+(transmitter) and a
+Decoder (receiver).
+A typical use
+is transporting
+arguments and
+results of remote
+procedure calls
+(RPCs) such as those
+provided by package
+"net/rpc".
+
+The implementation
+compiles a custom
+codec for each
+data type in the
+stream and is most
+efficient when a
+single Encoder is
+used to transmit a
+stream of values,
+amortizing the cost
+of compilation.
diff --git a/src/go/doc/comment/testdata/text6.txt b/src/go/doc/comment/testdata/text6.txt
new file mode 100644
index 00000000000..d6deff52cf8
--- /dev/null
+++ b/src/go/doc/comment/testdata/text6.txt
@@ -0,0 +1,18 @@
+-- input --
+Package gob manages streams of gobs - binary values exchanged between an
+Encoder (transmitter) and a Decoder (receiver). A typical use is
+transporting arguments and results of remote procedure calls (RPCs) such as
+those provided by package "net/rpc".
+
+The implementation compiles a custom codec for each data type in the stream
+and is most efficient when a single Encoder is used to transmit a stream of
+values, amortizing the cost of compilation.
+-- text --
+Package gob manages streams of gobs - binary values exchanged between an Encoder
+(transmitter) and a Decoder (receiver). A typical use is transporting arguments
+and results of remote procedure calls (RPCs) such as those provided by package
+"net/rpc".
+
+The implementation compiles a custom codec for each data type in the stream and
+is most efficient when a single Encoder is used to transmit a stream of values,
+amortizing the cost of compilation.
diff --git a/src/go/doc/comment/testdata/text7.txt b/src/go/doc/comment/testdata/text7.txt
new file mode 100644
index 00000000000..c9fb6d37547
--- /dev/null
+++ b/src/go/doc/comment/testdata/text7.txt
@@ -0,0 +1,21 @@
+{"TextPrefix": " "}
+-- input --
+Package gob manages streams of gobs - binary values exchanged between an
+Encoder (transmitter) and a Decoder (receiver). A typical use is
+transporting arguments and results of remote procedure calls (RPCs) such as
+those provided by package "net/rpc".
+
+The implementation compiles a custom codec for each data type in the stream
+and is most efficient when a single Encoder is used to transmit a stream of
+values, amortizing the cost of compilation.
+-- text --
+ Package gob manages streams of gobs - binary values
+ exchanged between an Encoder (transmitter) and a Decoder
+ (receiver). A typical use is transporting arguments and
+ results of remote procedure calls (RPCs) such as those
+ provided by package "net/rpc".
+
+ The implementation compiles a custom codec for each data
+ type in the stream and is most efficient when a single
+ Encoder is used to transmit a stream of values, amortizing
+ the cost of compilation.
diff --git a/src/go/doc/comment/testdata/text8.txt b/src/go/doc/comment/testdata/text8.txt
new file mode 100644
index 00000000000..560ac951c17
--- /dev/null
+++ b/src/go/doc/comment/testdata/text8.txt
@@ -0,0 +1,94 @@
+{"TextWidth": 40}
+-- input --
+If the arguments have version suffixes (like @latest or @v1.0.0), "go install"
+builds packages in module-aware mode, ignoring the go.mod file in the current
+directory or any parent directory, if there is one. This is useful for
+installing executables without affecting the dependencies of the main module.
+To eliminate ambiguity about which module versions are used in the build, the
+arguments must satisfy the following constraints:
+
+ - Arguments must be package paths or package patterns (with "..." wildcards).
+ They must not be standard packages (like fmt), meta-patterns (std, cmd,
+ all), or relative or absolute file paths.
+
+ - All arguments must have the same version suffix. Different queries are not
+ allowed, even if they refer to the same version.
+
+ - All arguments must refer to packages in the same module at the same version.
+
+ - Package path arguments must refer to main packages. Pattern arguments
+ will only match main packages.
+
+ - No module is considered the "main" module. If the module containing
+ packages named on the command line has a go.mod file, it must not contain
+ directives (replace and exclude) that would cause it to be interpreted
+ differently than if it were the main module. The module must not require
+ a higher version of itself.
+
+ - Vendor directories are not used in any module. (Vendor directories are not
+ included in the module zip files downloaded by 'go install'.)
+
+If the arguments don't have version suffixes, "go install" may run in
+module-aware mode or GOPATH mode, depending on the GO111MODULE environment
+variable and the presence of a go.mod file. See 'go help modules' for details.
+If module-aware mode is enabled, "go install" runs in the context of the main
+module.
+-- text --
+If the arguments have version suffixes
+(like @latest or @v1.0.0), "go install"
+builds packages in module-aware mode,
+ignoring the go.mod file in the current
+directory or any parent directory,
+if there is one. This is useful for
+installing executables without affecting
+the dependencies of the main module.
+To eliminate ambiguity about which
+module versions are used in the build,
+the arguments must satisfy the following
+constraints:
+
+ - Arguments must be package paths
+ or package patterns (with "..."
+ wildcards). They must not be
+ standard packages (like fmt),
+ meta-patterns (std, cmd, all),
+ or relative or absolute file paths.
+
+ - All arguments must have the same
+ version suffix. Different queries
+ are not allowed, even if they refer
+ to the same version.
+
+ - All arguments must refer to packages
+ in the same module at the same
+ version.
+
+ - Package path arguments must refer
+ to main packages. Pattern arguments
+ will only match main packages.
+
+ - No module is considered the "main"
+ module. If the module containing
+ packages named on the command line
+ has a go.mod file, it must not
+ contain directives (replace and
+ exclude) that would cause it to be
+ interpreted differently than if it
+ were the main module. The module
+ must not require a higher version of
+ itself.
+
+ - Vendor directories are not used in
+ any module. (Vendor directories are
+ not included in the module zip files
+ downloaded by 'go install'.)
+
+If the arguments don't have version
+suffixes, "go install" may run in
+module-aware mode or GOPATH mode,
+depending on the GO111MODULE environment
+variable and the presence of a go.mod
+file. See 'go help modules' for details.
+If module-aware mode is enabled,
+"go install" runs in the context of the
+main module.
diff --git a/src/go/doc/comment/testdata/text9.txt b/src/go/doc/comment/testdata/text9.txt
new file mode 100644
index 00000000000..07a64aa2276
--- /dev/null
+++ b/src/go/doc/comment/testdata/text9.txt
@@ -0,0 +1,12 @@
+{"TextPrefix":"|", "TextCodePrefix": "@"}
+-- input --
+Hello, world
+ Code block here.
+-- gofmt --
+Hello, world
+
+ Code block here.
+-- text --
+|Hello, world
+|
+@Code block here.
diff --git a/src/go/doc/comment/testdata/words.txt b/src/go/doc/comment/testdata/words.txt
new file mode 100644
index 00000000000..63c7e1a1b2b
--- /dev/null
+++ b/src/go/doc/comment/testdata/words.txt
@@ -0,0 +1,10 @@
+-- input --
+This is an italicword and a linkedword and Unicöde.
+-- gofmt --
+This is an italicword and a linkedword and Unicöde.
+-- text --
+This is an italicword and a linkedword and Unicöde.
+-- markdown --
+This is an *italicword* and a [*linkedword*](https://example.com/linkedword) and Unicöde.
+-- html --
+<p>This is an <i>italicword</i> and a <a href="https://example.com/linkedword"><i>linkedword</i></a> and Unicöde.
diff --git a/src/go/doc/comment/testdata_test.go b/src/go/doc/comment/testdata_test.go
new file mode 100644
index 00000000000..0676d864b29
--- /dev/null
+++ b/src/go/doc/comment/testdata_test.go
@@ -0,0 +1,202 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package comment
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "internal/diff"
+ "internal/txtar"
+ "path/filepath"
+ "strings"
+ "testing"
+)
+
+func TestTestdata(t *testing.T) {
+ files, _ := filepath.Glob("testdata/*.txt")
+ if len(files) == 0 {
+ t.Fatalf("no testdata")
+ }
+ var p Parser
+ p.Words = map[string]string{
+ "italicword": "",
+ "linkedword": "https://example.com/linkedword",
+ }
+ p.LookupPackage = func(name string) (importPath string, ok bool) {
+ if name == "comment" {
+ return "go/doc/comment", true
+ }
+ return DefaultLookupPackage(name)
+ }
+ p.LookupSym = func(recv, name string) (ok bool) {
+ if recv == "Parser" && name == "Parse" ||
+ recv == "" && name == "Doc" ||
+ recv == "" && name == "NoURL" {
+ return true
+ }
+ return false
+ }
+
+ stripDollars := func(b []byte) []byte {
+ // Remove trailing $ on lines.
+ // They make it easier to see lines with trailing spaces,
+ // as well as turning them into lines without trailing spaces,
+ // in case editors remove trailing spaces.
+ return bytes.ReplaceAll(b, []byte("$\n"), []byte("\n"))
+ }
+ for _, file := range files {
+ t.Run(filepath.Base(file), func(t *testing.T) {
+ var pr Printer
+ a, err := txtar.ParseFile(file)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(a.Comment) > 0 {
+ err := json.Unmarshal(a.Comment, &pr)
+ if err != nil {
+ t.Fatalf("unmarshalling top json: %v", err)
+ }
+ }
+ if len(a.Files) < 1 || a.Files[0].Name != "input" {
+ t.Fatalf("first file is not %q", "input")
+ }
+ d := p.Parse(string(stripDollars(a.Files[0].Data)))
+ for _, f := range a.Files[1:] {
+ want := stripDollars(f.Data)
+ for len(want) >= 2 && want[len(want)-1] == '\n' && want[len(want)-2] == '\n' {
+ want = want[:len(want)-1]
+ }
+ var out []byte
+ switch f.Name {
+ default:
+ t.Fatalf("unknown output file %q", f.Name)
+ case "dump":
+ out = dump(d)
+ case "gofmt":
+ out = pr.Comment(d)
+ case "html":
+ out = pr.HTML(d)
+ case "markdown":
+ out = pr.Markdown(d)
+ case "text":
+ out = pr.Text(d)
+ }
+ if string(out) != string(want) {
+ t.Errorf("%s: %s", file, diff.Diff(f.Name, want, "have", out))
+ }
+ }
+ })
+ }
+}
+
+func dump(d *Doc) []byte {
+ var out bytes.Buffer
+ dumpTo(&out, 0, d)
+ return out.Bytes()
+}
+
+func dumpTo(out *bytes.Buffer, indent int, x any) {
+ switch x := x.(type) {
+ default:
+ fmt.Fprintf(out, "?%T", x)
+
+ case *Doc:
+ fmt.Fprintf(out, "Doc")
+ dumpTo(out, indent+1, x.Content)
+ if len(x.Links) > 0 {
+ dumpNL(out, indent+1)
+ fmt.Fprintf(out, "Links")
+ dumpTo(out, indent+2, x.Links)
+ }
+ fmt.Fprintf(out, "\n")
+
+ case []*LinkDef:
+ for _, def := range x {
+ dumpNL(out, indent)
+ dumpTo(out, indent, def)
+ }
+
+ case *LinkDef:
+ fmt.Fprintf(out, "LinkDef Used:%v Text:%q URL:%s", x.Used, x.Text, x.URL)
+
+ case []Block:
+ for _, blk := range x {
+ dumpNL(out, indent)
+ dumpTo(out, indent, blk)
+ }
+
+ case *Heading:
+ fmt.Fprintf(out, "Heading")
+ dumpTo(out, indent+1, x.Text)
+
+ case *List:
+ fmt.Fprintf(out, "List ForceBlankBefore=%v ForceBlankBetween=%v", x.ForceBlankBefore, x.ForceBlankBetween)
+ dumpTo(out, indent+1, x.Items)
+
+ case []*ListItem:
+ for _, item := range x {
+ dumpNL(out, indent)
+ dumpTo(out, indent, item)
+ }
+
+ case *ListItem:
+ fmt.Fprintf(out, "Item Number=%q", x.Number)
+ dumpTo(out, indent+1, x.Content)
+
+ case *Paragraph:
+ fmt.Fprintf(out, "Paragraph")
+ dumpTo(out, indent+1, x.Text)
+
+ case *Code:
+ fmt.Fprintf(out, "Code")
+ dumpTo(out, indent+1, x.Text)
+
+ case []Text:
+ for _, t := range x {
+ dumpNL(out, indent)
+ dumpTo(out, indent, t)
+ }
+
+ case Plain:
+ if !strings.Contains(string(x), "\n") {
+ fmt.Fprintf(out, "Plain %q", string(x))
+ } else {
+ fmt.Fprintf(out, "Plain")
+ dumpTo(out, indent+1, string(x))
+ }
+
+ case Italic:
+ if !strings.Contains(string(x), "\n") {
+ fmt.Fprintf(out, "Italic %q", string(x))
+ } else {
+ fmt.Fprintf(out, "Italic")
+ dumpTo(out, indent+1, string(x))
+ }
+
+ case string:
+ for _, line := range strings.SplitAfter(x, "\n") {
+ if line != "" {
+ dumpNL(out, indent)
+ fmt.Fprintf(out, "%q", line)
+ }
+ }
+
+ case *Link:
+ fmt.Fprintf(out, "Link %q", x.URL)
+ dumpTo(out, indent+1, x.Text)
+
+ case *DocLink:
+ fmt.Fprintf(out, "DocLink pkg:%q, recv:%q, name:%q", x.ImportPath, x.Recv, x.Name)
+ dumpTo(out, indent+1, x.Text)
+ }
+}
+
+func dumpNL(out *bytes.Buffer, n int) {
+ out.WriteByte('\n')
+ for i := 0; i < n; i++ {
+ out.WriteByte('\t')
+ }
+}
diff --git a/src/go/doc/comment/text.go b/src/go/doc/comment/text.go
new file mode 100644
index 00000000000..e9684f066bf
--- /dev/null
+++ b/src/go/doc/comment/text.go
@@ -0,0 +1,338 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package comment
+
+import (
+ "bytes"
+ "fmt"
+ "sort"
+ "strings"
+ "unicode/utf8"
+)
+
+// A textPrinter holds the state needed for printing a Doc as plain text.
+type textPrinter struct {
+ *Printer
+ long strings.Builder
+ prefix string
+ codePrefix string
+ width int
+}
+
+// Text returns a textual formatting of the Doc.
+// See the [Printer] documentation for ways to customize the text output.
+func (p *Printer) Text(d *Doc) []byte {
+ tp := &textPrinter{
+ Printer: p,
+ prefix: p.TextPrefix,
+ codePrefix: p.TextCodePrefix,
+ width: p.TextWidth,
+ }
+ if tp.codePrefix == "" {
+ tp.codePrefix = p.TextPrefix + "\t"
+ }
+ if tp.width == 0 {
+ tp.width = 80 - utf8.RuneCountInString(tp.prefix)
+ }
+
+ var out bytes.Buffer
+ for i, x := range d.Content {
+ if i > 0 && blankBefore(x) {
+ out.WriteString(tp.prefix)
+ writeNL(&out)
+ }
+ tp.block(&out, x)
+ }
+ anyUsed := false
+ for _, def := range d.Links {
+ if def.Used {
+ anyUsed = true
+ break
+ }
+ }
+ if anyUsed {
+ writeNL(&out)
+ for _, def := range d.Links {
+ if def.Used {
+ fmt.Fprintf(&out, "[%s]: %s\n", def.Text, def.URL)
+ }
+ }
+ }
+ return out.Bytes()
+}
+
+// writeNL calls out.WriteByte('\n')
+// but first trims trailing spaces on the previous line.
+func writeNL(out *bytes.Buffer) {
+ // Trim trailing spaces.
+ data := out.Bytes()
+ n := 0
+ for n < len(data) && (data[len(data)-n-1] == ' ' || data[len(data)-n-1] == '\t') {
+ n++
+ }
+ if n > 0 {
+ out.Truncate(len(data) - n)
+ }
+ out.WriteByte('\n')
+}
+
+// block prints the block x to out.
+func (p *textPrinter) block(out *bytes.Buffer, x Block) {
+ switch x := x.(type) {
+ default:
+ fmt.Fprintf(out, "?%T\n", x)
+
+ case *Paragraph:
+ out.WriteString(p.prefix)
+ p.text(out, "", x.Text)
+
+ case *Heading:
+ out.WriteString(p.prefix)
+ out.WriteString("# ")
+ p.text(out, "", x.Text)
+
+ case *Code:
+ text := x.Text
+ for text != "" {
+ var line string
+ line, text, _ = strings.Cut(text, "\n")
+ if line != "" {
+ out.WriteString(p.codePrefix)
+ out.WriteString(line)
+ }
+ writeNL(out)
+ }
+
+ case *List:
+ loose := x.BlankBetween()
+ for i, item := range x.Items {
+ if i > 0 && loose {
+ out.WriteString(p.prefix)
+ writeNL(out)
+ }
+ out.WriteString(p.prefix)
+ out.WriteString(" ")
+ if item.Number == "" {
+ out.WriteString(" - ")
+ } else {
+ out.WriteString(item.Number)
+ out.WriteString(". ")
+ }
+ for i, blk := range item.Content {
+ const fourSpace = " "
+ if i > 0 {
+ writeNL(out)
+ out.WriteString(p.prefix)
+ out.WriteString(fourSpace)
+ }
+ p.text(out, fourSpace, blk.(*Paragraph).Text)
+ }
+ }
+ }
+}
+
+// text prints the text sequence x to out.
+// TODO: Wrap lines.
+func (p *textPrinter) text(out *bytes.Buffer, indent string, x []Text) {
+ p.oneLongLine(&p.long, x)
+ words := strings.Fields(p.long.String())
+ p.long.Reset()
+
+ var seq []int
+ if p.width < 0 {
+ seq = []int{0, len(words)} // one long line
+ } else {
+ seq = wrap(words, p.width-utf8.RuneCountInString(indent))
+ }
+ for i := 0; i+1 < len(seq); i++ {
+ if i > 0 {
+ out.WriteString(p.prefix)
+ out.WriteString(indent)
+ }
+ for j, w := range words[seq[i]:seq[i+1]] {
+ if j > 0 {
+ out.WriteString(" ")
+ }
+ out.WriteString(w)
+ }
+ writeNL(out)
+ }
+}
+
+// oneLongLine prints the text sequence x to out as one long line,
+// without worrying about line wrapping.
+// Explicit links have the [ ] dropped to improve readability.
+func (p *textPrinter) oneLongLine(out *strings.Builder, x []Text) {
+ for _, t := range x {
+ switch t := t.(type) {
+ case Plain:
+ out.WriteString(string(t))
+ case Italic:
+ out.WriteString(string(t))
+ case *Link:
+ p.oneLongLine(out, t.Text)
+ case *DocLink:
+ p.oneLongLine(out, t.Text)
+ }
+ }
+}
+
+// wrap wraps words into lines of at most max runes,
+// minimizing the sum of the squares of the leftover lengths
+// at the end of each line (except the last, of course),
+// with a preference for ending lines at punctuation (.,:;).
+//
+// The returned slice gives the indexes of the first words
+// on each line in the wrapped text with a final entry of len(words).
+// Thus the lines are words[seq[0]:seq[1]], words[seq[1]:seq[2]],
+// ..., words[seq[len(seq)-2]:seq[len(seq)-1]].
+//
+// The implementation runs in O(n log n) time, where n = len(words),
+// using the algorithm described in D. S. Hirschberg and L. L. Larmore,
+// “[The least weight subsequence problem],” FOCS 1985, pp. 137-143.
+//
+// [The least weight subsequence problem]: https://doi.org/10.1109/SFCS.1985.60
+func wrap(words []string, max int) (seq []int) {
+ // The algorithm requires that our scoring function be concave,
+ // meaning that for all i₀ ≤ i₁ < j₀ ≤ j₁,
+ // weight(i₀, j₀) + weight(i₁, j₁) ≤ weight(i₀, j₁) + weight(i₁, j₀).
+ //
+ // Our weights are two-element pairs [hi, lo]
+ // ordered by elementwise comparison.
+ // The hi entry counts the weight for lines that are longer than max,
+ // and the lo entry counts the weight for lines that are not.
+ // This forces the algorithm to first minimize the number of lines
+ // that are longer than max, which correspond to lines with
+ // single very long words. Having done that, it can move on to
+ // minimizing the lo score, which is more interesting.
+ //
+ // The lo score is the sum for each line of the square of the
+ // number of spaces remaining at the end of the line and a
+ // penalty of 64 given out for not ending the line in a
+ // punctuation character (.,:;).
+ // The penalty is somewhat arbitrarily chosen by trying
+ // different amounts and judging how nice the wrapped text looks.
+ // Roughly speaking, using 64 means that we are willing to
+ // end a line with eight blank spaces in order to end at a
+ // punctuation character, even if the next word would fit in
+ // those spaces.
+ //
+ // We care about ending in punctuation characters because
+ // it makes the text easier to skim if not too many sentences
+ // or phrases begin with a single word on the previous line.
+
+ // A score is the score (also called weight) for a given line.
+ // add and cmp add and compare scores.
+ type score struct {
+ hi int64
+ lo int64
+ }
+ add := func(s, t score) score { return score{s.hi + t.hi, s.lo + t.lo} }
+ cmp := func(s, t score) int {
+ switch {
+ case s.hi < t.hi:
+ return -1
+ case s.hi > t.hi:
+ return +1
+ case s.lo < t.lo:
+ return -1
+ case s.lo > t.lo:
+ return +1
+ }
+ return 0
+ }
+
+ // total[j] is the total number of runes
+ // (including separating spaces) in words[:j].
+ total := make([]int, len(words)+1)
+ total[0] = 0
+ for i, s := range words {
+ total[1+i] = total[i] + utf8.RuneCountInString(s) + 1
+ }
+
+ // weight returns weight(i, j).
+ weight := func(i, j int) score {
+ // On the last line, there is zero weight for being too short.
+ n := total[j] - 1 - total[i]
+ if j == len(words) && n <= max {
+ return score{0, 0}
+ }
+
+ // Otherwise the weight is the penalty plus the square of the number of
+ // characters remaining on the line or by which the line goes over.
+ // In the latter case, that value goes in the hi part of the score.
+ // (See note above.)
+ p := wrapPenalty(words[j-1])
+ v := int64(max-n) * int64(max-n)
+ if n > max {
+ return score{v, p}
+ }
+ return score{0, v + p}
+ }
+
+ // The rest of this function is “The Basic Algorithm” from
+ // Hirschberg and Larmore's conference paper,
+ // using the same names as in the paper.
+ f := []score{{0, 0}}
+ g := func(i, j int) score { return add(f[i], weight(i, j)) }
+
+ bridge := func(a, b, c int) bool {
+ k := c + sort.Search(len(words)+1-c, func(k int) bool {
+ k += c
+ return cmp(g(a, k), g(b, k)) > 0
+ })
+ if k > len(words) {
+ return true
+ }
+ return cmp(g(c, k), g(b, k)) <= 0
+ }
+
+ // d is a one-ended deque implemented as a slice.
+ d := make([]int, 1, len(words))
+ d[0] = 0
+ bestleft := make([]int, 1, len(words))
+ bestleft[0] = -1
+ for m := 1; m < len(words); m++ {
+ f = append(f, g(d[0], m))
+ bestleft = append(bestleft, d[0])
+ for len(d) > 1 && cmp(g(d[1], m+1), g(d[0], m+1)) <= 0 {
+ d = d[1:] // “Retire”
+ }
+ for len(d) > 1 && bridge(d[len(d)-2], d[len(d)-1], m) {
+ d = d[:len(d)-1] // “Fire”
+ }
+ if cmp(g(m, len(words)), g(d[len(d)-1], len(words))) < 0 {
+ d = append(d, m) // “Hire”
+ // The next few lines are not in the paper but are necessary
+ // to handle two-word inputs correctly. It appears to be
+ // just a bug in the paper's pseudocode.
+ if len(d) == 2 && cmp(g(d[1], m+1), g(d[0], m+1)) <= 0 {
+ d = d[1:]
+ }
+ }
+ }
+ bestleft = append(bestleft, d[0])
+
+ // Recover least weight sequence from bestleft.
+ n := 1
+ for m := len(words); m > 0; m = bestleft[m] {
+ n++
+ }
+ seq = make([]int, n)
+ for m := len(words); m > 0; m = bestleft[m] {
+ n--
+ seq[n] = m
+ }
+ return seq
+}
+
+// wrapPenalty is the penalty for inserting a line break after word s.
+func wrapPenalty(s string) int64 {
+ switch s[len(s)-1] {
+ case '.', ',', ':', ';':
+ return 0
+ }
+ return 64
+}
diff --git a/src/go/doc/comment/wrap_test.go b/src/go/doc/comment/wrap_test.go
new file mode 100644
index 00000000000..f9802c9c445
--- /dev/null
+++ b/src/go/doc/comment/wrap_test.go
@@ -0,0 +1,141 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package comment
+
+import (
+ "flag"
+ "fmt"
+ "math/rand"
+ "testing"
+ "time"
+ "unicode/utf8"
+)
+
+var wrapSeed = flag.Int64("wrapseed", 0, "use `seed` for wrap test (default auto-seeds)")
+
+func TestWrap(t *testing.T) {
+ if *wrapSeed == 0 {
+ *wrapSeed = time.Now().UnixNano()
+ }
+ t.Logf("-wrapseed=%#x\n", *wrapSeed)
+ r := rand.New(rand.NewSource(*wrapSeed))
+
+ // Generate words of random length.
+ s := "1234567890αβcdefghijklmnopqrstuvwxyz"
+ sN := utf8.RuneCountInString(s)
+ var words []string
+ for i := 0; i < 100; i++ {
+ n := 1 + r.Intn(sN-1)
+ if n >= 12 {
+ n++ // extra byte for β
+ }
+ if n >= 11 {
+ n++ // extra byte for α
+ }
+ words = append(words, s[:n])
+ }
+
+ for n := 1; n <= len(words) && !t.Failed(); n++ {
+ t.Run(fmt.Sprint("n=", n), func(t *testing.T) {
+ words := words[:n]
+ t.Logf("words: %v", words)
+ for max := 1; max < 100 && !t.Failed(); max++ {
+ t.Run(fmt.Sprint("max=", max), func(t *testing.T) {
+ seq := wrap(words, max)
+
+ // Compute score for seq.
+ start := 0
+ score := int64(0)
+ if len(seq) == 0 {
+ t.Fatalf("wrap seq is empty")
+ }
+ if seq[0] != 0 {
+ t.Fatalf("wrap seq does not start with 0")
+ }
+ for _, n := range seq[1:] {
+ if n <= start {
+ t.Fatalf("wrap seq is non-increasing: %v", seq)
+ }
+ if n > len(words) {
+ t.Fatalf("wrap seq contains %d > %d: %v", n, len(words), seq)
+ }
+ size := -1
+ for _, s := range words[start:n] {
+ size += 1 + utf8.RuneCountInString(s)
+ }
+ if n-start == 1 && size >= max {
+ // no score
+ } else if size > max {
+ t.Fatalf("wrap used overlong line %d:%d: %v", start, n, words[start:n])
+ } else if n != len(words) {
+ score += int64(max-size)*int64(max-size) + wrapPenalty(words[n-1])
+ }
+ start = n
+ }
+ if start != len(words) {
+ t.Fatalf("wrap seq does not use all words (%d < %d): %v", start, len(words), seq)
+ }
+
+ // Check that score matches slow reference implementation.
+ slowSeq, slowScore := wrapSlow(words, max)
+ if score != slowScore {
+ t.Fatalf("wrap score = %d != wrapSlow score %d\nwrap: %v\nslow: %v", score, slowScore, seq, slowSeq)
+ }
+ })
+ }
+ })
+ }
+}
+
+// wrapSlow is an O(n²) reference implementation for wrap.
+// It returns a minimal-score sequence along with the score.
+// It is OK if wrap returns a different sequence as long as that
+// sequence has the same score.
+func wrapSlow(words []string, max int) (seq []int, score int64) {
+ // Quadratic dynamic programming algorithm for line wrapping problem.
+ // best[i] tracks the best score possible for words[:i],
+ // assuming that for i < len(words) the line breaks after those words.
+ // bestleft[i] tracks the previous line break for best[i].
+ best := make([]int64, len(words)+1)
+ bestleft := make([]int, len(words)+1)
+ best[0] = 0
+ for i, w := range words {
+ if utf8.RuneCountInString(w) >= max {
+ // Overlong word must appear on line by itself. No effect on score.
+ best[i+1] = best[i]
+ continue
+ }
+ best[i+1] = 1e18
+ p := wrapPenalty(w)
+ n := -1
+ for j := i; j >= 0; j-- {
+ n += 1 + utf8.RuneCountInString(words[j])
+ if n > max {
+ break
+ }
+ line := int64(n-max)*int64(n-max) + p
+ if i == len(words)-1 {
+ line = 0 // no score for final line being too short
+ }
+ s := best[j] + line
+ if best[i+1] > s {
+ best[i+1] = s
+ bestleft[i+1] = j
+ }
+ }
+ }
+
+ // Recover least weight sequence from bestleft.
+ n := 1
+ for m := len(words); m > 0; m = bestleft[m] {
+ n++
+ }
+ seq = make([]int, n)
+ for m := len(words); m > 0; m = bestleft[m] {
+ n--
+ seq[n] = m
+ }
+ return seq, best[len(words)]
+}
diff --git a/src/go/doc/comment_test.go b/src/go/doc/comment_test.go
index 6d1b209e1e1..e1e5f15bdf5 100644
--- a/src/go/doc/comment_test.go
+++ b/src/go/doc/comment_test.go
@@ -1,4 +1,4 @@
-// Copyright 2011 The Go Authors. All rights reserved.
+// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
@@ -6,242 +6,62 @@ package doc
import (
"bytes"
- "reflect"
- "strings"
+ "go/parser"
+ "go/token"
+ "internal/diff"
"testing"
)
-var headingTests = []struct {
- line string
- ok bool
-}{
- {"Section", true},
- {"A typical usage", true},
- {"ΔΛΞ is Greek", true},
- {"Foo 42", true},
- {"", false},
- {"section", false},
- {"A typical usage:", false},
- {"This code:", false},
- {"δ is Greek", false},
- {"Foo §", false},
- {"Fermat's Last Sentence", true},
- {"Fermat's", true},
- {"'sX", false},
- {"Ted 'Too' Bar", false},
- {"Use n+m", false},
- {"Scanning:", false},
- {"N:M", false},
-}
-
-func TestIsHeading(t *testing.T) {
- for _, tt := range headingTests {
- if h := heading(tt.line); (len(h) > 0) != tt.ok {
- t.Errorf("isHeading(%q) = %v, want %v", tt.line, h, tt.ok)
- }
+func TestComment(t *testing.T) {
+ fset := token.NewFileSet()
+ pkgs, err := parser.ParseDir(fset, "testdata/pkgdoc", nil, parser.ParseComments)
+ if err != nil {
+ t.Fatal(err)
}
-}
-
-var blocksTests = []struct {
- in string
- out []block
- text string
-}{
- {
- in: `Para 1.
-Para 1 line 2.
-
-Para 2.
-
-Section
-
-Para 3.
-
- pre
- pre1
-
-Para 4.
-
- pre
- pre1
-
- pre2
-
-Para 5.
-
-
- pre
-
-
- pre1
- pre2
-
-Para 6.
- pre
- pre2
-`,
- out: []block{
- {opPara, []string{"Para 1.\n", "Para 1 line 2.\n"}},
- {opPara, []string{"Para 2.\n"}},
- {opHead, []string{"Section"}},
- {opPara, []string{"Para 3.\n"}},
- {opPre, []string{"pre\n", "pre1\n"}},
- {opPara, []string{"Para 4.\n"}},
- {opPre, []string{"pre\n", "pre1\n", "\n", "pre2\n"}},
- {opPara, []string{"Para 5.\n"}},
- {opPre, []string{"pre\n", "\n", "\n", "pre1\n", "pre2\n"}},
- {opPara, []string{"Para 6.\n"}},
- {opPre, []string{"pre\n", "pre2\n"}},
- },
- text: `. Para 1. Para 1 line 2.
-
-. Para 2.
-
-
-. Section
-
-. Para 3.
-
-$ pre
-$ pre1
-
-. Para 4.
-
-$ pre
-$ pre1
-
-$ pre2
-
-. Para 5.
-
-$ pre
-
-
-$ pre1
-$ pre2
-
-. Para 6.
-
-$ pre
-$ pre2
-`,
- },
- {
- in: "Para.\n\tshould not be ``escaped''",
- out: []block{
- {opPara, []string{"Para.\n"}},
- {opPre, []string{"should not be ``escaped''"}},
- },
- text: ". Para.\n\n$ should not be ``escaped''",
- },
- {
- in: "// A very long line of 46 char for line wrapping.",
- out: []block{
- {opPara, []string{"// A very long line of 46 char for line wrapping."}},
- },
- text: `. // A very long line of 46 char for line
-. // wrapping.
-`,
- },
- {
- in: `/* A very long line of 46 char for line wrapping.
-A very long line of 46 char for line wrapping. */`,
- out: []block{
- {opPara, []string{"/* A very long line of 46 char for line wrapping.\n", "A very long line of 46 char for line wrapping. */"}},
- },
- text: `. /* A very long line of 46 char for line
-. wrapping. A very long line of 46 char
-. for line wrapping. */
-`,
- },
- {
- in: `A line of 36 char for line wrapping.
-//Another line starting with //`,
- out: []block{
- {opPara, []string{"A line of 36 char for line wrapping.\n",
- "//Another line starting with //"}},
- },
- text: `. A line of 36 char for line wrapping.
-. //Another line starting with //
-`,
- },
-}
-
-func TestBlocks(t *testing.T) {
- for i, tt := range blocksTests {
- b := blocks(tt.in)
- if !reflect.DeepEqual(b, tt.out) {
- t.Errorf("#%d: mismatch\nhave: %v\nwant: %v", i, b, tt.out)
- }
+ if pkgs["pkgdoc"] == nil {
+ t.Fatal("missing package pkgdoc")
}
-}
-
-func TestToText(t *testing.T) {
- var buf bytes.Buffer
- for i, tt := range blocksTests {
- ToText(&buf, tt.in, ". ", "$\t", 40)
- if have := buf.String(); have != tt.text {
- t.Errorf("#%d: mismatch\nhave: %s\nwant: %s\nhave vs want:\n%q\n%q", i, have, tt.text, have, tt.text)
- }
- buf.Reset()
+ pkg := New(pkgs["pkgdoc"], "testdata/pkgdoc", 0)
+
+ var (
+ input = "[T] and [U] are types, and [T.M] is a method, but [V] is a broken link. [rand.Int] and [crand.Reader] are things.\n"
+ wantHTML = `<p><a href="#T">T</a> and <a href="#U">U</a> are types, and <a href="#T.M">T.M</a> is a method, but [V] is a broken link. <a href="/math/rand#Int">rand.Int</a> and <a href="/crypto/rand#Reader">crand.Reader</a> are things.` + "\n"
+ wantOldHTML = "<p>[T] and [U] are <i>types</i>, and [T.M] is a method, but [V] is a broken link. [rand.Int] and [crand.Reader] are things.\n"
+ wantMarkdown = "[T](#T) and [U](#U) are types, and [T.M](#T.M) is a method, but \\[V] is a broken link. [rand.Int](/math/rand#Int) and [crand.Reader](/crypto/rand#Reader) are things.\n"
+ wantText = "T and U are types, and T.M is a method, but [V] is a broken link. rand.Int and\ncrand.Reader are things.\n"
+ wantOldText = "[T] and [U] are types, and [T.M] is a method, but [V] is a broken link.\n[rand.Int] and [crand.Reader] are things.\n"
+ wantSynopsis = "T and U are types, and T.M is a method, but [V] is a broken link."
+ wantOldSynopsis = "[T] and [U] are types, and [T.M] is a method, but [V] is a broken link."
+ )
+
+ if b := pkg.HTML(input); string(b) != wantHTML {
+ t.Errorf("%s", diff.Diff("pkg.HTML", b, "want", []byte(wantHTML)))
+ }
+ if b := pkg.Markdown(input); string(b) != wantMarkdown {
+ t.Errorf("%s", diff.Diff("pkg.Markdown", b, "want", []byte(wantMarkdown)))
+ }
+ if b := pkg.Text(input); string(b) != wantText {
+ t.Errorf("%s", diff.Diff("pkg.Text", b, "want", []byte(wantText)))
+ }
+ if b := pkg.Synopsis(input); b != wantSynopsis {
+ t.Errorf("%s", diff.Diff("pkg.Synopsis", []byte(b), "want", []byte(wantText)))
}
-}
-var emphasizeTests = []struct {
- in, out string
-}{
- {"", ""},
- {"http://[::1]:8080/foo.txt", `<a href="http://[::1]:8080/foo.txt">http://[::1]:8080/foo.txt</a>`},
- {"before (https://www.google.com) after", `before (<a href="https://www.google.com">https://www.google.com</a>) after`},
- {"before https://www.google.com:30/x/y/z:b::c. After", `before <a href="https://www.google.com:30/x/y/z:b::c">https://www.google.com:30/x/y/z:b::c</a>. After`},
- {"http://www.google.com/path/:;!-/?query=%34b#093124", `<a href="http://www.google.com/path/:;!-/?query=%34b#093124">http://www.google.com/path/:;!-/?query=%34b#093124</a>`},
- {"http://www.google.com/path/:;!-/?query=%34bar#093124", `<a href="http://www.google.com/path/:;!-/?query=%34bar#093124">http://www.google.com/path/:;!-/?query=%34bar#093124</a>`},
- {"http://www.google.com/index.html! After", `<a href="http://www.google.com/index.html">http://www.google.com/index.html</a>! After`},
- {"http://www.google.com/", `<a href="http://www.google.com/">http://www.google.com/</a>`},
- {"https://www.google.com/", `<a href="https://www.google.com/">https://www.google.com/</a>`},
- {"http://www.google.com/path.", `<a href="http://www.google.com/path">http://www.google.com/path</a>.`},
- {"http://en.wikipedia.org/wiki/Camellia_(cipher)", `<a href="http://en.wikipedia.org/wiki/Camellia_(cipher)">http://en.wikipedia.org/wiki/Camellia_(cipher)</a>`},
- {"(http://www.google.com/)", `(<a href="http://www.google.com/">http://www.google.com/</a>)`},
- {"http://gmail.com)", `<a href="http://gmail.com">http://gmail.com</a>)`},
- {"((http://gmail.com))", `((<a href="http://gmail.com">http://gmail.com</a>))`},
- {"http://gmail.com ((http://gmail.com)) ()", `<a href="http://gmail.com">http://gmail.com</a> ((<a href="http://gmail.com">http://gmail.com</a>)) ()`},
- {"Foo bar http://example.com/ quux!", `Foo bar <a href="http://example.com/">http://example.com/</a> quux!`},
- {"Hello http://example.com/%2f/ /world.", `Hello <a href="http://example.com/%2f/">http://example.com/%2f/</a> /world.`},
- {"Lorem http: ipsum //host/path", "Lorem http: ipsum //host/path"},
- {"javascript://is/not/linked", "javascript://is/not/linked"},
- {"http://foo", `<a href="http://foo">http://foo</a>`},
- {"art by [[https://www.example.com/person/][Person Name]]", `art by [[<a href="https://www.example.com/person/">https://www.example.com/person/</a>][Person Name]]`},
- {"please visit (http://golang.org/)", `please visit (<a href="http://golang.org/">http://golang.org/</a>)`},
- {"please visit http://golang.org/hello())", `please visit <a href="http://golang.org/hello()">http://golang.org/hello()</a>)`},
- {"http://git.qemu.org/?p=qemu.git;a=blob;f=qapi-schema.json;hb=HEAD", `<a href="http://git.qemu.org/?p=qemu.git;a=blob;f=qapi-schema.json;hb=HEAD">http://git.qemu.org/?p=qemu.git;a=blob;f=qapi-schema.json;hb=HEAD</a>`},
- {"https://foo.bar/bal/x(])", `<a href="https://foo.bar/bal/x(">https://foo.bar/bal/x(</a>])`}, // inner ] causes (]) to be cut off from URL
- {"foo [ http://bar(])", `foo [ <a href="http://bar(">http://bar(</a>])`}, // outer [ causes ]) to be cut off from URL
-}
+ var buf bytes.Buffer
-func TestEmphasize(t *testing.T) {
- for i, tt := range emphasizeTests {
- var buf bytes.Buffer
- emphasize(&buf, tt.in, nil, true)
- out := buf.String()
- if out != tt.out {
- t.Errorf("#%d: mismatch\nhave: %v\nwant: %v", i, out, tt.out)
- }
+ buf.Reset()
+ ToHTML(&buf, input, map[string]string{"types": ""})
+ if b := buf.Bytes(); string(b) != wantOldHTML {
+ t.Errorf("%s", diff.Diff("ToHTML", b, "want", []byte(wantOldHTML)))
}
-}
-func TestCommentEscape(t *testing.T) {
- commentTests := []struct {
- in, out string
- }{
- {"typically invoked as ``go tool asm'',", "typically invoked as " + ldquo + "go tool asm" + rdquo + ","},
- {"For more detail, run ``go help test'' and ``go help testflag''", "For more detail, run " + ldquo + "go help test" + rdquo + " and " + ldquo + "go help testflag" + rdquo},
+ buf.Reset()
+ ToText(&buf, input, "", "\t", 80)
+ if b := buf.Bytes(); string(b) != wantOldText {
+ t.Errorf("%s", diff.Diff("ToText", b, "want", []byte(wantOldText)))
}
- for i, tt := range commentTests {
- var buf strings.Builder
- commentEscape(&buf, tt.in, true)
- out := buf.String()
- if out != tt.out {
- t.Errorf("#%d: mismatch\nhave: %q\nwant: %q", i, out, tt.out)
- }
+
+ if b := Synopsis(input); b != wantOldSynopsis {
+ t.Errorf("%s", diff.Diff("Synopsis", []byte(b), "want", []byte(wantOldText)))
}
}
diff --git a/src/go/doc/doc.go b/src/go/doc/doc.go
index f0c1b5dd327..651a2c1f6c8 100644
--- a/src/go/doc/doc.go
+++ b/src/go/doc/doc.go
@@ -8,6 +8,7 @@ package doc
import (
"fmt"
"go/ast"
+ "go/doc/comment"
"go/token"
"strings"
)
@@ -35,6 +36,9 @@ type Package struct {
// the package. Examples are extracted from _test.go files
// provided to NewFromFiles.
Examples []*Example
+
+ importByName map[string]string
+ syms map[string]bool
}
// Value is the documentation for a (possibly grouped) var or const declaration.
@@ -119,7 +123,7 @@ func New(pkg *ast.Package, importPath string, mode Mode) *Package {
r.readPackage(pkg, mode)
r.computeMethodSets()
r.cleanupTypes()
- return &Package{
+ p := &Package{
Doc: r.doc,
Name: pkg.Name,
ImportPath: importPath,
@@ -131,6 +135,48 @@ func New(pkg *ast.Package, importPath string, mode Mode) *Package {
Types: sortedTypes(r.types, mode&AllMethods != 0),
Vars: sortedValues(r.values, token.VAR),
Funcs: sortedFuncs(r.funcs, true),
+
+ importByName: r.importByName,
+ syms: make(map[string]bool),
+ }
+
+ p.collectValues(p.Consts)
+ p.collectValues(p.Vars)
+ p.collectTypes(p.Types)
+ p.collectFuncs(p.Funcs)
+
+ return p
+}
+
+func (p *Package) collectValues(values []*Value) {
+ for _, v := range values {
+ for _, name := range v.Names {
+ p.syms[name] = true
+ }
+ }
+}
+
+func (p *Package) collectTypes(types []*Type) {
+ for _, t := range types {
+ if p.syms[t.Name] {
+ // Shouldn't be any cycles but stop just in case.
+ continue
+ }
+ p.syms[t.Name] = true
+ p.collectValues(t.Consts)
+ p.collectValues(t.Vars)
+ p.collectFuncs(t.Funcs)
+ p.collectFuncs(t.Methods)
+ }
+}
+
+func (p *Package) collectFuncs(funcs []*Func) {
+ for _, f := range funcs {
+ if f.Recv != "" {
+ p.syms[strings.TrimPrefix(f.Recv, "*")+"."+f.Name] = true
+ } else {
+ p.syms[f.Name] = true
+ }
}
}
@@ -218,3 +264,87 @@ func simpleImporter(imports map[string]*ast.Object, path string) (*ast.Object, e
}
return pkg, nil
}
+
+// lookupSym reports whether the package has a given symbol or method.
+//
+// If recv == "", HasSym reports whether the package has a top-level
+// const, func, type, or var named name.
+//
+// If recv != "", HasSym reports whether the package has a type
+// named recv with a method named name.
+func (p *Package) lookupSym(recv, name string) bool {
+ if recv != "" {
+ return p.syms[recv+"."+name]
+ }
+ return p.syms[name]
+}
+
+// lookupPackage returns the import path identified by name
+// in the given package. If name uniquely identifies a single import,
+// then lookupPackage returns that import.
+// If multiple packages are imported as name, importPath returns "", false.
+// Otherwise, if name is the name of p itself, importPath returns "", true,
+// to signal a reference to p.
+// Otherwise, importPath returns "", false.
+func (p *Package) lookupPackage(name string) (importPath string, ok bool) {
+ if path, ok := p.importByName[name]; ok {
+ if path == "" {
+ return "", false // multiple imports used the name
+ }
+ return path, true // found import
+ }
+ if p.Name == name {
+ return "", true // allow reference to this package
+ }
+ return "", false // unknown name
+}
+
+// Parser returns a doc comment parser configured
+// for parsing doc comments from package p.
+// Each call returns a new parser, so that the caller may
+// customize it before use.
+func (p *Package) Parser() *comment.Parser {
+ return &comment.Parser{
+ LookupPackage: p.lookupPackage,
+ LookupSym: p.lookupSym,
+ }
+}
+
+// Printer returns a doc comment printer configured
+// for printing doc comments from package p.
+// Each call returns a new printer, so that the caller may
+// customize it before use.
+func (p *Package) Printer() *comment.Printer {
+ // No customization today, but having p.Printer()
+ // gives us flexibility in the future, and it is convenient for callers.
+ return &comment.Printer{}
+}
+
+// HTML returns formatted HTML for the doc comment text.
+//
+// To customize details of the HTML, use [Package.Printer]
+// to obtain a [comment.Printer], and configure it
+// before calling its HTML method.
+func (p *Package) HTML(text string) []byte {
+ return p.Printer().HTML(p.Parser().Parse(text))
+}
+
+// Markdown returns formatted Markdown for the doc comment text.
+//
+// To customize details of the Markdown, use [Package.Printer]
+// to obtain a [comment.Printer], and configure it
+// before calling its Markdown method.
+func (p *Package) Markdown(text string) []byte {
+ return p.Printer().Markdown(p.Parser().Parse(text))
+}
+
+// Text returns formatted text for the doc comment text,
+// wrapped to 80 Unicode code points and using tabs for
+// code block indentation.
+//
+// To customize details of the formatting, use [Package.Printer]
+// to obtain a [comment.Printer], and configure it
+// before calling its Text method.
+func (p *Package) Text(text string) []byte {
+ return p.Printer().Text(p.Parser().Parse(text))
+}
diff --git a/src/go/doc/doc_test.go b/src/go/doc/doc_test.go
index 5a5fbd8bf3c..b79087e5387 100644
--- a/src/go/doc/doc_test.go
+++ b/src/go/doc/doc_test.go
@@ -152,15 +152,6 @@ func Test(t *testing.T) {
t.Run("AllMethods", func(t *testing.T) { test(t, AllMethods) })
}
-func TestAnchorID(t *testing.T) {
- const in = "Important Things 2 Know & Stuff"
- const want = "hdr-Important_Things_2_Know___Stuff"
- got := anchorID(in)
- if got != want {
- t.Errorf("anchorID(%q) = %q; want %q", in, got, want)
- }
-}
-
func TestFuncs(t *testing.T) {
fset := token.NewFileSet()
file, err := parser.ParseFile(fset, "funcs.go", strings.NewReader(funcsTestFile), parser.ParseComments)
diff --git a/src/go/doc/example.go b/src/go/doc/example.go
index fcd59e100ab..d1b5224b37d 100644
--- a/src/go/doc/example.go
+++ b/src/go/doc/example.go
@@ -456,10 +456,10 @@ func lastComment(b *ast.BlockStmt, c []*ast.CommentGroup) (i int, last *ast.Comm
//
// The classification process is ambiguous in some cases:
//
-// - ExampleFoo_Bar matches a type named Foo_Bar
-// or a method named Foo.Bar.
-// - ExampleFoo_bar matches a type named Foo_bar
-// or Foo (with a "bar" suffix).
+// - ExampleFoo_Bar matches a type named Foo_Bar
+// or a method named Foo.Bar.
+// - ExampleFoo_bar matches a type named Foo_bar
+// or Foo (with a "bar" suffix).
//
// Examples with malformed names are not associated with anything.
func classifyExamples(p *Package, examples []*Example) {
diff --git a/src/go/doc/reader.go b/src/go/doc/reader.go
index c591059e5c8..492e0397034 100644
--- a/src/go/doc/reader.go
+++ b/src/go/doc/reader.go
@@ -9,9 +9,12 @@ import (
"go/ast"
"go/token"
"internal/lazyregexp"
+ "path"
"sort"
"strconv"
"strings"
+ "unicode"
+ "unicode/utf8"
)
// ----------------------------------------------------------------------------
@@ -178,13 +181,16 @@ type reader struct {
filenames []string
notes map[string][]*Note
+ // imports
+ imports map[string]int
+ hasDotImp bool // if set, package contains a dot import
+ importByName map[string]string
+
// declarations
- imports map[string]int
- hasDotImp bool // if set, package contains a dot import
- values []*Value // consts and vars
- order int // sort order of const and var declarations (when we can't use a name)
- types map[string]*namedType
- funcs methodSet
+ values []*Value // consts and vars
+ order int // sort order of const and var declarations (when we can't use a name)
+ types map[string]*namedType
+ funcs methodSet
// support for package-local shadowing of predeclared types
shadowedPredecl map[string]bool
@@ -485,6 +491,28 @@ var (
noteCommentRx = lazyregexp.New(`^/[/*][ \t]*` + noteMarker) // MARKER(uid) at comment start
)
+// clean replaces each sequence of space, \r, or \t characters
+// with a single space and removes any trailing and leading spaces.
+func clean(s string) string {
+ var b []byte
+ p := byte(' ')
+ for i := 0; i < len(s); i++ {
+ q := s[i]
+ if q == '\r' || q == '\t' {
+ q = ' '
+ }
+ if q != ' ' || p != ' ' {
+ b = append(b, q)
+ p = q
+ }
+ }
+ // remove trailing blank, if any
+ if n := len(b); n > 0 && p == ' ' {
+ b = b[0 : n-1]
+ }
+ return string(b)
+}
+
// readNote collects a single note from a sequence of comments.
func (r *reader) readNote(list []*ast.Comment) {
text := (&ast.CommentGroup{List: list}).Text()
@@ -493,7 +521,7 @@ func (r *reader) readNote(list []*ast.Comment) {
// We remove any formatting so that we don't
// get spurious line breaks/indentation when
// showing the TODO body.
- body := clean(text[m[1]:], keepNL)
+ body := clean(text[m[1]:])
if body != "" {
marker := text[m[2]:m[3]]
r.notes[marker] = append(r.notes[marker], &Note{
@@ -550,8 +578,23 @@ func (r *reader) readFile(src *ast.File) {
if s, ok := spec.(*ast.ImportSpec); ok {
if import_, err := strconv.Unquote(s.Path.Value); err == nil {
r.imports[import_] = 1
- if s.Name != nil && s.Name.Name == "." {
- r.hasDotImp = true
+ var name string
+ if s.Name != nil {
+ name = s.Name.Name
+ if name == "." {
+ r.hasDotImp = true
+ }
+ }
+ if name != "." {
+ if name == "" {
+ name = assumedPackageName(import_)
+ }
+ old, ok := r.importByName[name]
+ if !ok {
+ r.importByName[name] = import_
+ } else if old != import_ && old != "" {
+ r.importByName[name] = "" // ambiguous
+ }
}
}
}
@@ -611,6 +654,7 @@ func (r *reader) readPackage(pkg *ast.Package, mode Mode) {
r.types = make(map[string]*namedType)
r.funcs = make(methodSet)
r.notes = make(map[string][]*Note)
+ r.importByName = make(map[string]string)
// sort package files before reading them so that the
// result does not depend on map iteration order
@@ -630,6 +674,12 @@ func (r *reader) readPackage(pkg *ast.Package, mode Mode) {
r.readFile(f)
}
+ for name, path := range r.importByName {
+ if path == "" {
+ delete(r.importByName, name)
+ }
+ }
+
// process functions now that we have better type information
for _, f := range pkg.Files {
for _, decl := range f.Decls {
@@ -950,3 +1000,30 @@ var predeclaredConstants = map[string]bool{
"nil": true,
"true": true,
}
+
+// assumedPackageName returns the assumed package name
+// for a given import path. This is a copy of
+// golang.org/x/tools/internal/imports.ImportPathToAssumedName.
+func assumedPackageName(importPath string) string {
+ notIdentifier := func(ch rune) bool {
+ return !('a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' ||
+ '0' <= ch && ch <= '9' ||
+ ch == '_' ||
+ ch >= utf8.RuneSelf && (unicode.IsLetter(ch) || unicode.IsDigit(ch)))
+ }
+
+ base := path.Base(importPath)
+ if strings.HasPrefix(base, "v") {
+ if _, err := strconv.Atoi(base[1:]); err == nil {
+ dir := path.Dir(importPath)
+ if dir != "." {
+ base = path.Base(dir)
+ }
+ }
+ }
+ base = strings.TrimPrefix(base, "go-")
+ if i := strings.IndexFunc(base, notIdentifier); i >= 0 {
+ base = base[:i]
+ }
+ return base
+}
diff --git a/src/go/doc/synopsis.go b/src/go/doc/synopsis.go
index ca607cc4e56..3c9e7e9b9e7 100644
--- a/src/go/doc/synopsis.go
+++ b/src/go/doc/synopsis.go
@@ -5,77 +5,74 @@
package doc
import (
+ "go/doc/comment"
"strings"
"unicode"
)
-// firstSentenceLen returns the length of the first sentence in s.
+// firstSentence returns the first sentence in s.
// The sentence ends after the first period followed by space and
// not preceded by exactly one uppercase letter.
-func firstSentenceLen(s string) int {
+func firstSentence(s string) string {
var ppp, pp, p rune
for i, q := range s {
if q == '\n' || q == '\r' || q == '\t' {
q = ' '
}
if q == ' ' && p == '.' && (!unicode.IsUpper(pp) || unicode.IsUpper(ppp)) {
- return i
+ return s[:i]
}
if p == '。' || p == '.' {
- return i
+ return s[:i]
}
ppp, pp, p = pp, p, q
}
- return len(s)
+ return s
}
-const (
- keepNL = 1 << iota
-)
+// Synopsis returns a cleaned version of the first sentence in text.
+//
+// Deprecated: New programs should use [Package.Synopsis] instead,
+// which handles links in text properly.
+func Synopsis(text string) string {
+ var p Package
+ return p.Synopsis(text)
+}
-// clean replaces each sequence of space, \n, \r, or \t characters
-// with a single space and removes any trailing and leading spaces.
-// If the keepNL flag is set, newline characters are passed through
-// instead of being change to spaces.
-func clean(s string, flags int) string {
- var b []byte
- p := byte(' ')
- for i := 0; i < len(s); i++ {
- q := s[i]
- if (flags&keepNL) == 0 && q == '\n' || q == '\r' || q == '\t' {
- q = ' '
- }
- if q != ' ' || p != ' ' {
- b = append(b, q)
- p = q
- }
- }
- // remove trailing blank, if any
- if n := len(b); n > 0 && p == ' ' {
- b = b[0 : n-1]
- }
- return string(b)
+// IllegalPrefixes is a list of lower-case prefixes that identify
+// a comment as not being a doc comment.
+// This helps to avoid misinterpreting the common mistake
+// of a copyright notice immediately before a package statement
+// as being a doc comment.
+var IllegalPrefixes = []string{
+ "copyright",
+ "all rights",
+ "author",
}
-// Synopsis returns a cleaned version of the first sentence in s.
-// That sentence ends after the first period followed by space and
-// not preceded by exactly one uppercase letter. The result string
-// has no \n, \r, or \t characters and uses only single spaces between
-// words. If s starts with any of the IllegalPrefixes, the result
-// is the empty string.
-func Synopsis(s string) string {
- s = clean(s[0:firstSentenceLen(s)], 0)
+// Synopsis returns a cleaned version of the first sentence in text.
+// That sentence ends after the first period followed by space and not
+// preceded by exactly one uppercase letter, or at the first paragraph break.
+// The result string has no \n, \r, or \t characters and uses only single
+// spaces between words. If text starts with any of the IllegalPrefixes,
+// the result is the empty string.
+func (p *Package) Synopsis(text string) string {
+ text = firstSentence(text)
+ lower := strings.ToLower(text)
for _, prefix := range IllegalPrefixes {
- if strings.HasPrefix(strings.ToLower(s), prefix) {
+ if strings.HasPrefix(lower, prefix) {
return ""
}
}
- s = convertQuotes(s)
- return s
-}
-
-var IllegalPrefixes = []string{
- "copyright",
- "all rights",
- "author",
+ pr := p.Printer()
+ pr.TextWidth = -1
+ d := p.Parser().Parse(text)
+ if len(d.Content) == 0 {
+ return ""
+ }
+ if _, ok := d.Content[0].(*comment.Paragraph); !ok {
+ return ""
+ }
+ d.Content = d.Content[:1] // might be blank lines, code blocks, etc in “first sentence”
+ return strings.TrimSpace(string(pr.Text(d)))
}
diff --git a/src/go/doc/synopsis_test.go b/src/go/doc/synopsis_test.go
index 3f443dc7578..158c734bf02 100644
--- a/src/go/doc/synopsis_test.go
+++ b/src/go/doc/synopsis_test.go
@@ -18,8 +18,8 @@ var tests = []struct {
{" foo. ", 6, "foo."},
{" foo\t bar.\n", 12, "foo bar."},
{" foo\t bar.\n", 12, "foo bar."},
- {"a b\n\nc\r\rd\t\t", 12, "a b c d"},
- {"a b\n\nc\r\rd\t\t . BLA", 15, "a b c d ."},
+ {"a b\n\nc\r\rd\t\t", 12, "a b"},
+ {"a b\n\nc\r\rd\t\t . BLA", 15, "a b"},
{"Package poems by T.S.Eliot. To rhyme...", 27, "Package poems by T.S.Eliot."},
{"Package poems by T. S. Eliot. To rhyme...", 29, "Package poems by T. S. Eliot."},
{"foo implements the foo ABI. The foo ABI is...", 27, "foo implements the foo ABI."},
@@ -35,18 +35,18 @@ var tests = []struct {
{"All Rights reserved. Package foo does bar.", 20, ""},
{"All rights reserved. Package foo does bar.", 20, ""},
{"Authors: foo@bar.com. Package foo does bar.", 21, ""},
- {"typically invoked as ``go tool asm'',", 37, "typically invoked as " + ulquo + "go tool asm" + urquo + ","},
+ {"typically invoked as ``go tool asm'',", 37, "typically invoked as “go tool asm”,"},
}
func TestSynopsis(t *testing.T) {
for _, e := range tests {
- fsl := firstSentenceLen(e.txt)
- if fsl != e.fsl {
- t.Errorf("got fsl = %d; want %d for %q\n", fsl, e.fsl, e.txt)
+ fs := firstSentence(e.txt)
+ if fs != e.txt[:e.fsl] {
+ t.Errorf("firstSentence(%q) = %q, want %q", e.txt, fs, e.txt[:e.fsl])
}
syn := Synopsis(e.txt)
if syn != e.syn {
- t.Errorf("got syn = %q; want %q for %q\n", syn, e.syn, e.txt)
+ t.Errorf("Synopsis(%q) = %q, want %q", e.txt, syn, e.syn)
}
}
}
diff --git a/src/go/doc/testdata/pkgdoc/doc.go b/src/go/doc/testdata/pkgdoc/doc.go
new file mode 100644
index 00000000000..61bd4e32f93
--- /dev/null
+++ b/src/go/doc/testdata/pkgdoc/doc.go
@@ -0,0 +1,19 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package pkgdoc
+
+import (
+ crand "crypto/rand"
+ "math/rand"
+)
+
+type T int
+
+type U int
+
+func (T) M() {}
+
+var _ = rand.Int
+var _ = crand.Reader
diff --git a/src/go/doc/testdata/testing.go b/src/go/doc/testdata/testing.go
index 80238df283a..6365ffceedc 100644
--- a/src/go/doc/testdata/testing.go
+++ b/src/go/doc/testdata/testing.go
@@ -3,7 +3,7 @@
// license that can be found in the LICENSE file.
// Package testing provides support for automated testing of Go packages.
-// It is intended to be used in concert with the ``go test'' utility, which automates
+// It is intended to be used in concert with the “go test” utility, which automates
// execution of any function of the form
// func TestXxx(*testing.T)
// where Xxx can be any alphanumeric string (but the first letter must not be in
diff --git a/src/go/format/benchmark_test.go b/src/go/format/benchmark_test.go
index d434d6f5493..f42aac4c511 100644
--- a/src/go/format/benchmark_test.go
+++ b/src/go/format/benchmark_test.go
@@ -21,16 +21,17 @@ var debug = flag.Bool("debug", false, "write .src files containing formatting in
// array1 generates an array literal with n elements of the form:
//
// var _ = [...]byte{
-// // 0
-// 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
-// 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
-// 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
-// 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
-// 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
-// // 40
-// 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
-// 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
-// ...
+//
+// // 0
+// 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+// 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+// 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+// 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+// 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+// // 40
+// 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+// 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+// ...
func array1(buf *bytes.Buffer, n int) {
buf.WriteString("var _ = [...]byte{\n")
for i := 0; i < n; {
diff --git a/src/go/internal/gccgoimporter/parser.go b/src/go/internal/gccgoimporter/parser.go
index 10402fe43e6..536083ae08c 100644
--- a/src/go/internal/gccgoimporter/parser.go
+++ b/src/go/internal/gccgoimporter/parser.go
@@ -1115,9 +1115,10 @@ func (p *parser) maybeCreatePackage() {
}
// InitDataDirective = ( "v1" | "v2" | "v3" ) ";" |
-// "priority" int ";" |
-// "init" { PackageInit } ";" |
-// "checksum" unquotedString ";" .
+//
+// "priority" int ";" |
+// "init" { PackageInit } ";" |
+// "checksum" unquotedString ";" .
func (p *parser) parseInitDataDirective() {
if p.tok != scanner.Ident {
// unexpected token kind; panic
@@ -1168,15 +1169,16 @@ func (p *parser) parseInitDataDirective() {
}
// Directive = InitDataDirective |
-// "package" unquotedString [ unquotedString ] [ unquotedString ] ";" |
-// "pkgpath" unquotedString ";" |
-// "prefix" unquotedString ";" |
-// "import" unquotedString unquotedString string ";" |
-// "indirectimport" unquotedString unquotedstring ";" |
-// "func" Func ";" |
-// "type" Type ";" |
-// "var" Var ";" |
-// "const" Const ";" .
+//
+// "package" unquotedString [ unquotedString ] [ unquotedString ] ";" |
+// "pkgpath" unquotedString ";" |
+// "prefix" unquotedString ";" |
+// "import" unquotedString unquotedString string ";" |
+// "indirectimport" unquotedString unquotedstring ";" |
+// "func" Func ";" |
+// "type" Type ";" |
+// "var" Var ";" |
+// "const" Const ";" .
func (p *parser) parseDirective() {
if p.tok != scanner.Ident {
// unexpected token kind; panic
diff --git a/src/go/parser/parser.go b/src/go/parser/parser.go
index 3eb00e94464..39ba9b33a7f 100644
--- a/src/go/parser/parser.go
+++ b/src/go/parser/parser.go
@@ -13,7 +13,6 @@
// treated like an ordinary parameter list and thus may contain multiple
// entries where the spec permits exactly one. Consequently, the corresponding
// field in the AST (ast.FuncDecl.Recv) field is not restricted to one entry.
-//
package parser
import (
diff --git a/src/go/printer/comment.go b/src/go/printer/comment.go
new file mode 100644
index 00000000000..76dd31efc73
--- /dev/null
+++ b/src/go/printer/comment.go
@@ -0,0 +1,154 @@
+// Copyright 2022 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package printer
+
+import (
+ "go/ast"
+ "go/doc/comment"
+ "strings"
+)
+
+// formatDocComment reformats the doc comment list,
+// returning the canonical formatting.
+func formatDocComment(list []*ast.Comment) []*ast.Comment {
+ // Extract comment text (removing comment markers).
+ var kind, text string
+ var directives []*ast.Comment
+ if len(list) == 1 && strings.HasPrefix(list[0].Text, "/*") {
+ kind = "/*"
+ text = list[0].Text
+ if !strings.Contains(text, "\n") || allStars(text) {
+ // Single-line /* .. */ comment in doc comment position,
+ // or multiline old-style comment like
+ // /*
+ // * Comment
+ // * text here.
+ // */
+ // Should not happen, since it will not work well as a
+ // doc comment, but if it does, just ignore:
+ // reformatting it will only make the situation worse.
+ return list
+ }
+ text = text[2 : len(text)-2] // cut /* and */
+ } else if strings.HasPrefix(list[0].Text, "//") {
+ kind = "//"
+ var b strings.Builder
+ for _, c := range list {
+ if !strings.HasPrefix(c.Text, "//") {
+ return list
+ }
+ // Accumulate //go:build etc lines separately.
+ if isDirective(c.Text[2:]) {
+ directives = append(directives, c)
+ continue
+ }
+ b.WriteString(strings.TrimPrefix(c.Text[2:], " "))
+ b.WriteString("\n")
+ }
+ text = b.String()
+ } else {
+ // Not sure what this is, so leave alone.
+ return list
+ }
+
+ if text == "" {
+ return list
+ }
+
+ // Parse comment and reformat as text.
+ var p comment.Parser
+ d := p.Parse(text)
+
+ var pr comment.Printer
+ text = string(pr.Comment(d))
+
+ // For /* */ comment, return one big comment with text inside.
+ slash := list[0].Slash
+ if kind == "/*" {
+ c := &ast.Comment{
+ Slash: slash,
+ Text: "/*\n" + text + "*/",
+ }
+ return []*ast.Comment{c}
+ }
+
+ // For // comment, return sequence of // lines.
+ var out []*ast.Comment
+ for text != "" {
+ var line string
+ line, text, _ = strings.Cut(text, "\n")
+ if line == "" {
+ line = "//"
+ } else if strings.HasPrefix(line, "\t") {
+ line = "//" + line
+ } else {
+ line = "// " + line
+ }
+ out = append(out, &ast.Comment{
+ Slash: slash,
+ Text: line,
+ })
+ }
+ if len(directives) > 0 {
+ out = append(out, &ast.Comment{
+ Slash: slash,
+ Text: "//",
+ })
+ for _, c := range directives {
+ out = append(out, &ast.Comment{
+ Slash: slash,
+ Text: c.Text,
+ })
+ }
+ }
+ return out
+}
+
+// isDirective reports whether c is a comment directive.
+// See go.dev/issue/37974.
+// This code is also in go/ast.
+func isDirective(c string) bool {
+ // "//line " is a line directive.
+ // "//extern " is for gccgo.
+ // "//export " is for cgo.
+ // (The // has been removed.)
+ if strings.HasPrefix(c, "line ") || strings.HasPrefix(c, "extern ") || strings.HasPrefix(c, "export ") {
+ return true
+ }
+
+ // "//[a-z0-9]+:[a-z0-9]"
+ // (The // has been removed.)
+ colon := strings.Index(c, ":")
+ if colon <= 0 || colon+1 >= len(c) {
+ return false
+ }
+ for i := 0; i <= colon+1; i++ {
+ if i == colon {
+ continue
+ }
+ b := c[i]
+ if !('a' <= b && b <= 'z' || '0' <= b && b <= '9') {
+ return false
+ }
+ }
+ return true
+}
+
+// allStars reports whether text is the interior of an
+// old-style /* */ comment with a star at the start of each line.
+func allStars(text string) bool {
+ for i := 0; i < len(text); i++ {
+ if text[i] == '\n' {
+ j := i + 1
+ for j < len(text) && (text[j] == ' ' || text[j] == '\t') {
+ j++
+ }
+ if j < len(text) && text[j] != '*' {
+ return false
+ }
+ }
+ }
+ return true
+}
diff --git a/src/go/printer/nodes.go b/src/go/printer/nodes.go
index f4fbde8ae6c..0494f99d246 100644
--- a/src/go/printer/nodes.go
+++ b/src/go/printer/nodes.go
@@ -125,7 +125,8 @@ const filteredMsg = "contains filtered or unexported fields"
//
// TODO(gri) Consider rewriting this to be independent of []ast.Expr
// so that we can use the algorithm for any kind of list
-// (e.g., pass list via a channel over which to range).
+//
+// (e.g., pass list via a channel over which to range).
func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exprListMode, next0 token.Pos, isIncomplete bool) {
if len(list) == 0 {
if isIncomplete {
@@ -714,6 +715,7 @@ func reduceDepth(depth int) int {
// (Algorithm suggestion by Russ Cox.)
//
// The precedences are:
+//
// 5 * / % << >> & &^
// 4 + - | ^
// 3 == != < <= > >=
@@ -726,24 +728,24 @@ func reduceDepth(depth int) int {
// To choose the cutoff, look at the whole expression but excluding primary
// expressions (function calls, parenthesized exprs), and apply these rules:
//
-// 1) If there is a binary operator with a right side unary operand
-// that would clash without a space, the cutoff must be (in order):
+// 1. If there is a binary operator with a right side unary operand
+// that would clash without a space, the cutoff must be (in order):
//
-// /* 6
-// && 6
-// &^ 6
-// ++ 5
-// -- 5
+// /* 6
+// && 6
+// &^ 6
+// ++ 5
+// -- 5
//
-// (Comparison operators always have spaces around them.)
+// (Comparison operators always have spaces around them.)
//
-// 2) If there is a mix of level 5 and level 4 operators, then the cutoff
-// is 5 (use spaces to distinguish precedence) in Normal mode
-// and 4 (never use spaces) in Compact mode.
+// 2. If there is a mix of level 5 and level 4 operators, then the cutoff
+// is 5 (use spaces to distinguish precedence) in Normal mode
+// and 4 (never use spaces) in Compact mode.
//
-// 3) If there are no level 4 operators or no level 5 operators, then the
-// cutoff is 6 (always use spaces) in Normal mode
-// and 4 (never use spaces) in Compact mode.
+// 3. If there are no level 4 operators or no level 5 operators, then the
+// cutoff is 6 (always use spaces) in Normal mode
+// and 4 (never use spaces) in Compact mode.
func (p *printer) binaryExpr(x *ast.BinaryExpr, prec1, cutoff, depth int) {
prec := x.Op.Precedence()
if prec < prec1 {
@@ -1483,23 +1485,23 @@ func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool) {
//
// For example, the declaration:
//
-// const (
-// foobar int = 42 // comment
-// x = 7 // comment
-// foo
-// bar = 991
-// )
+// const (
+// foobar int = 42 // comment
+// x = 7 // comment
+// foo
+// bar = 991
+// )
//
// leads to the type/values matrix below. A run of value columns (V) can
// be moved into the type column if there is no type for any of the values
// in that column (we only move entire columns so that they align properly).
//
-// matrix formatted result
-// matrix
-// T V -> T V -> true there is a T and so the type
-// - V - V true column must be kept
-// - - - - false
-// - V V - false V is moved into T column
+// matrix formatted result
+// matrix
+// T V -> T V -> true there is a T and so the type
+// - V - V true column must be kept
+// - - - - false
+// - V V - false V is moved into T column
func keepTypeColumn(specs []ast.Spec) []bool {
m := make([]bool, len(specs))
diff --git a/src/go/printer/printer.go b/src/go/printer/printer.go
index 5014f59ab5c..25eec6bd751 100644
--- a/src/go/printer/printer.go
+++ b/src/go/printer/printer.go
@@ -738,11 +738,28 @@ func (p *printer) containsLinebreak() bool {
func (p *printer) intersperseComments(next token.Position, tok token.Token) (wroteNewline, droppedFF bool) {
var last *ast.Comment
for p.commentBefore(next) {
- for _, c := range p.comment.List {
+ list := p.comment.List
+ changed := false
+ if p.lastTok != token.IMPORT && // do not rewrite cgo's import "C" comments
+ p.posFor(p.comment.Pos()).Column == 1 &&
+ p.posFor(p.comment.End()+1) == next {
+ // Unindented comment abutting next token position:
+ // a top-level doc comment.
+ list = formatDocComment(list)
+ changed = true
+ }
+ for _, c := range list {
p.writeCommentPrefix(p.posFor(c.Pos()), next, last, tok)
p.writeComment(c)
last = c
}
+ // In case list was rewritten, change print state to where
+ // the original list would have ended.
+ if len(p.comment.List) > 0 && changed {
+ last = p.comment.List[len(p.comment.List)-1]
+ p.pos = p.posFor(last.End())
+ p.last = p.pos
+ }
p.nextComment()
}
diff --git a/src/go/printer/printer_test.go b/src/go/printer/printer_test.go
index ad2d86052a4..cb62b3e4f35 100644
--- a/src/go/printer/printer_test.go
+++ b/src/go/printer/printer_test.go
@@ -6,6 +6,7 @@ package printer
import (
"bytes"
+ "errors"
"flag"
"fmt"
"go/ast"
@@ -92,8 +93,7 @@ func checkEqual(aname, bname string, a, b []byte) error {
if bytes.Equal(a, b) {
return nil
}
-
- return fmt.Errorf("diff %s %s\n%s", aname, bname, diff.Diff(aname, a, bname, b))
+ return errors.New(string(diff.Diff(aname, a, bname, b)))
}
func runcheck(t *testing.T, source, golden string, mode checkMode) {
diff --git a/src/go/printer/testdata/comments.golden b/src/go/printer/testdata/comments.golden
index 1a21fff3314..62f37ea0910 100644
--- a/src/go/printer/testdata/comments.golden
+++ b/src/go/printer/testdata/comments.golden
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
// This is a package for testing comment placement by go/printer.
-//
package main
import "fmt" // fmt
@@ -97,6 +96,13 @@ type S3 struct {
f3 int // f3 is not exported
}
+// Here is a comment.
+// Here is an accidentally unindented line.
+// More comment.
+//
+//dir:ect ive
+type directiveCheck struct{}
+
// This comment group should be separated
// with a newline from the next comment
// group.
@@ -116,9 +122,7 @@ func f0() {
x := pi
}
-//
// This comment should be associated with f1, with one blank line before the comment.
-//
func f1() {
f0()
/* 1 */
@@ -688,9 +692,16 @@ func _() {
}
}
+//extern foo
+func foo() {}
+
+//export bar
+func bar() {}
+
// Print line directives correctly.
// The following is a legal line directive.
+//
//line foo:1
func _() {
_ = 0
diff --git a/src/go/printer/testdata/comments.input b/src/go/printer/testdata/comments.input
index aa428a2aa68..4bdafc3781d 100644
--- a/src/go/printer/testdata/comments.input
+++ b/src/go/printer/testdata/comments.input
@@ -97,6 +97,12 @@ type S3 struct {
f3 int // f3 is not exported
}
+// Here is a comment.
+//Here is an accidentally unindented line.
+//dir:ect ive
+// More comment.
+type directiveCheck struct{}
+
// This comment group should be separated
// with a newline from the next comment
// group.
@@ -616,7 +622,7 @@ func _() {
func _() {
f(); f()
f(); /* comment */ f()
- f() /* comment */; f()
+ f() /* comment */; f()
f(); /* a */ /* b */ f()
f() /* a */ /* b */; f()
f() /* a */; /* b */ f()
@@ -663,7 +669,7 @@ func _() {
// This way, commas interspersed in lists stay with the respective expression.
func f(x/* comment */, y int, z int /* comment */, u, v, w int /* comment */) {
f(x /* comment */, y)
- f(x /* comment */,
+ f(x /* comment */,
y)
f(
x /* comment */,
@@ -685,6 +691,12 @@ func _() {
}
}
+//extern foo
+func foo() {}
+
+//export bar
+func bar() {}
+
// Print line directives correctly.
// The following is a legal line directive.
@@ -718,10 +730,10 @@ var lflag bool // -l - disable line directives
// Trailing white space in comments should be trimmed
func _() {
-// This comment has 4 blanks following that should be trimmed:
-/* Each line of this comment has blanks or tabs following that should be trimmed:
- line 2:
- line 3:
+// This comment has 4 blanks following that should be trimmed:
+/* Each line of this comment has blanks or tabs following that should be trimmed:
+ line 2:
+ line 3:
*/
}
diff --git a/src/go/printer/testdata/comments.x b/src/go/printer/testdata/comments.x
index ae7729286e5..5d088ab2c38 100644
--- a/src/go/printer/testdata/comments.x
+++ b/src/go/printer/testdata/comments.x
@@ -1,5 +1,4 @@
// This is a package for testing comment placement by go/printer.
-//
package main
// The SZ struct; it is empty.
diff --git a/src/go/printer/testdata/comments2.golden b/src/go/printer/testdata/comments2.golden
index 8b3a94ddcd0..83213d1a9db 100644
--- a/src/go/printer/testdata/comments2.golden
+++ b/src/go/printer/testdata/comments2.golden
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
// This is a package for testing comment placement by go/printer.
-//
package main
// Test cases for idempotent comment formatting (was issue 1835).
diff --git a/src/go/printer/testdata/doc.golden b/src/go/printer/testdata/doc.golden
new file mode 100644
index 00000000000..7ac241a4bb6
--- /dev/null
+++ b/src/go/printer/testdata/doc.golden
@@ -0,0 +1,21 @@
+package p
+
+/*
+Doc comment.
+
+ - List1.
+
+ - List2.
+*/
+var X int
+
+/* erroneous doc comment */
+var Y int
+
+/*
+ * Another erroneous
+ * doc comment.
+ */
+var Z int
+
+
diff --git a/src/go/printer/testdata/doc.input b/src/go/printer/testdata/doc.input
new file mode 100644
index 00000000000..5c057ed2c4c
--- /dev/null
+++ b/src/go/printer/testdata/doc.input
@@ -0,0 +1,20 @@
+package p
+
+/*
+Doc comment.
+ - List1.
+
+ - List2.
+*/
+var X int
+
+/* erroneous doc comment */
+var Y int
+
+/*
+ * Another erroneous
+ * doc comment.
+ */
+var Z int
+
+
diff --git a/src/go/scanner/scanner.go b/src/go/scanner/scanner.go
index b53de7a4270..07e07581f79 100644
--- a/src/go/scanner/scanner.go
+++ b/src/go/scanner/scanner.go
@@ -5,7 +5,6 @@
// Package scanner implements a scanner for Go source text.
// It takes a []byte as source which can then be tokenized
// through repeated calls to the Scan method.
-//
package scanner
import (
diff --git a/src/go/token/token.go b/src/go/token/token.go
index b6918832617..3ae10d823cf 100644
--- a/src/go/token/token.go
+++ b/src/go/token/token.go
@@ -4,7 +4,6 @@
// Package token defines constants representing the lexical tokens of the Go
// programming language and basic operations on tokens (printing, predicates).
-//
package token
import (
diff --git a/src/go/types/api.go b/src/go/types/api.go
index 04342bfac57..4f63d627130 100644
--- a/src/go/types/api.go
+++ b/src/go/types/api.go
@@ -23,7 +23,6 @@
// Use Info.Types[expr].Type for the results of type inference.
//
// For a tutorial, see https://golang.org/s/types-tutorial.
-//
package types
import (
diff --git a/src/go/types/check_test.go b/src/go/types/check_test.go
index 88622d6b0c1..8765ef2e804 100644
--- a/src/go/types/check_test.go
+++ b/src/go/types/check_test.go
@@ -319,7 +319,7 @@ func testFiles(t *testing.T, sizes Sizes, filenames []string, srcs [][]byte, man
// (and a separating "--"). For instance, to test the package made
// of the files foo.go and bar.go, use:
//
-// go test -run Manual -- foo.go bar.go
+// go test -run Manual -- foo.go bar.go
//
// If no source arguments are provided, the file testdata/manual.go
// is used instead.
diff --git a/src/go/types/gotype.go b/src/go/types/gotype.go
index 5d27bb7a077..e8ff9658daf 100644
--- a/src/go/types/gotype.go
+++ b/src/go/types/gotype.go
@@ -32,9 +32,11 @@ checking packages containing imports with relative import paths
files to include for such packages.
Usage:
+
gotype [flags] [path...]
The flags are:
+
-t
include local test files in a directory (ignored if -x is provided)
-x
@@ -47,6 +49,7 @@ The flags are:
compiler used for installed packages (gc, gccgo, or source); default: source
Flags controlling additional output:
+
-ast
print AST
-trace
@@ -74,7 +77,6 @@ cmd/compile:
To verify the output of a pipe:
echo "package foo" | gotype
-
*/
package main
diff --git a/src/go/types/infer.go b/src/go/types/infer.go
index 32ec5495ee0..031850b8dad 100644
--- a/src/go/types/infer.go
+++ b/src/go/types/infer.go
@@ -22,11 +22,11 @@ import (
//
// Inference proceeds as follows:
//
-// Starting with given type arguments
-// 1) apply FTI (function type inference) with typed arguments,
-// 2) apply CTI (constraint type inference),
-// 3) apply FTI with untyped function arguments,
-// 4) apply CTI.
+// Starting with given type arguments
+// 1) apply FTI (function type inference) with typed arguments,
+// 2) apply CTI (constraint type inference),
+// 3) apply FTI with untyped function arguments,
+// 4) apply CTI.
//
// The process stops as soon as all type arguments are known or an error occurs.
func (check *Checker) infer(posn positioner, tparams []*TypeParam, targs []Type, params *Tuple, args []*operand) (result []Type) {
diff --git a/src/go/types/lookup.go b/src/go/types/lookup.go
index e38f56c9565..70e211d0828 100644
--- a/src/go/types/lookup.go
+++ b/src/go/types/lookup.go
@@ -25,9 +25,9 @@ import (
// The last index entry is the field or method index in the (possibly embedded)
// type where the entry was found, either:
//
-// 1) the list of declared methods of a named type; or
-// 2) the list of all methods (method set) of an interface type; or
-// 3) the list of fields of a struct type.
+// 1. the list of declared methods of a named type; or
+// 2. the list of all methods (method set) of an interface type; or
+// 3. the list of fields of a struct type.
//
// The earlier index entries are the indices of the embedded struct fields
// traversed to get to the found entry, starting at depth 0.
@@ -35,12 +35,12 @@ import (
// If no entry is found, a nil object is returned. In this case, the returned
// index and indirect values have the following meaning:
//
-// - If index != nil, the index sequence points to an ambiguous entry
-// (the same name appeared more than once at the same embedding level).
+// - If index != nil, the index sequence points to an ambiguous entry
+// (the same name appeared more than once at the same embedding level).
//
-// - If indirect is set, a method with a pointer receiver type was found
-// but there was no pointer on the path from the actual receiver type to
-// the method's formal receiver base type, nor was the receiver addressable.
+// - If indirect is set, a method with a pointer receiver type was found
+// but there was no pointer on the path from the actual receiver type to
+// the method's formal receiver base type, nor was the receiver addressable.
func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) {
if T == nil {
panic("LookupFieldOrMethod on nil type")
diff --git a/src/go/types/selection.go b/src/go/types/selection.go
index 4e06ab16b4e..09c304d3785 100644
--- a/src/go/types/selection.go
+++ b/src/go/types/selection.go
@@ -92,9 +92,9 @@ func (s *Selection) Type() Type {
// The last index entry is the field or method index of the type declaring f;
// either:
//
-// 1) the list of declared methods of a named type; or
-// 2) the list of methods of an interface type; or
-// 3) the list of fields of a struct type.
+// 1. the list of declared methods of a named type; or
+// 2. the list of methods of an interface type; or
+// 3. the list of fields of a struct type.
//
// The earlier index entries are the indices of the embedded fields implicitly
// traversed to get from (the type of) x to f, starting at embedding depth 0.
@@ -111,6 +111,7 @@ func (s *Selection) String() string { return SelectionString(s, nil) }
// package-level objects, and may be nil.
//
// Examples:
+//
// "field (T) f int"
// "method (T) f(X) Y"
// "method expr (T) f(X) Y"
diff --git a/src/go/types/sizes.go b/src/go/types/sizes.go
index fd18fc57969..494e0454770 100644
--- a/src/go/types/sizes.go
+++ b/src/go/types/sizes.go
@@ -24,19 +24,19 @@ type Sizes interface {
// StdSizes is a convenience type for creating commonly used Sizes.
// It makes the following simplifying assumptions:
//
-// - The size of explicitly sized basic types (int16, etc.) is the
-// specified size.
-// - The size of strings and interfaces is 2*WordSize.
-// - The size of slices is 3*WordSize.
-// - The size of an array of n elements corresponds to the size of
-// a struct of n consecutive fields of the array's element type.
-// - The size of a struct is the offset of the last field plus that
-// field's size. As with all element types, if the struct is used
-// in an array its size must first be aligned to a multiple of the
-// struct's alignment.
-// - All other types have size WordSize.
-// - Arrays and structs are aligned per spec definition; all other
-// types are naturally aligned with a maximum alignment MaxAlign.
+// - The size of explicitly sized basic types (int16, etc.) is the
+// specified size.
+// - The size of strings and interfaces is 2*WordSize.
+// - The size of slices is 3*WordSize.
+// - The size of an array of n elements corresponds to the size of
+// a struct of n consecutive fields of the array's element type.
+// - The size of a struct is the offset of the last field plus that
+// field's size. As with all element types, if the struct is used
+// in an array its size must first be aligned to a multiple of the
+// struct's alignment.
+// - All other types have size WordSize.
+// - Arrays and structs are aligned per spec definition; all other
+// types are naturally aligned with a maximum alignment MaxAlign.
//
// *StdSizes implements Sizes.
type StdSizes struct {
diff --git a/src/go/types/typeterm.go b/src/go/types/typeterm.go
index 13b6ce6d0df..a7b89696272 100644
--- a/src/go/types/typeterm.go
+++ b/src/go/types/typeterm.go
@@ -6,10 +6,10 @@ package types
// A term describes elementary type sets:
//
-// ∅: (*term)(nil) == ∅ // set of no types (empty set)
-// 𝓤: &term{} == 𝓤 // set of all types (𝓤niverse)
-// T: &term{false, T} == {T} // set of type T
-// ~t: &term{true, t} == {t' | under(t') == t} // set of types with underlying type t
+// ∅: (*term)(nil) == ∅ // set of no types (empty set)
+// 𝓤: &term{} == 𝓤 // set of all types (𝓤niverse)
+// T: &term{false, T} == {T} // set of type T
+// ~t: &term{true, t} == {t' | under(t') == t} // set of types with underlying type t
type term struct {
tilde bool // valid if typ != nil
typ Type
diff --git a/src/hash/adler32/adler32.go b/src/hash/adler32/adler32.go
index e8783e4c39e..38d644d1ee5 100644
--- a/src/hash/adler32/adler32.go
+++ b/src/hash/adler32/adler32.go
@@ -5,6 +5,7 @@
// Package adler32 implements the Adler-32 checksum.
//
// It is defined in RFC 1950:
+//
// Adler-32 is composed of two sums accumulated per byte: s1 is
// the sum of all bytes, s2 is the sum of all s1 values. Both sums
// are done modulo 65521. s1 is initialized to 1, s2 to zero. The
diff --git a/src/hash/crc32/crc32_amd64.go b/src/hash/crc32/crc32_amd64.go
index 7017a89304f..6be129f5ddd 100644
--- a/src/hash/crc32/crc32_amd64.go
+++ b/src/hash/crc32/crc32_amd64.go
@@ -18,11 +18,13 @@ import (
// castagnoliSSE42 is defined in crc32_amd64.s and uses the SSE 4.2 CRC32
// instruction.
+//
//go:noescape
func castagnoliSSE42(crc uint32, p []byte) uint32
// castagnoliSSE42Triple is defined in crc32_amd64.s and uses the SSE 4.2 CRC32
// instruction.
+//
//go:noescape
func castagnoliSSE42Triple(
crcA, crcB, crcC uint32,
@@ -32,6 +34,7 @@ func castagnoliSSE42Triple(
// ieeeCLMUL is defined in crc_amd64.s and uses the PCLMULQDQ
// instruction as well as SSE 4.1.
+//
//go:noescape
func ieeeCLMUL(crc uint32, p []byte) uint32
diff --git a/src/hash/crc32/crc32_ppc64le.go b/src/hash/crc32/crc32_ppc64le.go
index 686722761d5..dcd32351a5b 100644
--- a/src/hash/crc32/crc32_ppc64le.go
+++ b/src/hash/crc32/crc32_ppc64le.go
@@ -19,6 +19,7 @@ const (
func ppc64SlicingUpdateBy8(crc uint32, table8 *slicing8Table, p []byte) uint32
// this function requires the buffer to be 16 byte aligned and > 16 bytes long
+//
//go:noescape
func vectorCrc32(crc uint32, poly uint32, p []byte) uint32
diff --git a/src/hash/crc32/crc32_s390x.go b/src/hash/crc32/crc32_s390x.go
index 3a98bd8799e..4e50b56c6f9 100644
--- a/src/hash/crc32/crc32_s390x.go
+++ b/src/hash/crc32/crc32_s390x.go
@@ -17,11 +17,13 @@ var hasVX = cpu.S390X.HasVX
// vectorizedCastagnoli implements CRC32 using vector instructions.
// It is defined in crc32_s390x.s.
+//
//go:noescape
func vectorizedCastagnoli(crc uint32, p []byte) uint32
// vectorizedIEEE implements CRC32 using vector instructions.
// It is defined in crc32_s390x.s.
+//
//go:noescape
func vectorizedIEEE(crc uint32, p []byte) uint32
diff --git a/src/hash/maphash/maphash.go b/src/hash/maphash/maphash.go
index d022d746a74..783690ea00f 100644
--- a/src/hash/maphash/maphash.go
+++ b/src/hash/maphash/maphash.go
@@ -10,7 +10,6 @@
//
// The hash functions are not cryptographically secure.
// (See crypto/sha256 and crypto/sha512 for cryptographic use.)
-//
package maphash
import (
@@ -33,6 +32,54 @@ type Seed struct {
s uint64
}
+// Bytes returns the hash of b with the given seed.
+//
+// Bytes is equivalent to, but more convenient and efficient than:
+//
+// var h Hash
+// h.SetSeed(seed)
+// h.Write(b)
+// return h.Sum64()
+func Bytes(seed Seed, b []byte) uint64 {
+ state := seed.s
+ if state == 0 {
+ panic("maphash: use of uninitialized Seed")
+ }
+ if len(b) == 0 {
+ return rthash(nil, 0, state) // avoid &b[0] index panic below
+ }
+ if len(b) > bufSize {
+ b = b[:len(b):len(b)] // merge len and cap calculations when reslicing
+ for len(b) > bufSize {
+ state = rthash(&b[0], bufSize, state)
+ b = b[bufSize:]
+ }
+ }
+ return rthash(&b[0], len(b), state)
+}
+
+// String returns the hash of s with the given seed.
+//
+// String is equivalent to, but more convenient and efficient than:
+//
+// var h Hash
+// h.SetSeed(seed)
+// h.WriteString(s)
+// return h.Sum64()
+func String(seed Seed, s string) uint64 {
+ state := seed.s
+ if state == 0 {
+ panic("maphash: use of uninitialized Seed")
+ }
+ for len(s) > bufSize {
+ p := (*byte)((*unsafeheader.String)(unsafe.Pointer(&s)).Data)
+ state = rthash(p, bufSize, state)
+ s = s[bufSize:]
+ }
+ p := (*byte)((*unsafeheader.String)(unsafe.Pointer(&s)).Data)
+ return rthash(p, len(s), state)
+}
+
// A Hash computes a seeded hash of a byte sequence.
//
// The zero Hash is a valid Hash ready to use.
@@ -44,9 +91,9 @@ type Seed struct {
// the sequence of bytes provided to the Hash object, not on the way
// in which the bytes are provided. For example, the three sequences
//
-// h.Write([]byte{'f','o','o'})
-// h.WriteByte('f'); h.WriteByte('o'); h.WriteByte('o')
-// h.WriteString("foo")
+// h.Write([]byte{'f','o','o'})
+// h.WriteByte('f'); h.WriteByte('o'); h.WriteByte('o')
+// h.WriteString("foo")
//
// all have the same effect.
//
diff --git a/src/hash/maphash/maphash_test.go b/src/hash/maphash/maphash_test.go
index 78cdfc0e737..75269890731 100644
--- a/src/hash/maphash/maphash_test.go
+++ b/src/hash/maphash/maphash_test.go
@@ -6,6 +6,7 @@ package maphash
import (
"bytes"
+ "fmt"
"hash"
"testing"
)
@@ -87,6 +88,14 @@ func TestHashGrouping(t *testing.T) {
t.Errorf("hash %d not identical to a single Write", i)
}
}
+
+ if sum1 := Bytes(hh[0].Seed(), b); sum1 != hh[0].Sum64() {
+ t.Errorf("hash using Bytes not identical to a single Write")
+ }
+
+ if sum1 := String(hh[0].Seed(), string(b)); sum1 != hh[0].Sum64() {
+ t.Errorf("hash using String not identical to a single Write")
+ }
}
func TestHashBytesVsString(t *testing.T) {
@@ -208,28 +217,39 @@ var _ hash.Hash64 = &Hash{}
func benchmarkSize(b *testing.B, size int) {
h := &Hash{}
buf := make([]byte, size)
- b.SetBytes(int64(size))
- b.ResetTimer()
-
- for i := 0; i < b.N; i++ {
- h.Reset()
- h.Write(buf)
- h.Sum64()
- }
-}
-
-func BenchmarkHash8Bytes(b *testing.B) {
- benchmarkSize(b, 8)
-}
+ s := string(buf)
+
+ b.Run("Write", func(b *testing.B) {
+ b.SetBytes(int64(size))
+ for i := 0; i < b.N; i++ {
+ h.Reset()
+ h.Write(buf)
+ h.Sum64()
+ }
+ })
-func BenchmarkHash320Bytes(b *testing.B) {
- benchmarkSize(b, 320)
-}
+ b.Run("Bytes", func(b *testing.B) {
+ b.SetBytes(int64(size))
+ seed := h.Seed()
+ for i := 0; i < b.N; i++ {
+ Bytes(seed, buf)
+ }
+ })
-func BenchmarkHash1K(b *testing.B) {
- benchmarkSize(b, 1024)
+ b.Run("String", func(b *testing.B) {
+ b.SetBytes(int64(size))
+ seed := h.Seed()
+ for i := 0; i < b.N; i++ {
+ String(seed, s)
+ }
+ })
}
-func BenchmarkHash8K(b *testing.B) {
- benchmarkSize(b, 8192)
+func BenchmarkHash(b *testing.B) {
+ sizes := []int{4, 8, 16, 32, 64, 256, 320, 1024, 4096, 16384}
+ for _, size := range sizes {
+ b.Run(fmt.Sprint("n=", size), func(b *testing.B) {
+ benchmarkSize(b, size)
+ })
+ }
}
diff --git a/src/html/template/context.go b/src/html/template/context.go
index aaa7d083593..a97c8be56f9 100644
--- a/src/html/template/context.go
+++ b/src/html/template/context.go
@@ -79,7 +79,9 @@ func (c context) mangle(templateName string) string {
// HTML5 parsing algorithm because a single token production in the HTML
// grammar may contain embedded actions in a template. For instance, the quoted
// HTML attribute produced by
-// <div title="Hello {{.World}}">
+//
+// <div title="Hello {{.World}}">
+//
// is a single token in HTML's grammar but in a template spans several nodes.
type state uint8
diff --git a/src/html/template/doc.go b/src/html/template/doc.go
index 650e7147a36..5d1631b2663 100644
--- a/src/html/template/doc.go
+++ b/src/html/template/doc.go
@@ -12,14 +12,14 @@ The documentation here focuses on the security features of the package.
For information about how to program the templates themselves, see the
documentation for text/template.
-Introduction
+# Introduction
This package wraps package text/template so you can share its template API
to parse and execute HTML templates safely.
- tmpl, err := template.New("name").Parse(...)
- // Error checking elided
- err = tmpl.Execute(out, data)
+ tmpl, err := template.New("name").Parse(...)
+ // Error checking elided
+ err = tmpl.Execute(out, data)
If successful, tmpl will now be injection-safe. Otherwise, err is an error
defined in the docs for ErrorCode.
@@ -34,38 +34,37 @@ provided below.
Example
- import "text/template"
- ...
- t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`)
- err = t.ExecuteTemplate(out, "T", "<script>alert('you have been pwned')</script>")
+ import "text/template"
+ ...
+ t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`)
+ err = t.ExecuteTemplate(out, "T", "<script>alert('you have been pwned')</script>")
produces
- Hello, <script>alert('you have been pwned')</script>!
+ Hello, <script>alert('you have been pwned')</script>!
but the contextual autoescaping in html/template
- import "html/template"
- ...
- t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`)
- err = t.ExecuteTemplate(out, "T", "<script>alert('you have been pwned')</script>")
+ import "html/template"
+ ...
+ t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`)
+ err = t.ExecuteTemplate(out, "T", "<script>alert('you have been pwned')</script>")
produces safe, escaped HTML output
- Hello, &lt;script&gt;alert(&#39;you have been pwned&#39;)&lt;/script&gt;!
+ Hello, &lt;script&gt;alert(&#39;you have been pwned&#39;)&lt;/script&gt;!
-
-Contexts
+# Contexts
This package understands HTML, CSS, JavaScript, and URIs. It adds sanitizing
functions to each simple action pipeline, so given the excerpt
- <a href="/search?q={{.}}">{{.}}</a>
+ <a href="/search?q={{.}}">{{.}}</a>
At parse time each {{.}} is overwritten to add escaping functions as necessary.
In this case it becomes
- <a href="/search?q={{. | urlescaper | attrescaper}}">{{. | htmlescaper}}</a>
+ <a href="/search?q={{. | urlescaper | attrescaper}}">{{. | htmlescaper}}</a>
where urlescaper, attrescaper, and htmlescaper are aliases for internal escaping
functions.
@@ -73,117 +72,113 @@ functions.
For these internal escaping functions, if an action pipeline evaluates to
a nil interface value, it is treated as though it were an empty string.
-Namespaced and data- attributes
+# Namespaced and data- attributes
Attributes with a namespace are treated as if they had no namespace.
Given the excerpt
- <a my:href="{{.}}"></a>
+ <a my:href="{{.}}"></a>
At parse time the attribute will be treated as if it were just "href".
So at parse time the template becomes:
- <a my:href="{{. | urlescaper | attrescaper}}"></a>
+ <a my:href="{{. | urlescaper | attrescaper}}"></a>
Similarly to attributes with namespaces, attributes with a "data-" prefix are
treated as if they had no "data-" prefix. So given
- <a data-href="{{.}}"></a>
+ <a data-href="{{.}}"></a>
At parse time this becomes
- <a data-href="{{. | urlescaper | attrescaper}}"></a>
+ <a data-href="{{. | urlescaper | attrescaper}}"></a>
If an attribute has both a namespace and a "data-" prefix, only the namespace
will be removed when determining the context. For example
- <a my:data-href="{{.}}"></a>
+ <a my:data-href="{{.}}"></a>
This is handled as if "my:data-href" was just "data-href" and not "href" as
it would be if the "data-" prefix were to be ignored too. Thus at parse
time this becomes just
- <a my:data-href="{{. | attrescaper}}"></a>
+ <a my:data-href="{{. | attrescaper}}"></a>
As a special case, attributes with the namespace "xmlns" are always treated
as containing URLs. Given the excerpts
- <a xmlns:title="{{.}}"></a>
- <a xmlns:href="{{.}}"></a>
- <a xmlns:onclick="{{.}}"></a>
+ <a xmlns:title="{{.}}"></a>
+ <a xmlns:href="{{.}}"></a>
+ <a xmlns:onclick="{{.}}"></a>
At parse time they become:
- <a xmlns:title="{{. | urlescaper | attrescaper}}"></a>
- <a xmlns:href="{{. | urlescaper | attrescaper}}"></a>
- <a xmlns:onclick="{{. | urlescaper | attrescaper}}"></a>
+ <a xmlns:title="{{. | urlescaper | attrescaper}}"></a>
+ <a xmlns:href="{{. | urlescaper | attrescaper}}"></a>
+ <a xmlns:onclick="{{. | urlescaper | attrescaper}}"></a>
-Errors
+# Errors
See the documentation of ErrorCode for details.
-
-A fuller picture
+# A fuller picture
The rest of this package comment may be skipped on first reading; it includes
details necessary to understand escaping contexts and error messages. Most users
will not need to understand these details.
-
-Contexts
+# Contexts
Assuming {{.}} is `O'Reilly: How are <i>you</i>?`, the table below shows
how {{.}} appears when used in the context to the left.
- Context {{.}} After
- {{.}} O'Reilly: How are &lt;i&gt;you&lt;/i&gt;?
- <a title='{{.}}'> O&#39;Reilly: How are you?
- <a href="/{{.}}"> O&#39;Reilly: How are %3ci%3eyou%3c/i%3e?
- <a href="?q={{.}}"> O&#39;Reilly%3a%20How%20are%3ci%3e...%3f
- <a onx='f("{{.}}")'> O\x27Reilly: How are \x3ci\x3eyou...?
- <a onx='f({{.}})'> "O\x27Reilly: How are \x3ci\x3eyou...?"
- <a onx='pattern = /{{.}}/;'> O\x27Reilly: How are \x3ci\x3eyou...\x3f
+ Context {{.}} After
+ {{.}} O'Reilly: How are &lt;i&gt;you&lt;/i&gt;?
+ <a title='{{.}}'> O&#39;Reilly: How are you?
+ <a href="/{{.}}"> O&#39;Reilly: How are %3ci%3eyou%3c/i%3e?
+ <a href="?q={{.}}"> O&#39;Reilly%3a%20How%20are%3ci%3e...%3f
+ <a onx='f("{{.}}")'> O\x27Reilly: How are \x3ci\x3eyou...?
+ <a onx='f({{.}})'> "O\x27Reilly: How are \x3ci\x3eyou...?"
+ <a onx='pattern = /{{.}}/;'> O\x27Reilly: How are \x3ci\x3eyou...\x3f
If used in an unsafe context, then the value might be filtered out:
- Context {{.}} After
- <a href="{{.}}"> #ZgotmplZ
+ Context {{.}} After
+ <a href="{{.}}"> #ZgotmplZ
since "O'Reilly:" is not an allowed protocol like "http:".
-
If {{.}} is the innocuous word, `left`, then it can appear more widely,
- Context {{.}} After
- {{.}} left
- <a title='{{.}}'> left
- <a href='{{.}}'> left
- <a href='/{{.}}'> left
- <a href='?dir={{.}}'> left
- <a style="border-{{.}}: 4px"> left
- <a style="align: {{.}}"> left
- <a style="background: '{{.}}'> left
- <a style="background: url('{{.}}')> left
- <style>p.{{.}} {color:red}</style> left
+ Context {{.}} After
+ {{.}} left
+ <a title='{{.}}'> left
+ <a href='{{.}}'> left
+ <a href='/{{.}}'> left
+ <a href='?dir={{.}}'> left
+ <a style="border-{{.}}: 4px"> left
+ <a style="align: {{.}}"> left
+ <a style="background: '{{.}}'> left
+ <a style="background: url('{{.}}')> left
+ <style>p.{{.}} {color:red}</style> left
Non-string values can be used in JavaScript contexts.
If {{.}} is
- struct{A,B string}{ "foo", "bar" }
+ struct{A,B string}{ "foo", "bar" }
in the escaped template
- <script>var pair = {{.}};</script>
+ <script>var pair = {{.}};</script>
then the template output is
- <script>var pair = {"A": "foo", "B": "bar"};</script>
+ <script>var pair = {"A": "foo", "B": "bar"};</script>
See package json to understand how non-string content is marshaled for
embedding in JavaScript contexts.
-
-Typed Strings
+# Typed Strings
By default, this package assumes that all pipelines produce a plain text string.
It adds escaping pipeline stages necessary to correctly and safely embed that
@@ -197,24 +192,23 @@ exempted from escaping.
The template
- Hello, {{.}}!
+ Hello, {{.}}!
can be invoked with
- tmpl.Execute(out, template.HTML(`<b>World</b>`))
+ tmpl.Execute(out, template.HTML(`<b>World</b>`))
to produce
- Hello, <b>World</b>!
+ Hello, <b>World</b>!
instead of the
- Hello, &lt;b&gt;World&lt;b&gt;!
+ Hello, &lt;b&gt;World&lt;b&gt;!
that would have been produced if {{.}} was a regular string.
-
-Security Model
+# Security Model
https://rawgit.com/mikesamuel/sanitized-jquery-templates/trunk/safetemplate.html#problem_definition defines "safe" as used by this package.
diff --git a/src/html/template/error.go b/src/html/template/error.go
index 6bb5a2027f6..5c51f772cbd 100644
--- a/src/html/template/error.go
+++ b/src/html/template/error.go
@@ -32,14 +32,17 @@ type ErrorCode int
//
// Output: "ZgotmplZ"
// Example:
-// <img src="{{.X}}">
-// where {{.X}} evaluates to `javascript:...`
+//
+// <img src="{{.X}}">
+// where {{.X}} evaluates to `javascript:...`
+//
// Discussion:
-// "ZgotmplZ" is a special value that indicates that unsafe content reached a
-// CSS or URL context at runtime. The output of the example will be
-// <img src="#ZgotmplZ">
-// If the data comes from a trusted source, use content types to exempt it
-// from filtering: URL(`javascript:...`).
+//
+// "ZgotmplZ" is a special value that indicates that unsafe content reached a
+// CSS or URL context at runtime. The output of the example will be
+// <img src="#ZgotmplZ">
+// If the data comes from a trusted source, use content types to exempt it
+// from filtering: URL(`javascript:...`).
const (
// OK indicates the lack of an error.
OK ErrorCode = iota
diff --git a/src/html/template/escape.go b/src/html/template/escape.go
index 2b4027348ae..54fbcdca333 100644
--- a/src/html/template/escape.go
+++ b/src/html/template/escape.go
@@ -411,13 +411,19 @@ func newIdentCmd(identifier string, pos parse.Pos) *parse.CommandNode {
// nudge returns the context that would result from following empty string
// transitions from the input context.
// For example, parsing:
-// `<a href=`
+//
+// `<a href=`
+//
// will end in context{stateBeforeValue, attrURL}, but parsing one extra rune:
-// `<a href=x`
+//
+// `<a href=x`
+//
// will end in context{stateURL, delimSpaceOrTagEnd, ...}.
// There are two transitions that happen when the 'x' is seen:
// (1) Transition from a before-value state to a start-of-value state without
-// consuming any character.
+//
+// consuming any character.
+//
// (2) Consume 'x' and transition past the first value character.
// In this case, nudging produces the context after (1) happens.
func nudge(c context) context {
diff --git a/src/html/template/html.go b/src/html/template/html.go
index 19bd0ccb204..46e9d931511 100644
--- a/src/html/template/html.go
+++ b/src/html/template/html.go
@@ -84,10 +84,12 @@ var htmlNormReplacementTable = []string{
// <script>(function () {
// var a = [], d = document.getElementById("d"), i, c, s;
// for (i = 0; i < 0x10000; ++i) {
-// c = String.fromCharCode(i);
-// d.innerHTML = "<span title=" + c + "lt" + c + "></span>"
-// s = d.getElementsByTagName("SPAN")[0];
-// if (!s || s.title !== c + "lt" + c) { a.push(i.toString(16)); }
+//
+// c = String.fromCharCode(i);
+// d.innerHTML = "<span title=" + c + "lt" + c + "></span>"
+// s = d.getElementsByTagName("SPAN")[0];
+// if (!s || s.title !== c + "lt" + c) { a.push(i.toString(16)); }
+//
// }
// document.write(a.join(", "));
// })()</script>
diff --git a/src/html/template/template.go b/src/html/template/template.go
index a99f69231cb..30b64dff040 100644
--- a/src/html/template/template.go
+++ b/src/html/template/template.go
@@ -64,6 +64,7 @@ func (t *Template) Templates() []*Template {
//
// missingkey: Control the behavior during execution if a map is
// indexed with a key that is not present in the map.
+//
// "missingkey=default" or "missingkey=invalid"
// The default behavior: Do nothing and continue execution.
// If printed, the result of the index operation is the string
@@ -360,6 +361,7 @@ func (t *Template) Lookup(name string) *Template {
// Must is a helper that wraps a call to a function returning (*Template, error)
// and panics if the error is non-nil. It is intended for use in variable initializations
// such as
+//
// var t = template.Must(template.New("name").Parse("html"))
func Must(t *Template, err error) *Template {
if err != nil {
diff --git a/src/html/template/url.go b/src/html/template/url.go
index 93905586a2f..9d0be390225 100644
--- a/src/html/template/url.go
+++ b/src/html/template/url.go
@@ -19,15 +19,15 @@ import (
//
// This filter conservatively assumes that all schemes other than the following
// are unsafe:
-// * http: Navigates to a new website, and may open a new window or tab.
-// These side effects can be reversed by navigating back to the
-// previous website, or closing the window or tab. No irreversible
-// changes will take place without further user interaction with
-// the new website.
-// * https: Same as http.
-// * mailto: Opens an email program and starts a new draft. This side effect
-// is not irreversible until the user explicitly clicks send; it
-// can be undone by closing the email program.
+// - http: Navigates to a new website, and may open a new window or tab.
+// These side effects can be reversed by navigating back to the
+// previous website, or closing the window or tab. No irreversible
+// changes will take place without further user interaction with
+// the new website.
+// - https: Same as http.
+// - mailto: Opens an email program and starts a new draft. This side effect
+// is not irreversible until the user explicitly clicks send; it
+// can be undone by closing the email program.
//
// To allow URLs containing other schemes to bypass this filter, developers must
// explicitly indicate that such a URL is expected and safe by encapsulating it
diff --git a/src/image/draw/draw.go b/src/image/draw/draw.go
index 7dd18dfdb59..920ebb905eb 100644
--- a/src/image/draw/draw.go
+++ b/src/image/draw/draw.go
@@ -121,6 +121,11 @@ func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mas
// Fast paths for special cases. If none of them apply, then we fall back
// to general but slower implementations.
+ //
+ // For NRGBA and NRGBA64 image types, the code paths aren't just faster.
+ // They also avoid the information loss that would otherwise occur from
+ // converting non-alpha-premultiplied color to and from alpha-premultiplied
+ // color. See TestDrawSrcNonpremultiplied.
switch dst0 := dst.(type) {
case *image.RGBA:
if op == Over {
@@ -181,7 +186,10 @@ func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mas
drawFillSrc(dst0, r, sr, sg, sb, sa)
return
case *image.RGBA:
- drawCopySrc(dst0, r, src0, sp)
+ d0 := dst0.PixOffset(r.Min.X, r.Min.Y)
+ s0 := src0.PixOffset(sp.X, sp.Y)
+ drawCopySrc(
+ dst0.Pix[d0:], dst0.Stride, r, src0.Pix[s0:], src0.Stride, sp, 4*r.Dx())
return
case *image.NRGBA:
drawNRGBASrc(dst0, r, src0, sp)
@@ -222,6 +230,26 @@ func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mas
return
}
}
+ case *image.NRGBA:
+ if op == Src && mask == nil {
+ if src0, ok := src.(*image.NRGBA); ok {
+ d0 := dst0.PixOffset(r.Min.X, r.Min.Y)
+ s0 := src0.PixOffset(sp.X, sp.Y)
+ drawCopySrc(
+ dst0.Pix[d0:], dst0.Stride, r, src0.Pix[s0:], src0.Stride, sp, 4*r.Dx())
+ return
+ }
+ }
+ case *image.NRGBA64:
+ if op == Src && mask == nil {
+ if src0, ok := src.(*image.NRGBA64); ok {
+ d0 := dst0.PixOffset(r.Min.X, r.Min.Y)
+ s0 := src0.PixOffset(sp.X, sp.Y)
+ drawCopySrc(
+ dst0.Pix[d0:], dst0.Stride, r, src0.Pix[s0:], src0.Stride, sp, 8*r.Dx())
+ return
+ }
+ }
}
x0, x1, dx := r.Min.X, r.Max.X, 1
@@ -449,27 +477,28 @@ func drawCopyOver(dst *image.RGBA, r image.Rectangle, src *image.RGBA, sp image.
}
}
-func drawCopySrc(dst *image.RGBA, r image.Rectangle, src *image.RGBA, sp image.Point) {
- n, dy := 4*r.Dx(), r.Dy()
- d0 := dst.PixOffset(r.Min.X, r.Min.Y)
- s0 := src.PixOffset(sp.X, sp.Y)
- var ddelta, sdelta int
- if r.Min.Y <= sp.Y {
- ddelta = dst.Stride
- sdelta = src.Stride
- } else {
+// drawCopySrc copies bytes to dstPix from srcPix. These arguments roughly
+// correspond to the Pix fields of the image package's concrete image.Image
+// implementations, but are offset (dstPix is dst.Pix[dpOffset:] not dst.Pix).
+func drawCopySrc(
+ dstPix []byte, dstStride int, r image.Rectangle,
+ srcPix []byte, srcStride int, sp image.Point,
+ bytesPerRow int) {
+
+ d0, s0, ddelta, sdelta, dy := 0, 0, dstStride, srcStride, r.Dy()
+ if r.Min.Y > sp.Y {
// If the source start point is higher than the destination start
// point, then we compose the rows in bottom-up order instead of
// top-down. Unlike the drawCopyOver function, we don't have to check
// the x coordinates because the built-in copy function can handle
// overlapping slices.
- d0 += (dy - 1) * dst.Stride
- s0 += (dy - 1) * src.Stride
- ddelta = -dst.Stride
- sdelta = -src.Stride
+ d0 = (dy - 1) * dstStride
+ s0 = (dy - 1) * srcStride
+ ddelta = -dstStride
+ sdelta = -srcStride
}
for ; dy > 0; dy-- {
- copy(dst.Pix[d0:d0+n], src.Pix[s0:s0+n])
+ copy(dstPix[d0:d0+bytesPerRow], srcPix[s0:s0+bytesPerRow])
d0 += ddelta
s0 += sdelta
}
diff --git a/src/image/draw/draw_test.go b/src/image/draw/draw_test.go
index 3be93962ad7..a34d1c3e6e8 100644
--- a/src/image/draw/draw_test.go
+++ b/src/image/draw/draw_test.go
@@ -622,6 +622,70 @@ func TestFill(t *testing.T) {
}
}
+func TestDrawSrcNonpremultiplied(t *testing.T) {
+ var (
+ opaqueGray = color.NRGBA{0x99, 0x99, 0x99, 0xff}
+ transparentBlue = color.NRGBA{0x00, 0x00, 0xff, 0x00}
+ transparentGreen = color.NRGBA{0x00, 0xff, 0x00, 0x00}
+ transparentRed = color.NRGBA{0xff, 0x00, 0x00, 0x00}
+
+ opaqueGray64 = color.NRGBA64{0x9999, 0x9999, 0x9999, 0xffff}
+ transparentPurple64 = color.NRGBA64{0xfedc, 0x0000, 0x7654, 0x0000}
+ )
+
+ // dst and src are 1x3 images but the dr rectangle (and hence the overlap)
+ // is only 1x2. The Draw call should affect dst's pixels at (1, 10) and (2,
+ // 10) but the pixel at (0, 10) should be untouched.
+ //
+ // The src image is entirely transparent (and the Draw operator is Src) so
+ // the two touched pixels should be set to transparent colors.
+ //
+ // In general, Go's color.Color type (and specifically the Color.RGBA
+ // method) works in premultiplied alpha, where there's no difference
+ // between "transparent blue" and "transparent red". It's all "just
+ // transparent" and canonically "transparent black" (all zeroes).
+ //
+ // However, since the operator is Src (so the pixels are 'copied', not
+ // 'blended') and both dst and src images are *image.NRGBA (N stands for
+ // Non-premultiplied alpha which *does* distinguish "transparent blue" and
+ // "transparent red"), we prefer that this distinction carries through and
+ // dst's touched pixels should be transparent blue and transparent green,
+ // not just transparent black.
+ {
+ dst := image.NewNRGBA(image.Rect(0, 10, 3, 11))
+ dst.SetNRGBA(0, 10, opaqueGray)
+ src := image.NewNRGBA(image.Rect(1, 20, 4, 21))
+ src.SetNRGBA(1, 20, transparentBlue)
+ src.SetNRGBA(2, 20, transparentGreen)
+ src.SetNRGBA(3, 20, transparentRed)
+
+ dr := image.Rect(1, 10, 3, 11)
+ Draw(dst, dr, src, image.Point{1, 20}, Src)
+
+ if got, want := dst.At(0, 10), opaqueGray; got != want {
+ t.Errorf("At(0, 10):\ngot %#v\nwant %#v", got, want)
+ }
+ if got, want := dst.At(1, 10), transparentBlue; got != want {
+ t.Errorf("At(1, 10):\ngot %#v\nwant %#v", got, want)
+ }
+ if got, want := dst.At(2, 10), transparentGreen; got != want {
+ t.Errorf("At(2, 10):\ngot %#v\nwant %#v", got, want)
+ }
+ }
+
+ // Check image.NRGBA64 (not image.NRGBA) similarly.
+ {
+ dst := image.NewNRGBA64(image.Rect(0, 0, 1, 1))
+ dst.SetNRGBA64(0, 0, opaqueGray64)
+ src := image.NewNRGBA64(image.Rect(0, 0, 1, 1))
+ src.SetNRGBA64(0, 0, transparentPurple64)
+ Draw(dst, dst.Bounds(), src, image.Point{0, 0}, Src)
+ if got, want := dst.At(0, 0), transparentPurple64; got != want {
+ t.Errorf("At(0, 0):\ngot %#v\nwant %#v", got, want)
+ }
+ }
+}
+
// TestFloydSteinbergCheckerboard tests that the result of Floyd-Steinberg
// error diffusion of a uniform 50% gray source image with a black-and-white
// palette is a checkerboard pattern.
diff --git a/src/image/image.go b/src/image/image.go
index 930d9ac6c7b..dfb70d4eaf6 100644
--- a/src/image/image.go
+++ b/src/image/image.go
@@ -13,7 +13,9 @@
// image format requires the prior registration of a decoder function.
// Registration is typically automatic as a side effect of initializing that
// format's package so that, to decode a PNG image, it suffices to have
+//
// import _ "image/png"
+//
// in a program's main package. The _ means to import a package purely for its
// initialization side effects.
//
diff --git a/src/image/jpeg/writer.go b/src/image/jpeg/writer.go
index a6004990047..0027f78294a 100644
--- a/src/image/jpeg/writer.go
+++ b/src/image/jpeg/writer.go
@@ -481,25 +481,25 @@ func scale(dst *block, src *[4]block) {
}
// sosHeaderY is the SOS marker "\xff\xda" followed by 8 bytes:
-// - the marker length "\x00\x08",
-// - the number of components "\x01",
-// - component 1 uses DC table 0 and AC table 0 "\x01\x00",
-// - the bytes "\x00\x3f\x00". Section B.2.3 of the spec says that for
-// sequential DCTs, those bytes (8-bit Ss, 8-bit Se, 4-bit Ah, 4-bit Al)
-// should be 0x00, 0x3f, 0x00<<4 | 0x00.
+// - the marker length "\x00\x08",
+// - the number of components "\x01",
+// - component 1 uses DC table 0 and AC table 0 "\x01\x00",
+// - the bytes "\x00\x3f\x00". Section B.2.3 of the spec says that for
+// sequential DCTs, those bytes (8-bit Ss, 8-bit Se, 4-bit Ah, 4-bit Al)
+// should be 0x00, 0x3f, 0x00<<4 | 0x00.
var sosHeaderY = []byte{
0xff, 0xda, 0x00, 0x08, 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00,
}
// sosHeaderYCbCr is the SOS marker "\xff\xda" followed by 12 bytes:
-// - the marker length "\x00\x0c",
-// - the number of components "\x03",
-// - component 1 uses DC table 0 and AC table 0 "\x01\x00",
-// - component 2 uses DC table 1 and AC table 1 "\x02\x11",
-// - component 3 uses DC table 1 and AC table 1 "\x03\x11",
-// - the bytes "\x00\x3f\x00". Section B.2.3 of the spec says that for
-// sequential DCTs, those bytes (8-bit Ss, 8-bit Se, 4-bit Ah, 4-bit Al)
-// should be 0x00, 0x3f, 0x00<<4 | 0x00.
+// - the marker length "\x00\x0c",
+// - the number of components "\x03",
+// - component 1 uses DC table 0 and AC table 0 "\x01\x00",
+// - component 2 uses DC table 1 and AC table 1 "\x02\x11",
+// - component 3 uses DC table 1 and AC table 1 "\x03\x11",
+// - the bytes "\x00\x3f\x00". Section B.2.3 of the spec says that for
+// sequential DCTs, those bytes (8-bit Ss, 8-bit Se, 4-bit Ah, 4-bit Al)
+// should be 0x00, 0x3f, 0x00<<4 | 0x00.
var sosHeaderYCbCr = []byte{
0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02,
0x11, 0x03, 0x11, 0x00, 0x3f, 0x00,
diff --git a/src/image/png/reader.go b/src/image/png/reader.go
index 4c65038cb5b..95b507cf68c 100644
--- a/src/image/png/reader.go
+++ b/src/image/png/reader.go
@@ -325,7 +325,9 @@ func (d *decoder) parsetRNS(length uint32) error {
// Read presents one or more IDAT chunks as one continuous stream (minus the
// intermediate chunk headers and footers). If the PNG data looked like:
-// ... len0 IDAT xxx crc0 len1 IDAT yy crc1 len2 IEND crc2
+//
+// ... len0 IDAT xxx crc0 len1 IDAT yy crc1 len2 IEND crc2
+//
// then this reader presents xxxyy. For well-formed PNG data, the decoder state
// immediately before the first Read call is that d.r is positioned between the
// first IDAT and xxx, and the decoder state immediately after the last Read
diff --git a/src/image/ycbcr.go b/src/image/ycbcr.go
index 328b90d1525..78f5ebe1d81 100644
--- a/src/image/ycbcr.go
+++ b/src/image/ycbcr.go
@@ -45,6 +45,7 @@ func (s YCbCrSubsampleRatio) String() string {
// that map to separate chroma samples.
// It is not an absolute requirement, but YStride and len(Y) are typically
// multiples of 8, and:
+//
// For 4:4:4, CStride == YStride/1 && len(Cb) == len(Cr) == len(Y)/1.
// For 4:2:2, CStride == YStride/2 && len(Cb) == len(Cr) == len(Y)/2.
// For 4:2:0, CStride == YStride/2 && len(Cb) == len(Cr) == len(Y)/4.
diff --git a/src/index/suffixarray/suffixarray.go b/src/index/suffixarray/suffixarray.go
index cdc6e81a800..7fca0fd7ba9 100644
--- a/src/index/suffixarray/suffixarray.go
+++ b/src/index/suffixarray/suffixarray.go
@@ -13,7 +13,6 @@
// // lookup byte slice s
// offsets1 := index.Lookup(s, -1) // the list of all indices where s occurs in data
// offsets2 := index.Lookup(s, 3) // the list of at most 3 indices where s occurs in data
-//
package suffixarray
import (
diff --git a/src/internal/bytealg/indexbyte_ppc64x.s b/src/internal/bytealg/indexbyte_ppc64x.s
index 4cc2b440876..1a6e852d672 100644
--- a/src/internal/bytealg/indexbyte_ppc64x.s
+++ b/src/internal/bytealg/indexbyte_ppc64x.s
@@ -11,17 +11,20 @@ TEXT ·IndexByte<ABIInternal>(SB),NOSPLIT|NOFRAME,$0-40
// R3 = byte array pointer
// R4 = length
MOVD R6, R5 // R5 = byte
+ MOVBZ internal∕cpu·PPC64+const_offsetPPC64HasPOWER9(SB), R16
BR indexbytebody<>(SB)
TEXT ·IndexByteString<ABIInternal>(SB),NOSPLIT|NOFRAME,$0-32
// R3 = string
// R4 = length
// R5 = byte
+ MOVBZ internal∕cpu·PPC64+const_offsetPPC64HasPOWER9(SB), R16
BR indexbytebody<>(SB)
// R3 = addr of string
// R4 = len of string
// R5 = byte to find
+// R16 = 1 if running on a POWER9 system, 0 otherwise
// On exit:
// R3 = return value
TEXT indexbytebody<>(SB),NOSPLIT|NOFRAME,$0-0
@@ -29,12 +32,11 @@ TEXT indexbytebody<>(SB),NOSPLIT|NOFRAME,$0-0
RLDICR $0,R3,$60,R8 // Align address to doubleword boundary in R8.
RLDIMI $8,R5,$48,R5 // Replicating the byte across the register.
ADD R4,R3,R7 // Last acceptable address in R7.
- DCBT (R8) // Prepare cache line.
RLDIMI $16,R5,$32,R5
CMPU R4,$32 // Check if it's a small string (≤32 bytes). Those will be processed differently.
MOVD $-1,R9
- WORD $0x54661EB8 // Calculate padding in R6 (rlwinm r6,r3,3,26,28).
+ RLWNM $3,R3,$26,$28,R6 // shift amount for mask (r3&0x7)*8
RLDIMI $32,R5,$0,R5
MOVD R7,R10 // Save last acceptable address in R10 for later.
ADD $-1,R7,R7
@@ -43,8 +45,77 @@ TEXT indexbytebody<>(SB),NOSPLIT|NOFRAME,$0-0
#else
SRD R6,R9,R9 // Same for Big Endian
#endif
- BLE small_string // Jump to the small string case if it's ≤32 bytes.
-
+ BLT small_string // Jump to the small string case if it's <32 bytes.
+ CMP R16,$1 // optimize for power8 v power9
+ BNE power8
+ VSPLTISB $3,V10 // Use V10 as control for VBPERMQ
+ MTVRD R5,V1
+ LVSL (R0+R0),V11 // set up the permute vector such that V10 has {0x78, .., 0x8, 0x0}
+ VSLB V11,V10,V10 // to extract the first bit of match result into GPR
+ VSPLTB $7,V1,V1 // Replicate byte across V1
+ CMP R4,$64
+ MOVD $16,R11
+ MOVD R3,R8
+ BLT cmp32
+ MOVD $32,R12
+ MOVD $48,R6
+
+loop64:
+ LXVB16X (R0)(R8),V2 // scan 64 bytes at a time
+ VCMPEQUBCC V2,V1,V6
+ BNE CR6,foundat0 // match found at R8, jump out
+
+ LXVB16X (R8)(R11),V2
+ VCMPEQUBCC V2,V1,V6
+ BNE CR6,foundat1 // match found at R8+16 bytes, jump out
+
+ LXVB16X (R8)(R12),V2
+ VCMPEQUBCC V2,V1,V6
+ BNE CR6,foundat2 // match found at R8+32 bytes, jump out
+
+ LXVB16X (R8)(R6),V2
+ VCMPEQUBCC V2,V1,V6
+ BNE CR6,foundat3 // match found at R8+48 bytes, jump out
+ ADD $64,R8
+ ADD $-64,R4
+ CMP R4,$64 // >=64 bytes left to scan?
+ BGE loop64
+ CMP R4,$32
+ BLT rem // jump to rem if there are < 32 bytes left
+cmp32:
+ LXVB16X (R0)(R8),V2 // 32-63 bytes left
+ VCMPEQUBCC V2,V1,V6
+ BNE CR6,foundat0 // match found at R8
+
+ LXVB16X (R11)(R8),V2
+ VCMPEQUBCC V2,V1,V6
+ BNE CR6,foundat1 // match found at R8+16
+
+ ADD $32,R8
+ ADD $-32,R4
+rem:
+ RLDICR $0,R8,$60,R8 // align address to reuse code for tail end processing
+ BR small_string
+
+foundat3:
+ ADD $16,R8
+foundat2:
+ ADD $16,R8
+foundat1:
+ ADD $16,R8
+foundat0:
+ // Compress the result into a single doubleword and
+ // move it to a GPR for the final calculation.
+ VBPERMQ V6,V10,V6
+ MFVRD V6,R3
+ // count leading zeroes upto the match that ends up in low 16 bits
+ // in both endian modes, compute index by subtracting the number by 16
+ CNTLZW R3,R11
+ ADD $-16,R11
+ ADD R8,R11,R3 // Calculate byte address
+ SUB R17,R3
+ RET
+power8:
// If we are 64-byte aligned, branch to qw_align just to get the auxiliary values
// in V0, V1 and V10, then branch to the preloop.
ANDCC $63,R3,R11
@@ -54,7 +125,6 @@ TEXT indexbytebody<>(SB),NOSPLIT|NOFRAME,$0-0
MOVD 0(R8),R12 // Load one doubleword from the aligned address in R8.
CMPB R12,R5,R3 // Check for a match.
AND R9,R3,R3 // Mask bytes below s_base
- RLDICL $0,R7,$61,R6 // length-1
RLDICR $0,R7,$60,R7 // Last doubleword in R7
CMPU R3,$0,CR7 // If we have a match, jump to the final computation
BNE CR7,done
@@ -252,8 +322,13 @@ found_qw_align:
CMPU R11,R4
BLT return
BR notfound
+ PCALIGN $16
done:
+ ADD $-1,R10,R6
+ // Offset of last index for the final
+ // doubleword comparison
+ RLDICL $0,R6,$61,R6
// At this point, R3 has 0xFF in the same position as the byte we are
// looking for in the doubleword. Use that to calculate the exact index
// of the byte.
@@ -273,6 +348,7 @@ done:
BR notfound
small_string:
+ // process string of length < 32 bytes
// We unroll this loop for better performance.
CMPU R4,$0 // Check for length=0
BEQ notfound
@@ -281,7 +357,6 @@ small_string:
CMPB R12,R5,R3 // Check for a match.
AND R9,R3,R3 // Mask bytes below s_base.
CMPU R3,$0,CR7 // If we have a match, jump to the final computation.
- RLDICL $0,R7,$61,R6 // length-1
RLDICR $0,R7,$60,R7 // Last doubleword in R7.
CMPU R8,R7
BNE CR7,done
diff --git a/src/internal/fmtsort/sort.go b/src/internal/fmtsort/sort.go
index 09e987d0e6f..278a89bd75d 100644
--- a/src/internal/fmtsort/sort.go
+++ b/src/internal/fmtsort/sort.go
@@ -36,18 +36,18 @@ func (o *SortedMap) Swap(i, j int) {
//
// The ordering rules are more general than with Go's < operator:
//
-// - when applicable, nil compares low
-// - ints, floats, and strings order by <
-// - NaN compares less than non-NaN floats
-// - bool compares false before true
-// - complex compares real, then imag
-// - pointers compare by machine address
-// - channel values compare by machine address
-// - structs compare each field in turn
-// - arrays compare each element in turn.
-// Otherwise identical arrays compare by length.
-// - interface values compare first by reflect.Type describing the concrete type
-// and then by concrete value as described in the previous rules.
+// - when applicable, nil compares low
+// - ints, floats, and strings order by <
+// - NaN compares less than non-NaN floats
+// - bool compares false before true
+// - complex compares real, then imag
+// - pointers compare by machine address
+// - channel values compare by machine address
+// - structs compare each field in turn
+// - arrays compare each element in turn.
+// Otherwise identical arrays compare by length.
+// - interface values compare first by reflect.Type describing the concrete type
+// and then by concrete value as described in the previous rules.
func Sort(mapValue reflect.Value) *SortedMap {
if mapValue.Type().Kind() != reflect.Map {
return nil
diff --git a/src/internal/goarch/goarch.go b/src/internal/goarch/goarch.go
index 921f5a208fc..e8de67b01b4 100644
--- a/src/internal/goarch/goarch.go
+++ b/src/internal/goarch/goarch.go
@@ -9,6 +9,7 @@ package goarch
// per-arch information, including constants named $GOARCH for every
// GOARCH. The constant is 1 on the current system, 0 otherwise; multiplying
// by them is useful for defining GOARCH-specific constants.
+//
//go:generate go run gengoarch.go
type ArchFamilyType int
diff --git a/src/internal/goos/goos.go b/src/internal/goos/goos.go
index ebb521fec68..02dc9688cb2 100644
--- a/src/internal/goos/goos.go
+++ b/src/internal/goos/goos.go
@@ -9,4 +9,5 @@ package goos
// per-OS information, including constants named Is$GOOS for every
// known GOOS. The constant is 1 on the current system, 0 otherwise;
// multiplying by them is useful for defining GOOS-specific constants.
+//
//go:generate go run gengoos.go
diff --git a/src/internal/intern/intern.go b/src/internal/intern/intern.go
index 75641106abd..c7639b46687 100644
--- a/src/internal/intern/intern.go
+++ b/src/internal/intern/intern.go
@@ -93,6 +93,7 @@ func GetByString(s string) *Value {
// We play unsafe games that violate Go's rules (and assume a non-moving
// collector). So we quiet Go here.
// See the comment below Get for more implementation details.
+//
//go:nocheckptr
func get(k key) *Value {
mu.Lock()
diff --git a/src/internal/nettrace/nettrace.go b/src/internal/nettrace/nettrace.go
index 6e0dbe73bbf..0a2bf925e9a 100644
--- a/src/internal/nettrace/nettrace.go
+++ b/src/internal/nettrace/nettrace.go
@@ -16,7 +16,8 @@ type TraceKey struct{}
// specify an alternate resolver func.
// It is not exposed to outsider users. (But see issue 12503)
// The value should be the same type as lookupIP:
-// func lookupIP(ctx context.Context, host string) ([]IPAddr, error)
+//
+// func lookupIP(ctx context.Context, host string) ([]IPAddr, error)
type LookupIPAltResolverKey struct{}
// Trace contains a set of hooks for tracing events within
diff --git a/src/internal/poll/fcntl_libc.go b/src/internal/poll/fcntl_libc.go
index f503d7a336f..13614dc3e8d 100644
--- a/src/internal/poll/fcntl_libc.go
+++ b/src/internal/poll/fcntl_libc.go
@@ -9,5 +9,6 @@ package poll
import _ "unsafe" // for go:linkname
// Implemented in the syscall package.
+//
//go:linkname fcntl syscall.fcntl
func fcntl(fd int, cmd int, arg int) (int, error)
diff --git a/src/internal/poll/fd_opendir_darwin.go b/src/internal/poll/fd_opendir_darwin.go
index 8eb770c3581..3ae2dc84482 100644
--- a/src/internal/poll/fd_opendir_darwin.go
+++ b/src/internal/poll/fd_opendir_darwin.go
@@ -34,5 +34,6 @@ func (fd *FD) OpenDir() (uintptr, string, error) {
}
// Implemented in syscall/syscall_darwin.go.
+//
//go:linkname fdopendir syscall.fdopendir
func fdopendir(fd int) (dir uintptr, err error)
diff --git a/src/internal/poll/fd_poll_runtime.go b/src/internal/poll/fd_poll_runtime.go
index 2e9cd5c9d7e..4d3cc784059 100644
--- a/src/internal/poll/fd_poll_runtime.go
+++ b/src/internal/poll/fd_poll_runtime.go
@@ -15,6 +15,7 @@ import (
)
// runtimeNano returns the current value of the runtime clock in nanoseconds.
+//
//go:linkname runtimeNano runtime.nanotime
func runtimeNano() int64
diff --git a/src/internal/poll/fd_writev_darwin.go b/src/internal/poll/fd_writev_darwin.go
index 8137510c8b4..b5b8998df8c 100644
--- a/src/internal/poll/fd_writev_darwin.go
+++ b/src/internal/poll/fd_writev_darwin.go
@@ -12,5 +12,6 @@ import (
)
// Implemented in syscall/syscall_darwin.go.
+//
//go:linkname writev syscall.writev
func writev(fd int, iovecs []syscall.Iovec) (uintptr, error)
diff --git a/src/internal/poll/sendfile_solaris.go b/src/internal/poll/sendfile_solaris.go
index 0a884307bb1..7ae18f4b1a6 100644
--- a/src/internal/poll/sendfile_solaris.go
+++ b/src/internal/poll/sendfile_solaris.go
@@ -7,6 +7,7 @@ package poll
import "syscall"
// Not strictly needed, but very helpful for debugging, see issue #10221.
+//
//go:cgo_import_dynamic _ _ "libsendfile.so"
//go:cgo_import_dynamic _ _ "libsocket.so"
diff --git a/src/internal/profile/legacy_profile.go b/src/internal/profile/legacy_profile.go
index 377a43d5858..fee420986ee 100644
--- a/src/internal/profile/legacy_profile.go
+++ b/src/internal/profile/legacy_profile.go
@@ -335,11 +335,12 @@ func addTracebackSample(l []*Location, s []string, p *Profile) {
//
// The general format for profilez samples is a sequence of words in
// binary format. The first words are a header with the following data:
-// 1st word -- 0
-// 2nd word -- 3
-// 3rd word -- 0 if a c++ application, 1 if a java application.
-// 4th word -- Sampling period (in microseconds).
-// 5th word -- Padding.
+//
+// 1st word -- 0
+// 2nd word -- 3
+// 3rd word -- 0 if a c++ application, 1 if a java application.
+// 4th word -- Sampling period (in microseconds).
+// 5th word -- Padding.
func parseCPU(b []byte) (*Profile, error) {
var parse func([]byte) (uint64, []byte)
var n1, n2, n3, n4, n5 uint64
@@ -410,15 +411,18 @@ func cpuProfile(b []byte, period int64, parse func(b []byte) (uint64, []byte)) (
//
// profilez samples are a repeated sequence of stack frames of the
// form:
-// 1st word -- The number of times this stack was encountered.
-// 2nd word -- The size of the stack (StackSize).
-// 3rd word -- The first address on the stack.
-// ...
-// StackSize + 2 -- The last address on the stack
+//
+// 1st word -- The number of times this stack was encountered.
+// 2nd word -- The size of the stack (StackSize).
+// 3rd word -- The first address on the stack.
+// ...
+// StackSize + 2 -- The last address on the stack
+//
// The last stack trace is of the form:
-// 1st word -- 0
-// 2nd word -- 1
-// 3rd word -- 0
+//
+// 1st word -- 0
+// 2nd word -- 1
+// 3rd word -- 0
//
// Addresses from stack traces may point to the next instruction after
// each call. Optionally adjust by -1 to land somewhere on the actual
diff --git a/src/internal/reflectlite/export_test.go b/src/internal/reflectlite/export_test.go
index adae229e92c..3e5c258fb17 100644
--- a/src/internal/reflectlite/export_test.go
+++ b/src/internal/reflectlite/export_test.go
@@ -78,7 +78,9 @@ func Zero(typ Type) Value {
// ToInterface returns v's current value as an interface{}.
// It is equivalent to:
+//
// var i interface{} = (v's underlying value)
+//
// It panics if the Value was obtained by accessing
// unexported struct fields.
func ToInterface(v Value) (i any) {
diff --git a/src/internal/reflectlite/type.go b/src/internal/reflectlite/type.go
index 34677b400ec..bc6fc947738 100644
--- a/src/internal/reflectlite/type.go
+++ b/src/internal/reflectlite/type.go
@@ -113,6 +113,7 @@ const Ptr = Pointer
// available in the memory directly following the rtype value.
//
// tflag values must be kept in sync with copies in:
+//
// cmd/compile/internal/reflectdata/reflect.go
// cmd/link/internal/ld/decodesym.go
// runtime/type.go
@@ -298,7 +299,7 @@ type structType struct {
//
// The next two bytes are the data length:
//
-// l := uint16(data[1])<<8 | uint16(data[2])
+// l := uint16(data[1])<<8 | uint16(data[2])
//
// Bytes [3:3+l] are the string data.
//
diff --git a/src/internal/reflectlite/value.go b/src/internal/reflectlite/value.go
index 966230f5817..b9bca3ab44d 100644
--- a/src/internal/reflectlite/value.go
+++ b/src/internal/reflectlite/value.go
@@ -458,6 +458,7 @@ func arrayAt(p unsafe.Pointer, i int, eltSize uintptr, whySafe string) unsafe.Po
func ifaceE2I(t *rtype, src any, dst unsafe.Pointer)
// typedmemmove copies a value of type t to dst from src.
+//
//go:noescape
func typedmemmove(t *rtype, dst, src unsafe.Pointer)
diff --git a/src/internal/syscall/unix/nonblocking_libc.go b/src/internal/syscall/unix/nonblocking_libc.go
index 75c6e92a6e2..84940714c3c 100644
--- a/src/internal/syscall/unix/nonblocking_libc.go
+++ b/src/internal/syscall/unix/nonblocking_libc.go
@@ -20,5 +20,6 @@ func IsNonblock(fd int) (nonblocking bool, err error) {
}
// Implemented in the syscall package.
+//
//go:linkname fcntl syscall.fcntl
func fcntl(fd int, cmd int, arg int) (int, error)
diff --git a/src/internal/syscall/windows/registry/key.go b/src/internal/syscall/windows/registry/key.go
index ec38cf9288a..ce6397f1e29 100644
--- a/src/internal/syscall/windows/registry/key.go
+++ b/src/internal/syscall/windows/registry/key.go
@@ -22,7 +22,6 @@
//
// NOTE: This package is a copy of golang.org/x/sys/windows/registry
// with KeyInfo.ModTime removed to prevent dependency cycles.
-//
package registry
import (
diff --git a/src/internal/testenv/testenv.go b/src/internal/testenv/testenv.go
index 6ef889b02a9..1feb630cf5f 100644
--- a/src/internal/testenv/testenv.go
+++ b/src/internal/testenv/testenv.go
@@ -35,7 +35,7 @@ func Builder() string {
return os.Getenv("GO_BUILDER_NAME")
}
-// HasGoBuild reports whether the current system can build programs with ``go build''
+// HasGoBuild reports whether the current system can build programs with “go build”
// and then run them with os.StartProcess or exec.Command.
func HasGoBuild() bool {
if os.Getenv("GO_GCFLAGS") != "" {
@@ -52,7 +52,7 @@ func HasGoBuild() bool {
return true
}
-// MustHaveGoBuild checks that the current system can build programs with ``go build''
+// MustHaveGoBuild checks that the current system can build programs with “go build”
// and then run them with os.StartProcess or exec.Command.
// If not, MustHaveGoBuild calls t.Skip with an explanation.
func MustHaveGoBuild(t testing.TB) {
@@ -64,13 +64,13 @@ func MustHaveGoBuild(t testing.TB) {
}
}
-// HasGoRun reports whether the current system can run programs with ``go run.''
+// HasGoRun reports whether the current system can run programs with “go run.”
func HasGoRun() bool {
// For now, having go run and having go build are the same.
return HasGoBuild()
}
-// MustHaveGoRun checks that the current system can run programs with ``go run.''
+// MustHaveGoRun checks that the current system can run programs with “go run.”
// If not, MustHaveGoRun calls t.Skip with an explanation.
func MustHaveGoRun(t testing.TB) {
if !HasGoRun() {
diff --git a/src/internal/txtar/archive.go b/src/internal/txtar/archive.go
index 214256617b5..81b31454512 100644
--- a/src/internal/txtar/archive.go
+++ b/src/internal/txtar/archive.go
@@ -6,15 +6,15 @@
//
// The goals for the format are:
//
-// - be trivial enough to create and edit by hand.
-// - be able to store trees of text files describing go command test cases.
-// - diff nicely in git history and code reviews.
+// - be trivial enough to create and edit by hand.
+// - be able to store trees of text files describing go command test cases.
+// - diff nicely in git history and code reviews.
//
// Non-goals include being a completely general archive format,
// storing binary data, storing file modes, storing special files like
// symbolic links, and so on.
//
-// Txtar format
+// # Txtar format
//
// A txtar archive is zero or more comment lines and then a sequence of file entries.
// Each file entry begins with a file marker line of the form "-- FILENAME --"
diff --git a/src/io/ioutil/ioutil.go b/src/io/ioutil/ioutil.go
index 9921c2ae508..6a1d69172ce 100644
--- a/src/io/ioutil/ioutil.go
+++ b/src/io/ioutil/ioutil.go
@@ -55,6 +55,17 @@ func WriteFile(filename string, data []byte, perm fs.FileMode) error {
// it returns a list of fs.DirEntry instead of fs.FileInfo,
// and it returns partial results in the case of an error
// midway through reading a directory.
+//
+// If you must continue obtaining a list of fs.FileInfo, you still can:
+//
+// entries, err := os.ReadDir(dirname)
+// if err != nil { ... }
+// infos := make([]fs.FileInfo, 0, len(entries))
+// for _, entry := range entries {
+// info, err := entry.Info()
+// if err != nil { ... }
+// infos = append(infos, info)
+// }
func ReadDir(dirname string) ([]fs.FileInfo, error) {
f, err := os.Open(dirname)
if err != nil {
diff --git a/src/log/log.go b/src/log/log.go
index 5e79b195224..f7e48d5599b 100644
--- a/src/log/log.go
+++ b/src/log/log.go
@@ -32,8 +32,11 @@ import (
// The prefix is followed by a colon only when Llongfile or Lshortfile
// is specified.
// For example, flags Ldate | Ltime (or LstdFlags) produce,
+//
// 2009/01/23 01:23:23 message
+//
// while flags Ldate | Ltime | Lmicroseconds | Llongfile produce,
+//
// 2009/01/23 01:23:23.123123 /a/b/c/d.go:23: message
const (
Ldate = 1 << iota // the date in the local time zone: 2009/01/23
@@ -107,10 +110,10 @@ func itoa(buf *[]byte, i int, wid int) {
}
// formatHeader writes log header to buf in following order:
-// * l.prefix (if it's not blank and Lmsgprefix is unset),
-// * date and/or time (if corresponding flags are provided),
-// * file and line number (if corresponding flags are provided),
-// * l.prefix (if it's not blank and Lmsgprefix is set).
+// - l.prefix (if it's not blank and Lmsgprefix is unset),
+// - date and/or time (if corresponding flags are provided),
+// - file and line number (if corresponding flags are provided),
+// - l.prefix (if it's not blank and Lmsgprefix is set).
func (l *Logger) formatHeader(buf *[]byte, t time.Time, file string, line int) {
if l.flag&Lmsgprefix == 0 {
*buf = append(*buf, l.prefix...)
diff --git a/src/log/syslog/doc.go b/src/log/syslog/doc.go
index bd12bea581f..9a33eeb5d57 100644
--- a/src/log/syslog/doc.go
+++ b/src/log/syslog/doc.go
@@ -13,7 +13,7 @@
// The syslog package is frozen and is not accepting new features.
// Some external packages provide more functionality. See:
//
-// https://godoc.org/?q=syslog
+// https://godoc.org/?q=syslog
package syslog
// BUG(brainman): This package is not implemented on Windows. As the
diff --git a/src/make.bash b/src/make.bash
index e517a1bda91..ab2ce19f4ee 100755
--- a/src/make.bash
+++ b/src/make.bash
@@ -73,12 +73,6 @@
set -e
-export GOENV=off
-export GOWORK=off # Issue 51558
-unset GOBIN # Issue 14340
-unset GOFLAGS
-unset GO111MODULE
-
if [ ! -f run.bash ]; then
echo 'make.bash must be run from $GOROOT/src' 1>&2
exit 1
@@ -204,7 +198,7 @@ if [ "$GOROOT_BOOTSTRAP" = "$GOROOT" ]; then
exit 1
fi
rm -f cmd/dist/dist
-GOROOT="$GOROOT_BOOTSTRAP" GOOS="" GOARCH="" GO111MODULE=off GOEXPERIMENT="" "$GOROOT_BOOTSTRAP/bin/go" build -o cmd/dist/dist ./cmd/dist
+GOROOT="$GOROOT_BOOTSTRAP" GOOS="" GOARCH="" GO111MODULE=off GOEXPERIMENT="" GOENV=off GOFLAGS="" "$GOROOT_BOOTSTRAP/bin/go" build -o cmd/dist/dist ./cmd/dist
# -e doesn't propagate out of eval, so check success by hand.
eval $(./cmd/dist/dist env -p || echo FAIL=true)
diff --git a/src/make.bat b/src/make.bat
index c2f87ace759..0ba2dd57c5a 100644
--- a/src/make.bat
+++ b/src/make.bat
@@ -46,11 +46,7 @@ if x%4==x--no-local goto nolocal
setlocal
:nolocal
-set GOENV=off
-set GOWORK=off
set GOBUILDFAIL=0
-set GOFLAGS=
-set GO111MODULE=
if exist make.bat goto ok
echo Must run make.bat from Go src directory.
@@ -102,6 +98,8 @@ set GOARCH=
set GOBIN=
set GOEXPERIMENT=
set GO111MODULE=off
+set GOENV=off
+set GOFLAGS=
"%GOROOT_BOOTSTRAP%\bin\go.exe" build -o cmd\dist\dist.exe .\cmd\dist
endlocal
if errorlevel 1 goto fail
diff --git a/src/make.rc b/src/make.rc
index 273d1511907..4597403a041 100755
--- a/src/make.rc
+++ b/src/make.rc
@@ -47,10 +47,6 @@ if(~ $1 -v) {
shift
}
-GOENV=off
-GOWORK=off
-GOFLAGS=()
-GO111MODULE=()
GOROOT = `{cd .. && pwd}
goroot_bootstrap_set = 'true'
if(! ~ $#GOROOT_BOOTSTRAP 1){
@@ -88,7 +84,7 @@ if(~ $GOROOT_BOOTSTRAP $GOROOT){
echo 'Building Go cmd/dist using '^$GOROOT_BOOTSTRAP
if(~ $#vflag 1)
echo cmd/dist
-GOROOT=$GOROOT_BOOTSTRAP GOOS='' GOARCH='' GOEXPERIMENT='' GO111MODULE=off $GOROOT_BOOTSTRAP/bin/go build -o cmd/dist/dist ./cmd/dist
+GOROOT=$GOROOT_BOOTSTRAP GOOS='' GOARCH='' GOEXPERIMENT='' GO111MODULE=off GOENV=off GOFLAGS='' $GOROOT_BOOTSTRAP/bin/go build -o cmd/dist/dist ./cmd/dist
eval `{./cmd/dist/dist env -9}
if(~ $#vflag 1)
diff --git a/src/math/abs.go b/src/math/abs.go
index df83add6956..08be14548dd 100644
--- a/src/math/abs.go
+++ b/src/math/abs.go
@@ -7,6 +7,7 @@ package math
// Abs returns the absolute value of x.
//
// Special cases are:
+//
// Abs(±Inf) = +Inf
// Abs(NaN) = NaN
func Abs(x float64) float64 {
diff --git a/src/math/acosh.go b/src/math/acosh.go
index f74e0b62fba..a85d003d3ea 100644
--- a/src/math/acosh.go
+++ b/src/math/acosh.go
@@ -36,6 +36,7 @@ package math
// Acosh returns the inverse hyperbolic cosine of x.
//
// Special cases are:
+//
// Acosh(+Inf) = +Inf
// Acosh(x) = NaN if x < 1
// Acosh(NaN) = NaN
diff --git a/src/math/asin.go b/src/math/asin.go
index 989a74155be..8e1b2ab4916 100644
--- a/src/math/asin.go
+++ b/src/math/asin.go
@@ -14,6 +14,7 @@ package math
// Asin returns the arcsine, in radians, of x.
//
// Special cases are:
+//
// Asin(±0) = ±0
// Asin(x) = NaN if x < -1 or x > 1
func Asin(x float64) float64 {
@@ -52,6 +53,7 @@ func asin(x float64) float64 {
// Acos returns the arccosine, in radians, of x.
//
// Special case is:
+//
// Acos(x) = NaN if x < -1 or x > 1
func Acos(x float64) float64 {
if haveArchAcos {
diff --git a/src/math/asinh.go b/src/math/asinh.go
index 6dcb241c1fe..6f6e9e46082 100644
--- a/src/math/asinh.go
+++ b/src/math/asinh.go
@@ -33,6 +33,7 @@ package math
// Asinh returns the inverse hyperbolic sine of x.
//
// Special cases are:
+//
// Asinh(±0) = ±0
// Asinh(±Inf) = ±Inf
// Asinh(NaN) = NaN
diff --git a/src/math/atan.go b/src/math/atan.go
index 69af8601614..e722e99757f 100644
--- a/src/math/atan.go
+++ b/src/math/atan.go
@@ -90,8 +90,9 @@ func satan(x float64) float64 {
// Atan returns the arctangent, in radians, of x.
//
// Special cases are:
-// Atan(±0) = ±0
-// Atan(±Inf) = ±Pi/2
+//
+// Atan(±0) = ±0
+// Atan(±Inf) = ±Pi/2
func Atan(x float64) float64 {
if haveArchAtan {
return archAtan(x)
diff --git a/src/math/atan2.go b/src/math/atan2.go
index 11d7e81acdf..c324ed0a157 100644
--- a/src/math/atan2.go
+++ b/src/math/atan2.go
@@ -9,6 +9,7 @@ package math
// of the return value.
//
// Special cases are (in order):
+//
// Atan2(y, NaN) = NaN
// Atan2(NaN, x) = NaN
// Atan2(+0, x>=0) = +0
diff --git a/src/math/atanh.go b/src/math/atanh.go
index fe8bd6d8a43..9d594625a5c 100644
--- a/src/math/atanh.go
+++ b/src/math/atanh.go
@@ -39,6 +39,7 @@ package math
// Atanh returns the inverse hyperbolic tangent of x.
//
// Special cases are:
+//
// Atanh(1) = +Inf
// Atanh(±0) = ±0
// Atanh(-1) = -Inf
diff --git a/src/math/big/arith_ppc64x.s b/src/math/big/arith_ppc64x.s
index 68c62864946..601cafe6bb7 100644
--- a/src/math/big/arith_ppc64x.s
+++ b/src/math/big/arith_ppc64x.s
@@ -346,11 +346,161 @@ done:
MOVD R4, c+56(FP)
RET
+//func shlVU(z, x []Word, s uint) (c Word)
TEXT ·shlVU(SB), NOSPLIT, $0
- BR ·shlVU_g(SB)
+ MOVD z+0(FP), R3
+ MOVD x+24(FP), R6
+ MOVD s+48(FP), R9
+ MOVD z_len+8(FP), R4
+ MOVD x_len+32(FP), R7
+ CMP R9, R0 // s==0 copy(z,x)
+ BEQ zeroshift
+ CMP R4, R0 // len(z)==0 return
+ BEQ done
+
+ ADD $-1, R4, R5 // len(z)-1
+ SUBC R9, $64, R4 // ŝ=_W-s, we skip & by _W-1 as the caller ensures s < _W(64)
+ SLD $3, R5, R7
+ ADD R6, R7, R15 // save starting address &x[len(z)-1]
+ ADD R3, R7, R16 // save starting address &z[len(z)-1]
+ MOVD (R6)(R7), R14
+ SRD R4, R14, R7 // compute x[len(z)-1]>>ŝ into R7
+ CMP R5, R0 // iterate from i=len(z)-1 to 0
+ BEQ loopexit // Already at end?
+ MOVD 0(R15),R10 // x[i]
+shloop:
+ SLD R9, R10, R10 // x[i]<<s
+ MOVDU -8(R15), R14
+ SRD R4, R14, R11 // x[i-1]>>ŝ
+ OR R11, R10, R10
+ MOVD R10, 0(R16) // z[i-1]=x[i]<<s | x[i-1]>>ŝ
+ MOVD R14, R10 // reuse x[i-1] for next iteration
+ ADD $-8, R16 // i--
+ CMP R15, R6 // &x[i-1]>&x[0]?
+ BGT shloop
+loopexit:
+ MOVD 0(R6), R4
+ SLD R9, R4, R4
+ MOVD R4, 0(R3) // z[0]=x[0]<<s
+ MOVD R7, c+56(FP) // store pre-computed x[len(z)-1]>>ŝ into c
+ RET
+
+zeroshift:
+ CMP R6, R0 // x is null, nothing to copy
+ BEQ done
+ CMP R6, R3 // if x is same as z, nothing to copy
+ BEQ done
+ CMP R7, R4
+ ISEL $0, R7, R4, R7 // Take the lower bound of lengths of x,z
+ SLD $3, R7, R7
+ SUB R6, R3, R11 // dest - src
+ CMPU R11, R7, CR2 // < len?
+ BLT CR2, backward // there is overlap, copy backwards
+ MOVD $0, R14
+ // shlVU processes backwards, but added a forward copy option
+ // since its faster on POWER
+repeat:
+ MOVD (R6)(R14), R15 // Copy 8 bytes at a time
+ MOVD R15, (R3)(R14)
+ ADD $8, R14
+ CMP R14, R7 // More 8 bytes left?
+ BLT repeat
+ BR done
+backward:
+ ADD $-8,R7, R14
+repeatback:
+ MOVD (R6)(R14), R15 // copy x into z backwards
+ MOVD R15, (R3)(R14) // copy 8 bytes at a time
+ SUB $8, R14
+ CMP R14, $-8 // More 8 bytes left?
+ BGT repeatback
+
+done:
+ MOVD R0, c+56(FP) // c=0
+ RET
+//func shrVU(z, x []Word, s uint) (c Word)
TEXT ·shrVU(SB), NOSPLIT, $0
- BR ·shrVU_g(SB)
+ MOVD z+0(FP), R3
+ MOVD x+24(FP), R6
+ MOVD s+48(FP), R9
+ MOVD z_len+8(FP), R4
+ MOVD x_len+32(FP), R7
+
+ CMP R9, R0 // s==0, copy(z,x)
+ BEQ zeroshift
+ CMP R4, R0 // len(z)==0 return
+ BEQ done
+ SUBC R9, $64, R5 // ŝ=_W-s, we skip & by _W-1 as the caller ensures s < _W(64)
+
+ MOVD 0(R6), R7
+ SLD R5, R7, R7 // compute x[0]<<ŝ
+ MOVD $1, R8 // iterate from i=1 to i<len(z)
+ CMP R8, R4
+ BGE loopexit // Already at end?
+
+ // vectorize if len(z) is >=3, else jump to scalar loop
+ CMP R4, $3
+ BLT scalar
+ MTVSRD R9, VS38 // s
+ VSPLTB $7, V6, V4
+ MTVSRD R5, VS39 // ŝ
+ VSPLTB $7, V7, V2
+ ADD $-2, R4, R16
+ PCALIGN $16
+loopback:
+ ADD $-1, R8, R10
+ SLD $3, R10
+ LXVD2X (R6)(R10), VS32 // load x[i-1], x[i]
+ SLD $3, R8, R12
+ LXVD2X (R6)(R12), VS33 // load x[i], x[i+1]
+
+ VSRD V0, V4, V3 // x[i-1]>>s, x[i]>>s
+ VSLD V1, V2, V5 // x[i]<<ŝ, x[i+1]<<ŝ
+ VOR V3, V5, V5 // Or(|) the two registers together
+ STXVD2X VS37, (R3)(R10) // store into z[i-1] and z[i]
+ ADD $2, R8 // Done processing 2 entries, i and i+1
+ CMP R8, R16 // Are there at least a couple of more entries left?
+ BLE loopback
+ CMP R8, R4 // Are we at the last element?
+ BEQ loopexit
+scalar:
+ ADD $-1, R8, R10
+ SLD $3, R10
+ MOVD (R6)(R10),R11
+ SRD R9, R11, R11 // x[len(z)-2] >> s
+ SLD $3, R8, R12
+ MOVD (R6)(R12), R12
+ SLD R5, R12, R12 // x[len(z)-1]<<ŝ
+ OR R12, R11, R11 // x[len(z)-2]>>s | x[len(z)-1]<<ŝ
+ MOVD R11, (R3)(R10) // z[len(z)-2]=x[len(z)-2]>>s | x[len(z)-1]<<ŝ
+loopexit:
+ ADD $-1, R4
+ SLD $3, R4
+ MOVD (R6)(R4), R5
+ SRD R9, R5, R5 // x[len(z)-1]>>s
+ MOVD R5, (R3)(R4) // z[len(z)-1]=x[len(z)-1]>>s
+ MOVD R7, c+56(FP) // store pre-computed x[0]<<ŝ into c
+ RET
+
+zeroshift:
+ CMP R6, R0 // x is null, nothing to copy
+ BEQ done
+ CMP R6, R3 // if x is same as z, nothing to copy
+ BEQ done
+ CMP R7, R4
+ ISEL $0, R7, R4, R7 // Take the lower bounds of lengths of x, z
+ SLD $3, R7, R7
+ MOVD $0, R14
+repeat:
+ MOVD (R6)(R14), R15 // copy 8 bytes at a time
+ MOVD R15, (R3)(R14) // shrVU processes bytes only forwards
+ ADD $8, R14
+ CMP R14, R7 // More 8 bytes left?
+ BLT repeat
+done:
+ MOVD R0, c+56(FP)
+ RET
// func mulAddVWW(z, x []Word, y, r Word) (c Word)
TEXT ·mulAddVWW(SB), NOSPLIT, $0
diff --git a/src/math/big/example_rat_test.go b/src/math/big/example_rat_test.go
index a97117001c0..dc674309801 100644
--- a/src/math/big/example_rat_test.go
+++ b/src/math/big/example_rat_test.go
@@ -10,10 +10,13 @@ import (
)
// Use the classic continued fraction for e
-// e = [1; 0, 1, 1, 2, 1, 1, ... 2n, 1, 1, ...]
+//
+// e = [1; 0, 1, 1, 2, 1, 1, ... 2n, 1, 1, ...]
+//
// i.e., for the nth term, use
-// 1 if n mod 3 != 1
-// (n-1)/3 * 2 if n mod 3 == 1
+//
+// 1 if n mod 3 != 1
+// (n-1)/3 * 2 if n mod 3 == 1
func recur(n, lim int64) *big.Rat {
term := new(big.Rat)
if n%3 != 1 {
diff --git a/src/math/big/float.go b/src/math/big/float.go
index 70c7b794a41..84666d817bb 100644
--- a/src/math/big/float.go
+++ b/src/math/big/float.go
@@ -21,7 +21,7 @@ const debugFloat = false // enable for debugging
// A nonzero finite Float represents a multi-precision floating point number
//
-// sign × mantissa × 2**exponent
+// sign × mantissa × 2**exponent
//
// with 0.5 <= mantissa < 1.0, and MinExp <= exponent <= MaxExp.
// A Float may also be zero (+0, -0) or infinite (+Inf, -Inf).
@@ -1668,9 +1668,9 @@ func (z *Float) Quo(x, y *Float) *Float {
// Cmp compares x and y and returns:
//
-// -1 if x < y
-// 0 if x == y (incl. -0 == 0, -Inf == -Inf, and +Inf == +Inf)
-// +1 if x > y
+// -1 if x < y
+// 0 if x == y (incl. -0 == 0, -Inf == -Inf, and +Inf == +Inf)
+// +1 if x > y
func (x *Float) Cmp(y *Float) int {
if debugFloat {
x.validate()
diff --git a/src/math/big/floatconv.go b/src/math/big/floatconv.go
index 93f7195219f..3bb51c7dea8 100644
--- a/src/math/big/floatconv.go
+++ b/src/math/big/floatconv.go
@@ -215,7 +215,7 @@ func (z *Float) pow5(n uint64) *Float {
// point number with a mantissa in the given conversion base (the exponent
// is always a decimal number), or a string representing an infinite value.
//
-// For base 0, an underscore character ``_'' may appear between a base
+// For base 0, an underscore character “_” may appear between a base
// prefix and an adjacent digit, and between successive digits; such
// underscores do not change the value of the number, or the returned
// digit count. Incorrect placement of underscores is reported as an
@@ -229,22 +229,22 @@ func (z *Float) pow5(n uint64) *Float {
// If z's precision is 0, it is changed to 64 before rounding takes effect.
// The number must be of the form:
//
-// number = [ sign ] ( float | "inf" | "Inf" ) .
-// sign = "+" | "-" .
-// float = ( mantissa | prefix pmantissa ) [ exponent ] .
-// prefix = "0" [ "b" | "B" | "o" | "O" | "x" | "X" ] .
-// mantissa = digits "." [ digits ] | digits | "." digits .
-// pmantissa = [ "_" ] digits "." [ digits ] | [ "_" ] digits | "." digits .
-// exponent = ( "e" | "E" | "p" | "P" ) [ sign ] digits .
-// digits = digit { [ "_" ] digit } .
-// digit = "0" ... "9" | "a" ... "z" | "A" ... "Z" .
+// number = [ sign ] ( float | "inf" | "Inf" ) .
+// sign = "+" | "-" .
+// float = ( mantissa | prefix pmantissa ) [ exponent ] .
+// prefix = "0" [ "b" | "B" | "o" | "O" | "x" | "X" ] .
+// mantissa = digits "." [ digits ] | digits | "." digits .
+// pmantissa = [ "_" ] digits "." [ digits ] | [ "_" ] digits | "." digits .
+// exponent = ( "e" | "E" | "p" | "P" ) [ sign ] digits .
+// digits = digit { [ "_" ] digit } .
+// digit = "0" ... "9" | "a" ... "z" | "A" ... "Z" .
//
// The base argument must be 0, 2, 8, 10, or 16. Providing an invalid base
// argument will lead to a run-time panic.
//
// For base 0, the number prefix determines the actual base: A prefix of
-// ``0b'' or ``0B'' selects base 2, ``0o'' or ``0O'' selects base 8, and
-// ``0x'' or ``0X'' selects base 16. Otherwise, the actual base is 10 and
+// “0b” or “0B” selects base 2, “0o” or “0O” selects base 8, and
+// “0x” or “0X” selects base 16. Otherwise, the actual base is 10 and
// no prefix is accepted. The octal prefix "0" is not supported (a leading
// "0" is simply considered a "0").
//
diff --git a/src/math/big/int.go b/src/math/big/int.go
index 700d00d031a..a31cf27418f 100644
--- a/src/math/big/int.go
+++ b/src/math/big/int.go
@@ -231,7 +231,7 @@ func (z *Int) Rem(x, y *Int) *Int {
// q = x/y with the result truncated to zero
// r = x - y*q
//
-// (See Daan Leijen, ``Division and Modulus for Computer Scientists''.)
+// (See Daan Leijen, “Division and Modulus for Computer Scientists”.)
// See DivMod for Euclidean division and modulus (unlike Go).
func (z *Int) QuoRem(x, y, r *Int) (*Int, *Int) {
z.abs, r.abs = z.abs.div(r.abs, x.abs, y.abs)
@@ -285,8 +285,8 @@ func (z *Int) Mod(x, y *Int) *Int {
// q = x div y such that
// m = x - y*q with 0 <= m < |y|
//
-// (See Raymond T. Boute, ``The Euclidean definition of the functions
-// div and mod''. ACM Transactions on Programming Languages and
+// (See Raymond T. Boute, “The Euclidean definition of the functions
+// div and mod”. ACM Transactions on Programming Languages and
// Systems (TOPLAS), 14(2):127-144, New York, NY, USA, 4/1992.
// ACM press.)
// See QuoRem for T-division and modulus (like Go).
@@ -310,9 +310,9 @@ func (z *Int) DivMod(x, y, m *Int) (*Int, *Int) {
// Cmp compares x and y and returns:
//
-// -1 if x < y
-// 0 if x == y
-// +1 if x > y
+// -1 if x < y
+// 0 if x == y
+// +1 if x > y
func (x *Int) Cmp(y *Int) (r int) {
// x cmp y == x cmp y
// x cmp (-y) == x
@@ -336,9 +336,9 @@ func (x *Int) Cmp(y *Int) (r int) {
// CmpAbs compares the absolute values of x and y and returns:
//
-// -1 if |x| < |y|
-// 0 if |x| == |y|
-// +1 if |x| > |y|
+// -1 if |x| < |y|
+// 0 if |x| == |y|
+// +1 if |x| > |y|
func (x *Int) CmpAbs(y *Int) int {
return x.abs.cmp(y.abs)
}
@@ -400,8 +400,8 @@ func (x *Int) IsUint64() bool {
//
// The base argument must be 0 or a value between 2 and MaxBase.
// For base 0, the number prefix determines the actual base: A prefix of
-// ``0b'' or ``0B'' selects base 2, ``0'', ``0o'' or ``0O'' selects base 8,
-// and ``0x'' or ``0X'' selects base 16. Otherwise, the selected base is 10
+// “0b” or “0B” selects base 2, “0”, “0o” or “0O” selects base 8,
+// and “0x” or “0X” selects base 16. Otherwise, the selected base is 10
// and no prefix is accepted.
//
// For bases <= 36, lower and upper case letters are considered the same:
@@ -409,7 +409,7 @@ func (x *Int) IsUint64() bool {
// For bases > 36, the upper case letters 'A' to 'Z' represent the digit
// values 36 to 61.
//
-// For base 0, an underscore character ``_'' may appear between a base
+// For base 0, an underscore character “_” may appear between a base
// prefix and an adjacent digit, and between successive digits; such
// underscores do not change the value of the number.
// Incorrect placement of underscores is reported as an error if there
@@ -556,8 +556,10 @@ func (z *Int) GCD(x, y, a, b *Int) *Int {
// lehmerSimulate attempts to simulate several Euclidean update steps
// using the leading digits of A and B. It returns u0, u1, v0, v1
// such that A and B can be updated as:
-// A = u0*A + v0*B
-// B = u1*A + v1*B
+//
+// A = u0*A + v0*B
+// B = u1*A + v1*B
+//
// Requirements: A >= B and len(B.abs) >= 2
// Since we are calculating with full words to avoid overflow,
// we use 'even' to track the sign of the cosequences.
@@ -608,8 +610,10 @@ func lehmerSimulate(A, B *Int) (u0, u1, v0, v1 Word, even bool) {
}
// lehmerUpdate updates the inputs A and B such that:
-// A = u0*A + v0*B
-// B = u1*A + v1*B
+//
+// A = u0*A + v0*B
+// B = u1*A + v1*B
+//
// where the signs of u0, u1, v0, v1 are given by even
// For even == true: u0, v1 >= 0 && u1, v0 <= 0
// For even == false: u0, v1 <= 0 && u1, v0 >= 0
@@ -883,9 +887,11 @@ func Jacobi(x, y *Int) int {
}
// modSqrt3Mod4 uses the identity
-// (a^((p+1)/4))^2 mod p
-// == u^(p+1) mod p
-// == u^2 mod p
+//
+// (a^((p+1)/4))^2 mod p
+// == u^(p+1) mod p
+// == u^2 mod p
+//
// to calculate the square root of any quadratic residue mod p quickly for 3
// mod 4 primes.
func (z *Int) modSqrt3Mod4Prime(x, p *Int) *Int {
@@ -896,9 +902,11 @@ func (z *Int) modSqrt3Mod4Prime(x, p *Int) *Int {
}
// modSqrt5Mod8 uses Atkin's observation that 2 is not a square mod p
-// alpha == (2*a)^((p-5)/8) mod p
-// beta == 2*a*alpha^2 mod p is a square root of -1
-// b == a*alpha*(beta-1) mod p is a square root of a
+//
+// alpha == (2*a)^((p-5)/8) mod p
+// beta == 2*a*alpha^2 mod p is a square root of -1
+// b == a*alpha*(beta-1) mod p is a square root of a
+//
// to calculate the square root of any quadratic residue mod p quickly for 5
// mod 8 primes.
func (z *Int) modSqrt5Mod8Prime(x, p *Int) *Int {
diff --git a/src/math/big/intconv.go b/src/math/big/intconv.go
index 2fe10ff0a24..a3a4023caa6 100644
--- a/src/math/big/intconv.go
+++ b/src/math/big/intconv.go
@@ -174,8 +174,8 @@ func (x *Int) Format(s fmt.State, ch rune) {
//
// The base argument must be 0 or a value from 2 through MaxBase. If the base
// is 0, the string prefix determines the actual conversion base. A prefix of
-// ``0b'' or ``0B'' selects base 2; a ``0'', ``0o'', or ``0O'' prefix selects
-// base 8, and a ``0x'' or ``0X'' prefix selects base 16. Otherwise the selected
+// “0b” or “0B” selects base 2; a “0”, “0o”, or “0O” prefix selects
+// base 8, and a “0x” or “0X” prefix selects base 16. Otherwise the selected
// base is 10.
func (z *Int) scan(r io.ByteScanner, base int) (*Int, int, error) {
// determine sign
diff --git a/src/math/big/nat.go b/src/math/big/nat.go
index ee0c63eb281..5cc42b80dc7 100644
--- a/src/math/big/nat.go
+++ b/src/math/big/nat.go
@@ -22,7 +22,7 @@ import (
// An unsigned integer x of the form
//
-// x = x[n-1]*_B^(n-1) + x[n-2]*_B^(n-2) + ... + x[1]*_B + x[0]
+// x = x[n-1]*_B^(n-1) + x[n-2]*_B^(n-2) + ... + x[1]*_B + x[0]
//
// with 0 <= x[i] < _B and 0 <= i < n is stored in a slice of length n,
// with the digits x[i] as the slice elements.
diff --git a/src/math/big/natconv.go b/src/math/big/natconv.go
index 99488ac833a..21fdab53fd4 100644
--- a/src/math/big/natconv.go
+++ b/src/math/big/natconv.go
@@ -66,7 +66,7 @@ var (
// scan returns the corresponding natural number res, the actual base b,
// a digit count, and a read or syntax error err, if any.
//
-// For base 0, an underscore character ``_'' may appear between a base
+// For base 0, an underscore character “_” may appear between a base
// prefix and an adjacent digit, and between successive digits; such
// underscores do not change the value of the number, or the returned
// digit count. Incorrect placement of underscores is reported as an
@@ -74,12 +74,12 @@ var (
// not recognized and thus terminate scanning like any other character
// that is not a valid radix point or digit.
//
-// number = mantissa | prefix pmantissa .
-// prefix = "0" [ "b" | "B" | "o" | "O" | "x" | "X" ] .
-// mantissa = digits "." [ digits ] | digits | "." digits .
-// pmantissa = [ "_" ] digits "." [ digits ] | [ "_" ] digits | "." digits .
-// digits = digit { [ "_" ] digit } .
-// digit = "0" ... "9" | "a" ... "z" | "A" ... "Z" .
+// number = mantissa | prefix pmantissa .
+// prefix = "0" [ "b" | "B" | "o" | "O" | "x" | "X" ] .
+// mantissa = digits "." [ digits ] | digits | "." digits .
+// pmantissa = [ "_" ] digits "." [ digits ] | [ "_" ] digits | "." digits .
+// digits = digit { [ "_" ] digit } .
+// digit = "0" ... "9" | "a" ... "z" | "A" ... "Z" .
//
// Unless fracOk is set, the base argument must be 0 or a value between
// 2 and MaxBase. If fracOk is set, the base argument must be one of
@@ -87,8 +87,8 @@ var (
// time panic.
//
// For base 0, the number prefix determines the actual base: A prefix of
-// ``0b'' or ``0B'' selects base 2, ``0o'' or ``0O'' selects base 8, and
-// ``0x'' or ``0X'' selects base 16. If fracOk is false, a ``0'' prefix
+// “0b” or “0B” selects base 2, “0o” or “0O” selects base 8, and
+// “0x” or “0X” selects base 16. If fracOk is false, a “0” prefix
// (immediately followed by digits) selects base 8 as well. Otherwise,
// the selected base is 10 and no prefix is accepted.
//
@@ -434,8 +434,9 @@ func (q nat) convertWords(s []byte, b Word, ndigits int, bb Word, table []diviso
// Split blocks greater than leafSize Words (or set to 0 to disable recursive conversion)
// Benchmark and configure leafSize using: go test -bench="Leaf"
-// 8 and 16 effective on 3.0 GHz Xeon "Clovertown" CPU (128 byte cache lines)
-// 8 and 16 effective on 2.66 GHz Core 2 Duo "Penryn" CPU
+//
+// 8 and 16 effective on 3.0 GHz Xeon "Clovertown" CPU (128 byte cache lines)
+// 8 and 16 effective on 2.66 GHz Core 2 Duo "Penryn" CPU
var leafSize int = 8 // number of Word-size binary values treat as a monolithic block
type divisor struct {
diff --git a/src/math/big/rat.go b/src/math/big/rat.go
index e77da67d1b9..700a6432659 100644
--- a/src/math/big/rat.go
+++ b/src/math/big/rat.go
@@ -478,9 +478,9 @@ func (z *Int) scaleDenom(x *Int, f nat) {
// Cmp compares x and y and returns:
//
-// -1 if x < y
-// 0 if x == y
-// +1 if x > y
+// -1 if x < y
+// 0 if x == y
+// +1 if x > y
func (x *Rat) Cmp(y *Rat) int {
var a, b Int
a.scaleDenom(&x.a, y.b.abs)
diff --git a/src/math/big/ratconv.go b/src/math/big/ratconv.go
index dadd4d7b8ee..794a51d007a 100644
--- a/src/math/big/ratconv.go
+++ b/src/math/big/ratconv.go
@@ -41,16 +41,16 @@ func (z *Rat) Scan(s fmt.ScanState, ch rune) error {
// success. s can be given as a (possibly signed) fraction "a/b", or as a
// floating-point number optionally followed by an exponent.
// If a fraction is provided, both the dividend and the divisor may be a
-// decimal integer or independently use a prefix of ``0b'', ``0'' or ``0o'',
-// or ``0x'' (or their upper-case variants) to denote a binary, octal, or
+// decimal integer or independently use a prefix of “0b”, “0” or “0o”,
+// or “0x” (or their upper-case variants) to denote a binary, octal, or
// hexadecimal integer, respectively. The divisor may not be signed.
// If a floating-point number is provided, it may be in decimal form or
-// use any of the same prefixes as above but for ``0'' to denote a non-decimal
-// mantissa. A leading ``0'' is considered a decimal leading 0; it does not
+// use any of the same prefixes as above but for “0” to denote a non-decimal
+// mantissa. A leading “0” is considered a decimal leading 0; it does not
// indicate octal representation in this case.
-// An optional base-10 ``e'' or base-2 ``p'' (or their upper-case variants)
+// An optional base-10 “e” or base-2 “p” (or their upper-case variants)
// exponent may be provided as well, except for hexadecimal floats which
-// only accept an (optional) ``p'' exponent (because an ``e'' or ``E'' cannot
+// only accept an (optional) “p” exponent (because an “e” or “E” cannot
// be distinguished from a mantissa digit). If the exponent's absolute value
// is too large, the operation may fail.
// The entire string, not just a prefix, must be valid for success. If the
@@ -205,10 +205,10 @@ func (z *Rat) SetString(s string) (*Rat, bool) {
}
// scanExponent scans the longest possible prefix of r representing a base 10
-// (``e'', ``E'') or a base 2 (``p'', ``P'') exponent, if any. It returns the
+// (“e”, “E”) or a base 2 (“p”, “P”) exponent, if any. It returns the
// exponent, the exponent base (10 or 2), or a read or syntax error, if any.
//
-// If sepOk is set, an underscore character ``_'' may appear between successive
+// If sepOk is set, an underscore character “_” may appear between successive
// exponent digits; such underscores do not change the value of the exponent.
// Incorrect placement of underscores is reported as an error if there are no
// other errors. If sepOk is not set, underscores are not recognized and thus
diff --git a/src/math/big/sqrt.go b/src/math/big/sqrt.go
index 0d50164557c..b4b03743f4d 100644
--- a/src/math/big/sqrt.go
+++ b/src/math/big/sqrt.go
@@ -82,7 +82,9 @@ func (z *Float) Sqrt(x *Float) *Float {
}
// Compute √x (to z.prec precision) by solving
-// 1/t² - x = 0
+//
+// 1/t² - x = 0
+//
// for t (using Newton's method), and then inverting.
func (z *Float) sqrtInverse(x *Float) {
// let
diff --git a/src/math/bits.go b/src/math/bits.go
index 77bcdbe1ce6..c5cb93b1594 100644
--- a/src/math/bits.go
+++ b/src/math/bits.go
@@ -27,10 +27,10 @@ func Inf(sign int) float64 {
return Float64frombits(v)
}
-// NaN returns an IEEE 754 ``not-a-number'' value.
+// NaN returns an IEEE 754 “not-a-number” value.
func NaN() float64 { return Float64frombits(uvnan) }
-// IsNaN reports whether f is an IEEE 754 ``not-a-number'' value.
+// IsNaN reports whether f is an IEEE 754 “not-a-number” value.
func IsNaN(f float64) (is bool) {
// IEEE 754 says that only NaNs satisfy f != f.
// To avoid the floating-point hardware, could use:
diff --git a/src/math/cbrt.go b/src/math/cbrt.go
index 45c8ecb3a8c..e5e9548cb1f 100644
--- a/src/math/cbrt.go
+++ b/src/math/cbrt.go
@@ -19,6 +19,7 @@ package math
// Cbrt returns the cube root of x.
//
// Special cases are:
+//
// Cbrt(±0) = ±0
// Cbrt(±Inf) = ±Inf
// Cbrt(NaN) = NaN
diff --git a/src/math/cmplx/isnan.go b/src/math/cmplx/isnan.go
index d3382c05eef..fed442cb483 100644
--- a/src/math/cmplx/isnan.go
+++ b/src/math/cmplx/isnan.go
@@ -18,7 +18,7 @@ func IsNaN(x complex128) bool {
return false
}
-// NaN returns a complex ``not-a-number'' value.
+// NaN returns a complex “not-a-number” value.
func NaN() complex128 {
nan := math.NaN()
return complex(nan, nan)
diff --git a/src/math/cmplx/pow.go b/src/math/cmplx/pow.go
index 5a405f8e960..666bba28c5b 100644
--- a/src/math/cmplx/pow.go
+++ b/src/math/cmplx/pow.go
@@ -44,6 +44,7 @@ import "math"
// Pow returns x**y, the base-x exponential of y.
// For generalized compatibility with math.Pow:
+//
// Pow(0, ±0) returns 1+0i
// Pow(0, c) for real(c)<0 returns Inf+0i if imag(c) is zero, otherwise Inf+Inf i.
func Pow(x, y complex128) complex128 {
diff --git a/src/math/copysign.go b/src/math/copysign.go
index 719c64b9eba..3a30afb4136 100644
--- a/src/math/copysign.go
+++ b/src/math/copysign.go
@@ -4,9 +4,9 @@
package math
-// Copysign returns a value with the magnitude
-// of x and the sign of y.
-func Copysign(x, y float64) float64 {
- const sign = 1 << 63
- return Float64frombits(Float64bits(x)&^sign | Float64bits(y)&sign)
+// Copysign returns a value with the magnitude of f
+// and the sign of sign.
+func Copysign(f, sign float64) float64 {
+ const signBit = 1 << 63
+ return Float64frombits(Float64bits(f)&^signBit | Float64bits(sign)&signBit)
}
diff --git a/src/math/dim.go b/src/math/dim.go
index 6a857bbe41f..6a286cdc75a 100644
--- a/src/math/dim.go
+++ b/src/math/dim.go
@@ -7,6 +7,7 @@ package math
// Dim returns the maximum of x-y or 0.
//
// Special cases are:
+//
// Dim(+Inf, +Inf) = NaN
// Dim(-Inf, -Inf) = NaN
// Dim(x, NaN) = Dim(NaN, x) = NaN
@@ -28,6 +29,7 @@ func Dim(x, y float64) float64 {
// Max returns the larger of x or y.
//
// Special cases are:
+//
// Max(x, +Inf) = Max(+Inf, x) = +Inf
// Max(x, NaN) = Max(NaN, x) = NaN
// Max(+0, ±0) = Max(±0, +0) = +0
@@ -61,6 +63,7 @@ func max(x, y float64) float64 {
// Min returns the smaller of x or y.
//
// Special cases are:
+//
// Min(x, -Inf) = Min(-Inf, x) = -Inf
// Min(x, NaN) = Min(NaN, x) = NaN
// Min(-0, ±0) = Min(±0, -0) = -0
diff --git a/src/math/erf.go b/src/math/erf.go
index 4d6fe472f1a..ba00c7d03ed 100644
--- a/src/math/erf.go
+++ b/src/math/erf.go
@@ -182,6 +182,7 @@ const (
// Erf returns the error function of x.
//
// Special cases are:
+//
// Erf(+Inf) = 1
// Erf(-Inf) = -1
// Erf(NaN) = NaN
@@ -266,6 +267,7 @@ func erf(x float64) float64 {
// Erfc returns the complementary error function of x.
//
// Special cases are:
+//
// Erfc(+Inf) = 0
// Erfc(-Inf) = 2
// Erfc(NaN) = NaN
diff --git a/src/math/erfinv.go b/src/math/erfinv.go
index ee423d33e42..eed0feb42dd 100644
--- a/src/math/erfinv.go
+++ b/src/math/erfinv.go
@@ -69,6 +69,7 @@ const (
// Erfinv returns the inverse error function of x.
//
// Special cases are:
+//
// Erfinv(1) = +Inf
// Erfinv(-1) = -Inf
// Erfinv(x) = NaN if x < -1 or x > 1
@@ -118,6 +119,7 @@ func Erfinv(x float64) float64 {
// Erfcinv returns the inverse of Erfc(x).
//
// Special cases are:
+//
// Erfcinv(0) = +Inf
// Erfcinv(2) = -Inf
// Erfcinv(x) = NaN if x < 0 or x > 2
diff --git a/src/math/exp.go b/src/math/exp.go
index d05eb91fb00..760795f46fe 100644
--- a/src/math/exp.go
+++ b/src/math/exp.go
@@ -7,8 +7,10 @@ package math
// Exp returns e**x, the base-e exponential of x.
//
// Special cases are:
+//
// Exp(+Inf) = +Inf
// Exp(NaN) = NaN
+//
// Very large values overflow to 0 or +Inf.
// Very small values underflow to 1.
func Exp(x float64) float64 {
diff --git a/src/math/expm1.go b/src/math/expm1.go
index 66d3421661e..ff1c82f5241 100644
--- a/src/math/expm1.go
+++ b/src/math/expm1.go
@@ -117,9 +117,11 @@ package math
// It is more accurate than Exp(x) - 1 when x is near zero.
//
// Special cases are:
+//
// Expm1(+Inf) = +Inf
// Expm1(-Inf) = -1
// Expm1(NaN) = NaN
+//
// Very large values overflow to -1 or +Inf.
func Expm1(x float64) float64 {
if haveArchExpm1 {
diff --git a/src/math/floor.go b/src/math/floor.go
index 7913a900e37..cb5856424b4 100644
--- a/src/math/floor.go
+++ b/src/math/floor.go
@@ -7,6 +7,7 @@ package math
// Floor returns the greatest integer value less than or equal to x.
//
// Special cases are:
+//
// Floor(±0) = ±0
// Floor(±Inf) = ±Inf
// Floor(NaN) = NaN
@@ -35,6 +36,7 @@ func floor(x float64) float64 {
// Ceil returns the least integer value greater than or equal to x.
//
// Special cases are:
+//
// Ceil(±0) = ±0
// Ceil(±Inf) = ±Inf
// Ceil(NaN) = NaN
@@ -52,6 +54,7 @@ func ceil(x float64) float64 {
// Trunc returns the integer value of x.
//
// Special cases are:
+//
// Trunc(±0) = ±0
// Trunc(±Inf) = ±Inf
// Trunc(NaN) = NaN
@@ -73,6 +76,7 @@ func trunc(x float64) float64 {
// Round returns the nearest integer, rounding half away from zero.
//
// Special cases are:
+//
// Round(±0) = ±0
// Round(±Inf) = ±Inf
// Round(NaN) = NaN
@@ -110,6 +114,7 @@ func Round(x float64) float64 {
// RoundToEven returns the nearest integer, rounding ties to even.
//
// Special cases are:
+//
// RoundToEven(±0) = ±0
// RoundToEven(±Inf) = ±Inf
// RoundToEven(NaN) = NaN
diff --git a/src/math/frexp.go b/src/math/frexp.go
index 3c8a909ed0c..e194947e646 100644
--- a/src/math/frexp.go
+++ b/src/math/frexp.go
@@ -10,6 +10,7 @@ package math
// with the absolute value of frac in the interval [½, 1).
//
// Special cases are:
+//
// Frexp(±0) = ±0, 0
// Frexp(±Inf) = ±Inf, 0
// Frexp(NaN) = NaN, 0
diff --git a/src/math/gamma.go b/src/math/gamma.go
index cc9e869496b..86c67232580 100644
--- a/src/math/gamma.go
+++ b/src/math/gamma.go
@@ -121,6 +121,7 @@ func stirling(x float64) (float64, float64) {
// Gamma returns the Gamma function of x.
//
// Special cases are:
+//
// Gamma(+Inf) = +Inf
// Gamma(+0) = +Inf
// Gamma(-0) = -Inf
diff --git a/src/math/hypot.go b/src/math/hypot.go
index 12af17766d1..4e79de0e9bc 100644
--- a/src/math/hypot.go
+++ b/src/math/hypot.go
@@ -12,6 +12,7 @@ package math
// unnecessary overflow and underflow.
//
// Special cases are:
+//
// Hypot(±Inf, q) = +Inf
// Hypot(p, ±Inf) = +Inf
// Hypot(NaN, q) = NaN
diff --git a/src/math/j0.go b/src/math/j0.go
index cb5f07bca63..a311e18d62d 100644
--- a/src/math/j0.go
+++ b/src/math/j0.go
@@ -70,6 +70,7 @@ package math
// J0 returns the order-zero Bessel function of the first kind.
//
// Special cases are:
+//
// J0(±Inf) = 0
// J0(0) = 1
// J0(NaN) = NaN
@@ -147,6 +148,7 @@ func J0(x float64) float64 {
// Y0 returns the order-zero Bessel function of the second kind.
//
// Special cases are:
+//
// Y0(+Inf) = 0
// Y0(0) = -Inf
// Y0(x < 0) = NaN
diff --git a/src/math/j1.go b/src/math/j1.go
index 7c7d279730d..cc19e75b950 100644
--- a/src/math/j1.go
+++ b/src/math/j1.go
@@ -69,6 +69,7 @@ package math
// J1 returns the order-one Bessel function of the first kind.
//
// Special cases are:
+//
// J1(±Inf) = 0
// J1(NaN) = NaN
func J1(x float64) float64 {
@@ -147,6 +148,7 @@ func J1(x float64) float64 {
// Y1 returns the order-one Bessel function of the second kind.
//
// Special cases are:
+//
// Y1(+Inf) = 0
// Y1(0) = -Inf
// Y1(x < 0) = NaN
diff --git a/src/math/jn.go b/src/math/jn.go
index b1aca8ff6be..3491692a96c 100644
--- a/src/math/jn.go
+++ b/src/math/jn.go
@@ -48,6 +48,7 @@ package math
// Jn returns the order-n Bessel function of the first kind.
//
// Special cases are:
+//
// Jn(n, ±Inf) = 0
// Jn(n, NaN) = NaN
func Jn(n int, x float64) float64 {
@@ -225,6 +226,7 @@ func Jn(n int, x float64) float64 {
// Yn returns the order-n Bessel function of the second kind.
//
// Special cases are:
+//
// Yn(n, +Inf) = 0
// Yn(n ≥ 0, 0) = -Inf
// Yn(n < 0, 0) = +Inf if n is odd, -Inf if n is even
diff --git a/src/math/ldexp.go b/src/math/ldexp.go
index 55c82f1e842..df365c0b1ac 100644
--- a/src/math/ldexp.go
+++ b/src/math/ldexp.go
@@ -8,6 +8,7 @@ package math
// It returns frac × 2**exp.
//
// Special cases are:
+//
// Ldexp(±0, exp) = ±0
// Ldexp(±Inf, exp) = ±Inf
// Ldexp(NaN, exp) = NaN
diff --git a/src/math/lgamma.go b/src/math/lgamma.go
index 7af5871744b..4058ad6631e 100644
--- a/src/math/lgamma.go
+++ b/src/math/lgamma.go
@@ -166,6 +166,7 @@ var _lgamW = [...]float64{
// Lgamma returns the natural logarithm and sign (-1 or +1) of Gamma(x).
//
// Special cases are:
+//
// Lgamma(+Inf) = +Inf
// Lgamma(0) = +Inf
// Lgamma(-integer) = +Inf
diff --git a/src/math/log.go b/src/math/log.go
index 1b3e306adfc..695a545e7f0 100644
--- a/src/math/log.go
+++ b/src/math/log.go
@@ -73,6 +73,7 @@ package math
// Log returns the natural logarithm of x.
//
// Special cases are:
+//
// Log(+Inf) = +Inf
// Log(0) = -Inf
// Log(x < 0) = NaN
diff --git a/src/math/log1p.go b/src/math/log1p.go
index c117f7245db..3a7b3854a81 100644
--- a/src/math/log1p.go
+++ b/src/math/log1p.go
@@ -87,6 +87,7 @@ package math
// It is more accurate than Log(1 + x) when x is near zero.
//
// Special cases are:
+//
// Log1p(+Inf) = +Inf
// Log1p(±0) = ±0
// Log1p(-1) = -Inf
diff --git a/src/math/logb.go b/src/math/logb.go
index f2769d4fd75..04ba3e968e7 100644
--- a/src/math/logb.go
+++ b/src/math/logb.go
@@ -7,6 +7,7 @@ package math
// Logb returns the binary exponent of x.
//
// Special cases are:
+//
// Logb(±Inf) = +Inf
// Logb(0) = -Inf
// Logb(NaN) = NaN
@@ -26,6 +27,7 @@ func Logb(x float64) float64 {
// Ilogb returns the binary exponent of x as an integer.
//
// Special cases are:
+//
// Ilogb(±Inf) = MaxInt32
// Ilogb(0) = MinInt32
// Ilogb(NaN) = MaxInt32
diff --git a/src/math/mod.go b/src/math/mod.go
index 6bc5f288325..6f24250cfb4 100644
--- a/src/math/mod.go
+++ b/src/math/mod.go
@@ -13,6 +13,7 @@ package math
// sign agrees with that of x.
//
// Special cases are:
+//
// Mod(±Inf, y) = NaN
// Mod(NaN, y) = NaN
// Mod(x, 0) = NaN
diff --git a/src/math/modf.go b/src/math/modf.go
index bf08dc65568..613a75fc9a6 100644
--- a/src/math/modf.go
+++ b/src/math/modf.go
@@ -8,6 +8,7 @@ package math
// that sum to f. Both values have the same sign as f.
//
// Special cases are:
+//
// Modf(±Inf) = ±Inf, NaN
// Modf(NaN) = NaN, NaN
func Modf(f float64) (int float64, frac float64) {
diff --git a/src/math/nextafter.go b/src/math/nextafter.go
index 9088e4d248a..ec18d542d9c 100644
--- a/src/math/nextafter.go
+++ b/src/math/nextafter.go
@@ -7,6 +7,7 @@ package math
// Nextafter32 returns the next representable float32 value after x towards y.
//
// Special cases are:
+//
// Nextafter32(x, x) = x
// Nextafter32(NaN, y) = NaN
// Nextafter32(x, NaN) = NaN
@@ -29,6 +30,7 @@ func Nextafter32(x, y float32) (r float32) {
// Nextafter returns the next representable float64 value after x towards y.
//
// Special cases are:
+//
// Nextafter(x, x) = x
// Nextafter(NaN, y) = NaN
// Nextafter(x, NaN) = NaN
diff --git a/src/math/pow.go b/src/math/pow.go
index e45a044ae1c..3af8c8b649b 100644
--- a/src/math/pow.go
+++ b/src/math/pow.go
@@ -15,6 +15,7 @@ func isOddInt(x float64) bool {
// Pow returns x**y, the base-x exponential of y.
//
// Special cases are (in order):
+//
// Pow(x, ±0) = 1 for any x
// Pow(1, y) = 1 for any y
// Pow(x, 1) = x for any x
diff --git a/src/math/pow10.go b/src/math/pow10.go
index 1234e208850..c31ad8dbc77 100644
--- a/src/math/pow10.go
+++ b/src/math/pow10.go
@@ -25,6 +25,7 @@ var pow10negtab32 = [...]float64{
// Pow10 returns 10**n, the base-10 exponential of n.
//
// Special cases are:
+//
// Pow10(n) = 0 for n < -323
// Pow10(n) = +Inf for n > 308
func Pow10(n int) float64 {
diff --git a/src/math/rand/exp.go b/src/math/rand/exp.go
index 9a07ba1be00..c1162c19b68 100644
--- a/src/math/rand/exp.go
+++ b/src/math/rand/exp.go
@@ -26,7 +26,7 @@ const (
// To produce a distribution with a different rate parameter,
// callers can adjust the output using:
//
-// sample = ExpFloat64() / desiredRateParameter
+// sample = ExpFloat64() / desiredRateParameter
func (r *Rand) ExpFloat64() float64 {
for {
j := r.Uint32()
diff --git a/src/math/rand/normal.go b/src/math/rand/normal.go
index 48ecdd5adbd..6654479a005 100644
--- a/src/math/rand/normal.go
+++ b/src/math/rand/normal.go
@@ -33,7 +33,7 @@ func absInt32(i int32) uint32 {
// To produce a different normal distribution, callers can
// adjust the output using:
//
-// sample = NormFloat64() * desiredStdDev + desiredMean
+// sample = NormFloat64() * desiredStdDev + desiredMean
func (r *Rand) NormFloat64() float64 {
for {
j := int32(r.Uint32()) // Possibly negative
diff --git a/src/math/rand/rand.go b/src/math/rand/rand.go
index dfbd1fa4e7c..4cce3dab640 100644
--- a/src/math/rand/rand.go
+++ b/src/math/rand/rand.go
@@ -365,7 +365,7 @@ func Read(p []byte) (n int, err error) { return globalRand.Read(p) }
// To produce a different normal distribution, callers can
// adjust the output using:
//
-// sample = NormFloat64() * desiredStdDev + desiredMean
+// sample = NormFloat64() * desiredStdDev + desiredMean
func NormFloat64() float64 { return globalRand.NormFloat64() }
// ExpFloat64 returns an exponentially distributed float64 in the range
@@ -374,7 +374,7 @@ func NormFloat64() float64 { return globalRand.NormFloat64() }
// To produce a distribution with a different rate parameter,
// callers can adjust the output using:
//
-// sample = ExpFloat64() / desiredRateParameter
+// sample = ExpFloat64() / desiredRateParameter
func ExpFloat64() float64 { return globalRand.ExpFloat64() }
type lockedSource struct {
diff --git a/src/math/remainder.go b/src/math/remainder.go
index bf8bfd5553a..8e99345c59b 100644
--- a/src/math/remainder.go
+++ b/src/math/remainder.go
@@ -29,6 +29,7 @@ package math
// Remainder returns the IEEE 754 floating-point remainder of x/y.
//
// Special cases are:
+//
// Remainder(±Inf, y) = NaN
// Remainder(NaN, y) = NaN
// Remainder(x, 0) = NaN
diff --git a/src/math/sin.go b/src/math/sin.go
index d95bb548e8d..4793d7e7cd6 100644
--- a/src/math/sin.go
+++ b/src/math/sin.go
@@ -112,6 +112,7 @@ var _cos = [...]float64{
// Cos returns the cosine of the radian argument x.
//
// Special cases are:
+//
// Cos(±Inf) = NaN
// Cos(NaN) = NaN
func Cos(x float64) float64 {
@@ -177,6 +178,7 @@ func cos(x float64) float64 {
// Sin returns the sine of the radian argument x.
//
// Special cases are:
+//
// Sin(±0) = ±0
// Sin(±Inf) = NaN
// Sin(NaN) = NaN
diff --git a/src/math/sincos.go b/src/math/sincos.go
index 5c5726f6898..e3fb96094fa 100644
--- a/src/math/sincos.go
+++ b/src/math/sincos.go
@@ -9,6 +9,7 @@ package math
// Sincos returns Sin(x), Cos(x).
//
// Special cases are:
+//
// Sincos(±0) = ±0, 1
// Sincos(±Inf) = NaN, NaN
// Sincos(NaN) = NaN, NaN
diff --git a/src/math/sinh.go b/src/math/sinh.go
index 9fe9b4e17a1..78b3c299d66 100644
--- a/src/math/sinh.go
+++ b/src/math/sinh.go
@@ -19,6 +19,7 @@ package math
// Sinh returns the hyperbolic sine of x.
//
// Special cases are:
+//
// Sinh(±0) = ±0
// Sinh(±Inf) = ±Inf
// Sinh(NaN) = NaN
@@ -71,6 +72,7 @@ func sinh(x float64) float64 {
// Cosh returns the hyperbolic cosine of x.
//
// Special cases are:
+//
// Cosh(±0) = 1
// Cosh(±Inf) = +Inf
// Cosh(NaN) = NaN
diff --git a/src/math/sqrt.go b/src/math/sqrt.go
index 903d57d5e0d..b6d80c2c6f8 100644
--- a/src/math/sqrt.go
+++ b/src/math/sqrt.go
@@ -85,6 +85,7 @@ package math
// Sqrt returns the square root of x.
//
// Special cases are:
+//
// Sqrt(+Inf) = +Inf
// Sqrt(±0) = ±0
// Sqrt(x < 0) = NaN
diff --git a/src/math/tan.go b/src/math/tan.go
index a25417f527d..515dd82f731 100644
--- a/src/math/tan.go
+++ b/src/math/tan.go
@@ -76,6 +76,7 @@ var _tanQ = [...]float64{
// Tan returns the tangent of the radian argument x.
//
// Special cases are:
+//
// Tan(±0) = ±0
// Tan(±Inf) = NaN
// Tan(NaN) = NaN
diff --git a/src/math/tanh.go b/src/math/tanh.go
index a8256784246..94ebc3b6515 100644
--- a/src/math/tanh.go
+++ b/src/math/tanh.go
@@ -68,6 +68,7 @@ var tanhQ = [...]float64{
// Tanh returns the hyperbolic tangent of x.
//
// Special cases are:
+//
// Tanh(±0) = ±0
// Tanh(±Inf) = ±1
// Tanh(NaN) = NaN
diff --git a/src/math/trig_reduce.go b/src/math/trig_reduce.go
index 5cdf4fa0137..5ecdd8375e3 100644
--- a/src/math/trig_reduce.go
+++ b/src/math/trig_reduce.go
@@ -14,7 +14,9 @@ import (
// where y is given by y = floor(x * (4 / Pi)) and C is the leading partial
// terms of 4/Pi. Since the leading terms (PI4A and PI4B in sin.go) have 30
// and 32 trailing zero bits, y should have less than 30 significant bits.
+//
// y < 1<<30 -> floor(x*4/Pi) < 1<<30 -> x < (1<<30 - 1) * Pi/4
+//
// So, conservatively we can take x < 1<<29.
// Above this threshold Payne-Hanek range reduction must be used.
const reduceThreshold = 1 << 29
diff --git a/src/mime/multipart/multipart.go b/src/mime/multipart/multipart.go
index c7bcb4d1219..aa05ac8f9c8 100644
--- a/src/mime/multipart/multipart.go
+++ b/src/mime/multipart/multipart.go
@@ -437,7 +437,8 @@ func (r *Reader) isBoundaryDelimiterLine(line []byte) (ret bool) {
// skipLWSPChar returns b with leading spaces and tabs removed.
// RFC 822 defines:
-// LWSP-char = SPACE / HTAB
+//
+// LWSP-char = SPACE / HTAB
func skipLWSPChar(b []byte) []byte {
for len(b) > 0 && (b[0] == ' ' || b[0] == '\t') {
b = b[1:]
diff --git a/src/mime/testdata/test.types.globs2 b/src/mime/testdata/test.types.globs2
index cb5b7899b0d..fd9df7078bd 100644
--- a/src/mime/testdata/test.types.globs2
+++ b/src/mime/testdata/test.types.globs2
@@ -6,4 +6,6 @@
# mime package test for globs2
50:document/test:*.t3
50:example/test:*.t4
+50:text/plain:*,v
+50:application/x-trash:*~
30:example/do-not-use:*.t4
diff --git a/src/mime/type.go b/src/mime/type.go
index bdb8bb319af..465ecf0d599 100644
--- a/src/mime/type.go
+++ b/src/mime/type.go
@@ -99,11 +99,11 @@ func initMime() {
// system's MIME-info database or mime.types file(s) if available under one or
// more of these names:
//
-// /usr/local/share/mime/globs2
-// /usr/share/mime/globs2
-// /etc/mime.types
-// /etc/apache2/mime.types
-// /etc/apache/mime.types
+// /usr/local/share/mime/globs2
+// /usr/share/mime/globs2
+// /etc/mime.types
+// /etc/apache2/mime.types
+// /etc/apache/mime.types
//
// On Windows, MIME types are extracted from the registry.
//
diff --git a/src/mime/type_unix.go b/src/mime/type_unix.go
index 52579c56b9a..e297ecf5c1a 100644
--- a/src/mime/type_unix.go
+++ b/src/mime/type_unix.go
@@ -40,11 +40,11 @@ func loadMimeGlobsFile(filename string) error {
scanner := bufio.NewScanner(f)
for scanner.Scan() {
- // Each line should be of format: weight:mimetype:*.ext
+ // Each line should be of format: weight:mimetype:*.ext[:morefields...]
fields := strings.Split(scanner.Text(), ":")
- if len(fields) < 3 || len(fields[0]) < 1 || len(fields[2]) < 2 {
+ if len(fields) < 3 || len(fields[0]) < 1 || len(fields[2]) < 3 {
continue
- } else if fields[0][0] == '#' || fields[2][0] != '*' {
+ } else if fields[0][0] == '#' || fields[2][0] != '*' || fields[2][1] != '.' {
continue
}
diff --git a/src/mime/type_unix_test.go b/src/mime/type_unix_test.go
index 6bb408566cd..ab14ae6f80c 100644
--- a/src/mime/type_unix_test.go
+++ b/src/mime/type_unix_test.go
@@ -27,6 +27,8 @@ func TestTypeByExtensionUNIX(t *testing.T) {
".t3": "document/test",
".t4": "example/test",
".png": "image/png",
+ ",v": "",
+ "~": "",
}
for ext, want := range typeTests {
diff --git a/src/net/cgo_unix.go b/src/net/cgo_unix.go
index b156b198ee2..71d90560ac9 100644
--- a/src/net/cgo_unix.go
+++ b/src/net/cgo_unix.go
@@ -250,12 +250,12 @@ func cgoLookupCNAME(ctx context.Context, name string) (cname string, err error,
// These are roughly enough for the following:
//
-// Source Encoding Maximum length of single name entry
-// Unicast DNS ASCII or <=253 + a NUL terminator
-// Unicode in RFC 5892 252 * total number of labels + delimiters + a NUL terminator
-// Multicast DNS UTF-8 in RFC 5198 or <=253 + a NUL terminator
-// the same as unicast DNS ASCII <=253 + a NUL terminator
-// Local database various depends on implementation
+// Source Encoding Maximum length of single name entry
+// Unicast DNS ASCII or <=253 + a NUL terminator
+// Unicode in RFC 5892 252 * total number of labels + delimiters + a NUL terminator
+// Multicast DNS UTF-8 in RFC 5198 or <=253 + a NUL terminator
+// the same as unicast DNS ASCII <=253 + a NUL terminator
+// Local database various depends on implementation
const (
nameinfoLen = 64
maxNameinfoLen = 4096
diff --git a/src/net/conf.go b/src/net/conf.go
index 716a37ff806..9d4752173e1 100644
--- a/src/net/conf.go
+++ b/src/net/conf.go
@@ -278,13 +278,15 @@ func (c *conf) hostLookupOrder(r *Resolver, hostname string) (ret hostLookupOrde
// goDebugNetDNS parses the value of the GODEBUG "netdns" value.
// The netdns value can be of the form:
-// 1 // debug level 1
-// 2 // debug level 2
-// cgo // use cgo for DNS lookups
-// go // use go for DNS lookups
-// cgo+1 // use cgo for DNS lookups + debug level 1
-// 1+cgo // same
-// cgo+2 // same, but debug level 2
+//
+// 1 // debug level 1
+// 2 // debug level 2
+// cgo // use cgo for DNS lookups
+// go // use go for DNS lookups
+// cgo+1 // use cgo for DNS lookups + debug level 1
+// 1+cgo // same
+// cgo+2 // same, but debug level 2
+//
// etc.
func goDebugNetDNS() (dnsMode string, debugLevel int) {
goDebug := godebug.Get("netdns")
diff --git a/src/net/dial.go b/src/net/dial.go
index 9159e6b3844..b24bd2f5f43 100644
--- a/src/net/dial.go
+++ b/src/net/dial.go
@@ -114,6 +114,7 @@ func minNonzeroTime(a, b time.Time) time.Time {
// - now+Timeout
// - d.Deadline
// - the context's deadline
+//
// Or zero, if none of Timeout, Deadline, or context's deadline is set.
func (d *Dialer) deadline(ctx context.Context, now time.Time) (earliest time.Time) {
if d.Timeout != 0 { // including negative, for historical reasons
@@ -289,6 +290,7 @@ func (r *Resolver) resolveAddrList(ctx context.Context, op, network, addr string
// Dial will try each IP address in order until one succeeds.
//
// Examples:
+//
// Dial("tcp", "golang.org:http")
// Dial("tcp", "192.0.2.1:http")
// Dial("tcp", "198.51.100.1:80")
@@ -304,6 +306,7 @@ func (r *Resolver) resolveAddrList(ctx context.Context, op, network, addr string
// behaves with a non-well known protocol number such as "0" or "255".
//
// Examples:
+//
// Dial("ip4:1", "192.0.2.1")
// Dial("ip6:ipv6-icmp", "2001:db8::1")
// Dial("ip6:58", "fe80::1%lo0")
diff --git a/src/net/fcntl_libc_test.go b/src/net/fcntl_libc_test.go
index 3478ce72318..78892e3a9fa 100644
--- a/src/net/fcntl_libc_test.go
+++ b/src/net/fcntl_libc_test.go
@@ -9,5 +9,6 @@ package net
import _ "unsafe" // for go:linkname
// Implemented in the syscall package.
+//
//go:linkname fcntl syscall.fcntl
func fcntl(fd int, cmd int, arg int) (int, error)
diff --git a/src/net/http/cgi/host.go b/src/net/http/cgi/host.go
index 95b2e13e4ef..0d43e140d56 100644
--- a/src/net/http/cgi/host.go
+++ b/src/net/http/cgi/host.go
@@ -90,10 +90,11 @@ func (h *Handler) stderr() io.Writer {
// removeLeadingDuplicates remove leading duplicate in environments.
// It's possible to override environment like following.
-// cgi.Handler{
-// ...
-// Env: []string{"SCRIPT_FILENAME=foo.php"},
-// }
+//
+// cgi.Handler{
+// ...
+// Env: []string{"SCRIPT_FILENAME=foo.php"},
+// }
func removeLeadingDuplicates(env []string) (ret []string) {
for i, e := range env {
found := false
diff --git a/src/net/http/client.go b/src/net/http/client.go
index bc0ed1fc506..490349f7bd0 100644
--- a/src/net/http/client.go
+++ b/src/net/http/client.go
@@ -423,11 +423,11 @@ func basicAuth(username, password string) string {
// the following redirect codes, Get follows the redirect, up to a
// maximum of 10 redirects:
//
-// 301 (Moved Permanently)
-// 302 (Found)
-// 303 (See Other)
-// 307 (Temporary Redirect)
-// 308 (Permanent Redirect)
+// 301 (Moved Permanently)
+// 302 (Found)
+// 303 (See Other)
+// 307 (Temporary Redirect)
+// 308 (Permanent Redirect)
//
// An error is returned if there were too many redirects or if there
// was an HTTP protocol error. A non-2xx response doesn't cause an
@@ -452,11 +452,11 @@ func Get(url string) (resp *Response, err error) {
// following redirect codes, Get follows the redirect after calling the
// Client's CheckRedirect function:
//
-// 301 (Moved Permanently)
-// 302 (Found)
-// 303 (See Other)
-// 307 (Temporary Redirect)
-// 308 (Permanent Redirect)
+// 301 (Moved Permanently)
+// 302 (Found)
+// 303 (See Other)
+// 307 (Temporary Redirect)
+// 308 (Permanent Redirect)
//
// An error is returned if the Client's CheckRedirect function fails
// or if there was an HTTP protocol error. A non-2xx response doesn't
@@ -890,13 +890,13 @@ func (c *Client) PostForm(url string, data url.Values) (resp *Response, err erro
// the following redirect codes, Head follows the redirect, up to a
// maximum of 10 redirects:
//
-// 301 (Moved Permanently)
-// 302 (Found)
-// 303 (See Other)
-// 307 (Temporary Redirect)
-// 308 (Permanent Redirect)
+// 301 (Moved Permanently)
+// 302 (Found)
+// 303 (See Other)
+// 307 (Temporary Redirect)
+// 308 (Permanent Redirect)
//
-// Head is a wrapper around DefaultClient.Head
+// # Head is a wrapper around DefaultClient.Head
//
// To make a request with a specified context.Context, use NewRequestWithContext
// and DefaultClient.Do.
@@ -908,11 +908,11 @@ func Head(url string) (resp *Response, err error) {
// following redirect codes, Head follows the redirect after calling the
// Client's CheckRedirect function:
//
-// 301 (Moved Permanently)
-// 302 (Found)
-// 303 (See Other)
-// 307 (Temporary Redirect)
-// 308 (Permanent Redirect)
+// 301 (Moved Permanently)
+// 302 (Found)
+// 303 (See Other)
+// 307 (Temporary Redirect)
+// 308 (Permanent Redirect)
//
// To make a request with a specified context.Context, use NewRequestWithContext
// and Client.Do.
@@ -941,8 +941,8 @@ func (c *Client) CloseIdleConnections() {
}
// cancelTimerBody is an io.ReadCloser that wraps rc with two features:
-// 1) On Read error or close, the stop func is called.
-// 2) On Read failure, if reqDidTimeout is true, the error is wrapped and
+// 1. On Read error or close, the stop func is called.
+// 2. On Read failure, if reqDidTimeout is true, the error is wrapped and
// marked as net.Error that hit its timeout.
type cancelTimerBody struct {
stop func() // stops the time.Timer waiting to cancel the request
diff --git a/src/net/http/cookie.go b/src/net/http/cookie.go
index 6e1035330b4..9cb0804f8f2 100644
--- a/src/net/http/cookie.go
+++ b/src/net/http/cookie.go
@@ -387,11 +387,13 @@ func sanitizeCookieName(n string) string {
// sanitizeCookieValue produces a suitable cookie-value from v.
// https://tools.ietf.org/html/rfc6265#section-4.1.1
-// cookie-value = *cookie-octet / ( DQUOTE *cookie-octet DQUOTE )
-// cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E
-// ; US-ASCII characters excluding CTLs,
-// ; whitespace DQUOTE, comma, semicolon,
-// ; and backslash
+//
+// cookie-value = *cookie-octet / ( DQUOTE *cookie-octet DQUOTE )
+// cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E
+// ; US-ASCII characters excluding CTLs,
+// ; whitespace DQUOTE, comma, semicolon,
+// ; and backslash
+//
// We loosen this as spaces and commas are common in cookie values
// but we produce a quoted cookie-value if and only if v contains
// commas or spaces.
diff --git a/src/net/http/cookiejar/jar.go b/src/net/http/cookiejar/jar.go
index e6583da7fe6..309dfcc0e17 100644
--- a/src/net/http/cookiejar/jar.go
+++ b/src/net/http/cookiejar/jar.go
@@ -19,9 +19,9 @@ import (
)
// PublicSuffixList provides the public suffix of a domain. For example:
-// - the public suffix of "example.com" is "com",
-// - the public suffix of "foo1.foo2.foo3.co.uk" is "co.uk", and
-// - the public suffix of "bar.pvt.k12.ma.us" is "pvt.k12.ma.us".
+// - the public suffix of "example.com" is "com",
+// - the public suffix of "foo1.foo2.foo3.co.uk" is "co.uk", and
+// - the public suffix of "bar.pvt.k12.ma.us" is "pvt.k12.ma.us".
//
// Implementations of PublicSuffixList must be safe for concurrent use by
// multiple goroutines.
diff --git a/src/net/http/cookiejar/jar_test.go b/src/net/http/cookiejar/jar_test.go
index 47fb1abdaaf..b7267b17184 100644
--- a/src/net/http/cookiejar/jar_test.go
+++ b/src/net/http/cookiejar/jar_test.go
@@ -20,8 +20,9 @@ var tNow = time.Date(2013, 1, 1, 12, 0, 0, 0, time.UTC)
// testPSL implements PublicSuffixList with just two rules: "co.uk"
// and the default rule "*".
// The implementation has two intentional bugs:
-// PublicSuffix("www.buggy.psl") == "xy"
-// PublicSuffix("www2.buggy.psl") == "com"
+//
+// PublicSuffix("www.buggy.psl") == "xy"
+// PublicSuffix("www2.buggy.psl") == "com"
type testPSL struct{}
func (testPSL) String() string {
@@ -358,13 +359,13 @@ func mustParseURL(s string) *url.URL {
}
// jarTest encapsulates the following actions on a jar:
-// 1. Perform SetCookies with fromURL and the cookies from setCookies.
-// (Done at time tNow + 0 ms.)
-// 2. Check that the entries in the jar matches content.
-// (Done at time tNow + 1001 ms.)
-// 3. For each query in tests: Check that Cookies with toURL yields the
-// cookies in want.
-// (Query n done at tNow + (n+2)*1001 ms.)
+// 1. Perform SetCookies with fromURL and the cookies from setCookies.
+// (Done at time tNow + 0 ms.)
+// 2. Check that the entries in the jar matches content.
+// (Done at time tNow + 1001 ms.)
+// 3. For each query in tests: Check that Cookies with toURL yields the
+// cookies in want.
+// (Query n done at tNow + (n+2)*1001 ms.)
type jarTest struct {
description string // The description of what this test is supposed to test
fromURL string // The full URL of the request from which Set-Cookie headers where received
diff --git a/src/net/http/doc.go b/src/net/http/doc.go
index ae9b708c695..67c4246c608 100644
--- a/src/net/http/doc.go
+++ b/src/net/http/doc.go
@@ -102,6 +102,5 @@ directly and use its ConfigureTransport and/or ConfigureServer
functions. Manually configuring HTTP/2 via the golang.org/x/net/http2
package takes precedence over the net/http package's built-in HTTP/2
support.
-
*/
package http
diff --git a/src/net/http/fcgi/fcgi_test.go b/src/net/http/fcgi/fcgi_test.go
index 58887836208..7a344ff31dc 100644
--- a/src/net/http/fcgi/fcgi_test.go
+++ b/src/net/http/fcgi/fcgi_test.go
@@ -401,16 +401,16 @@ func TestResponseWriterSniffsContentType(t *testing.T) {
}
}
-type signallingNopCloser struct {
+type signalingNopCloser struct {
io.Reader
closed chan bool
}
-func (*signallingNopCloser) Write(buf []byte) (int, error) {
+func (*signalingNopCloser) Write(buf []byte) (int, error) {
return len(buf), nil
}
-func (rc *signallingNopCloser) Close() error {
+func (rc *signalingNopCloser) Close() error {
close(rc.closed)
return nil
}
@@ -429,7 +429,7 @@ func TestSlowRequest(t *testing.T) {
}
}(pw)
- rc := &signallingNopCloser{pr, make(chan bool)}
+ rc := &signalingNopCloser{pr, make(chan bool)}
handlerDone := make(chan bool)
c := newChild(rc, http.HandlerFunc(func(
diff --git a/src/net/http/filetransport.go b/src/net/http/filetransport.go
index 32126d7ec0f..94684b07a13 100644
--- a/src/net/http/filetransport.go
+++ b/src/net/http/filetransport.go
@@ -22,11 +22,11 @@ type fileTransport struct {
// The typical use case for NewFileTransport is to register the "file"
// protocol with a Transport, as in:
//
-// t := &http.Transport{}
-// t.RegisterProtocol("file", http.NewFileTransport(http.Dir("/")))
-// c := &http.Client{Transport: t}
-// res, err := c.Get("file:///etc/passwd")
-// ...
+// t := &http.Transport{}
+// t.RegisterProtocol("file", http.NewFileTransport(http.Dir("/")))
+// c := &http.Client{Transport: t}
+// res, err := c.Get("file:///etc/passwd")
+// ...
func NewFileTransport(fs FileSystem) RoundTripper {
return fileTransport{fileHandler{fs}}
}
diff --git a/src/net/http/fs.go b/src/net/http/fs.go
index d8f924296bc..7a1d5f4be5f 100644
--- a/src/net/http/fs.go
+++ b/src/net/http/fs.go
@@ -831,7 +831,7 @@ func FS(fsys fs.FS) FileSystem {
// To use the operating system's file system implementation,
// use http.Dir:
//
-// http.Handle("/", http.FileServer(http.Dir("/tmp")))
+// http.Handle("/", http.FileServer(http.Dir("/tmp")))
//
// To use an fs.FS implementation, use http.FS to convert it:
//
diff --git a/src/net/http/h2_bundle.go b/src/net/http/h2_bundle.go
index e955135c50f..4a76e6afe8c 100644
--- a/src/net/http/h2_bundle.go
+++ b/src/net/http/h2_bundle.go
@@ -3388,10 +3388,11 @@ func (s http2SettingID) String() string {
// name (key). See httpguts.ValidHeaderName for the base rules.
//
// Further, http2 says:
-// "Just as in HTTP/1.x, header field names are strings of ASCII
-// characters that are compared in a case-insensitive
-// fashion. However, header field names MUST be converted to
-// lowercase prior to their encoding in HTTP/2. "
+//
+// "Just as in HTTP/1.x, header field names are strings of ASCII
+// characters that are compared in a case-insensitive
+// fashion. However, header field names MUST be converted to
+// lowercase prior to their encoding in HTTP/2. "
func http2validWireHeaderFieldName(v string) bool {
if len(v) == 0 {
return false
@@ -3582,8 +3583,8 @@ func (s *http2sorter) SortStrings(ss []string) {
// validPseudoPath reports whether v is a valid :path pseudo-header
// value. It must be either:
//
-// *) a non-empty string starting with '/'
-// *) the string '*', for OPTIONS requests.
+// *) a non-empty string starting with '/'
+// *) the string '*', for OPTIONS requests.
//
// For now this is only used a quick check for deciding when to clean
// up Opaque URLs before sending requests from the Transport.
@@ -6269,8 +6270,9 @@ func (rws *http2responseWriterState) writeChunk(p []byte) (n int, err error) {
// prior to the headers being written. If the set of trailers is fixed
// or known before the header is written, the normal Go trailers mechanism
// is preferred:
-// https://golang.org/pkg/net/http/#ResponseWriter
-// https://golang.org/pkg/net/http/#example_ResponseWriter_trailers
+//
+// https://golang.org/pkg/net/http/#ResponseWriter
+// https://golang.org/pkg/net/http/#example_ResponseWriter_trailers
const http2TrailerPrefix = "Trailer:"
// promoteUndeclaredTrailers permits http.Handlers to set trailers
diff --git a/src/net/http/httptest/recorder.go b/src/net/http/httptest/recorder.go
index 1b712ef2b0a..1c1d8801558 100644
--- a/src/net/http/httptest/recorder.go
+++ b/src/net/http/httptest/recorder.go
@@ -207,18 +207,20 @@ func (rw *ResponseRecorder) Result() *http.Response {
if trailers, ok := rw.snapHeader["Trailer"]; ok {
res.Trailer = make(http.Header, len(trailers))
for _, k := range trailers {
- k = http.CanonicalHeaderKey(k)
- if !httpguts.ValidTrailerHeader(k) {
- // Ignore since forbidden by RFC 7230, section 4.1.2.
- continue
+ for _, k := range strings.Split(k, ",") {
+ k = http.CanonicalHeaderKey(textproto.TrimString(k))
+ if !httpguts.ValidTrailerHeader(k) {
+ // Ignore since forbidden by RFC 7230, section 4.1.2.
+ continue
+ }
+ vv, ok := rw.HeaderMap[k]
+ if !ok {
+ continue
+ }
+ vv2 := make([]string, len(vv))
+ copy(vv2, vv)
+ res.Trailer[k] = vv2
}
- vv, ok := rw.HeaderMap[k]
- if !ok {
- continue
- }
- vv2 := make([]string, len(vv))
- copy(vv2, vv)
- res.Trailer[k] = vv2
}
}
for k, vv := range rw.HeaderMap {
diff --git a/src/net/http/httptest/recorder_test.go b/src/net/http/httptest/recorder_test.go
index 8cb32dd7403..4782eced43e 100644
--- a/src/net/http/httptest/recorder_test.go
+++ b/src/net/http/httptest/recorder_test.go
@@ -220,8 +220,7 @@ func TestRecorder(t *testing.T) {
"Trailer headers are correctly recorded",
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Non-Trailer", "correct")
- w.Header().Set("Trailer", "Trailer-A")
- w.Header().Add("Trailer", "Trailer-B")
+ w.Header().Set("Trailer", "Trailer-A, Trailer-B")
w.Header().Add("Trailer", "Trailer-C")
io.WriteString(w, "<html>")
w.Header().Set("Non-Trailer", "incorrect")
diff --git a/src/net/http/httptest/server.go b/src/net/http/httptest/server.go
index 1c0c0f69875..f254a494d16 100644
--- a/src/net/http/httptest/server.go
+++ b/src/net/http/httptest/server.go
@@ -76,7 +76,9 @@ func newLocalListener() net.Listener {
// When debugging a particular http server-based test,
// this flag lets you run
+//
// go test -run=BrokenTest -httptest.serve=127.0.0.1:8000
+//
// to start the broken server so you can interact with it manually.
// We only register this flag if it looks like the caller knows about it
// and is trying to use it as we don't want to pollute flags and this
diff --git a/src/net/http/internal/chunked.go b/src/net/http/internal/chunked.go
index 37a72e9031a..5a174415dc4 100644
--- a/src/net/http/internal/chunked.go
+++ b/src/net/http/internal/chunked.go
@@ -163,10 +163,11 @@ var semi = []byte(";")
// removeChunkExtension removes any chunk-extension from p.
// For example,
-// "0" => "0"
-// "0;token" => "0"
-// "0;token=val" => "0"
-// `0;token="quoted string"` => "0"
+//
+// "0" => "0"
+// "0;token" => "0"
+// "0;token=val" => "0"
+// `0;token="quoted string"` => "0"
func removeChunkExtension(p []byte) ([]byte, error) {
p, _, _ = bytes.Cut(p, semi)
// TODO: care about exact syntax of chunk extensions? We're
diff --git a/src/net/http/pprof/pprof.go b/src/net/http/pprof/pprof.go
index dc855c8a6da..de5a4b9752a 100644
--- a/src/net/http/pprof/pprof.go
+++ b/src/net/http/pprof/pprof.go
@@ -10,15 +10,16 @@
// The handled paths all begin with /debug/pprof/.
//
// To use pprof, link this package into your program:
+//
// import _ "net/http/pprof"
//
// If your application is not already running an http server, you
// need to start one. Add "net/http" and "log" to your imports and
// the following code to your main function:
//
-// go func() {
-// log.Println(http.ListenAndServe("localhost:6060", nil))
-// }()
+// go func() {
+// log.Println(http.ListenAndServe("localhost:6060", nil))
+// }()
//
// If you are not using DefaultServeMux, you will have to register handlers
// with the mux you are using.
@@ -53,7 +54,6 @@
// For a study of the facility in action, visit
//
// https://blog.golang.org/2011/06/profiling-go-programs.html
-//
package pprof
import (
diff --git a/src/net/http/request.go b/src/net/http/request.go
index dbe947aec44..312211977d5 100644
--- a/src/net/http/request.go
+++ b/src/net/http/request.go
@@ -359,7 +359,6 @@ func (r *Request) WithContext(ctx context.Context) *Request {
r2 := new(Request)
*r2 = *r
r2.ctx = ctx
- r2.URL = cloneURL(r.URL) // legacy behavior; TODO: try to remove. Issue 23544
return r2
}
@@ -516,6 +515,7 @@ const defaultUserAgent = "Go-http-client/1.1"
// Write writes an HTTP/1.1 request, which is the header and body, in wire format.
// This method consults the following fields of the request:
+//
// Host
// URL
// Method (defaults to "GET")
@@ -739,9 +739,11 @@ func idnaASCII(v string) (string, error) {
// into Punycode form, if necessary.
//
// Ideally we'd clean the Host header according to the spec:
-// https://tools.ietf.org/html/rfc7230#section-5.4 (Host = uri-host [ ":" port ]")
-// https://tools.ietf.org/html/rfc7230#section-2.7 (uri-host -> rfc3986's host)
-// https://tools.ietf.org/html/rfc3986#section-3.2.2 (definition of host)
+//
+// https://tools.ietf.org/html/rfc7230#section-5.4 (Host = uri-host [ ":" port ]")
+// https://tools.ietf.org/html/rfc7230#section-2.7 (uri-host -> rfc3986's host)
+// https://tools.ietf.org/html/rfc3986#section-3.2.2 (definition of host)
+//
// But practically, what we are trying to avoid is the situation in
// issue 11206, where a malformed Host header used in the proxy context
// would create a bad request. So it is enough to just truncate at the
diff --git a/src/net/http/request_test.go b/src/net/http/request_test.go
index 4363e110333..d285840c1ce 100644
--- a/src/net/http/request_test.go
+++ b/src/net/http/request_test.go
@@ -998,23 +998,15 @@ func TestMaxBytesReaderDifferentLimits(t *testing.T) {
}
}
-func TestWithContextDeepCopiesURL(t *testing.T) {
+func TestWithContextNilURL(t *testing.T) {
req, err := NewRequest("POST", "https://golang.org/", nil)
if err != nil {
t.Fatal(err)
}
- reqCopy := req.WithContext(context.Background())
- reqCopy.URL.Scheme = "http"
-
- firstURL, secondURL := req.URL.String(), reqCopy.URL.String()
- if firstURL == secondURL {
- t.Errorf("unexpected change to original request's URL")
- }
-
- // And also check we don't crash on nil (Issue 20601)
+ // Issue 20601
req.URL = nil
- reqCopy = req.WithContext(context.Background())
+ reqCopy := req.WithContext(context.Background())
if reqCopy.URL != nil {
t.Error("expected nil URL in cloned request")
}
diff --git a/src/net/http/response.go b/src/net/http/response.go
index 297394eabeb..755c6965575 100644
--- a/src/net/http/response.go
+++ b/src/net/http/response.go
@@ -205,8 +205,11 @@ func ReadResponse(r *bufio.Reader, req *Request) (*Response, error) {
}
// RFC 7234, section 5.4: Should treat
+//
// Pragma: no-cache
+//
// like
+//
// Cache-Control: no-cache
func fixPragmaCacheControl(header Header) {
if hp, ok := header["Pragma"]; ok && len(hp) > 0 && hp[0] == "no-cache" {
@@ -228,24 +231,23 @@ func (r *Response) ProtoAtLeast(major, minor int) bool {
//
// This method consults the following fields of the response r:
//
-// StatusCode
-// ProtoMajor
-// ProtoMinor
-// Request.Method
-// TransferEncoding
-// Trailer
-// Body
-// ContentLength
-// Header, values for non-canonical keys will have unpredictable behavior
+// StatusCode
+// ProtoMajor
+// ProtoMinor
+// Request.Method
+// TransferEncoding
+// Trailer
+// Body
+// ContentLength
+// Header, values for non-canonical keys will have unpredictable behavior
//
// The Response Body is closed after it is sent.
func (r *Response) Write(w io.Writer) error {
// Status line
text := r.Status
if text == "" {
- var ok bool
- text, ok = statusText[r.StatusCode]
- if !ok {
+ text = StatusText(r.StatusCode)
+ if text == "" {
text = "status code " + strconv.Itoa(r.StatusCode)
}
} else {
diff --git a/src/net/http/serve_test.go b/src/net/http/serve_test.go
index fb18cb2c6f5..1c85a665997 100644
--- a/src/net/http/serve_test.go
+++ b/src/net/http/serve_test.go
@@ -4877,11 +4877,7 @@ func TestServerRequestContextCancel_ConnClose(t *testing.T) {
handlerDone := make(chan struct{})
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
close(inHandler)
- select {
- case <-r.Context().Done():
- case <-time.After(3 * time.Second):
- t.Errorf("timeout waiting for context to be done")
- }
+ <-r.Context().Done()
close(handlerDone)
}))
defer ts.Close()
@@ -4891,18 +4887,9 @@ func TestServerRequestContextCancel_ConnClose(t *testing.T) {
}
defer c.Close()
io.WriteString(c, "GET / HTTP/1.1\r\nHost: foo\r\n\r\n")
- select {
- case <-inHandler:
- case <-time.After(3 * time.Second):
- t.Fatalf("timeout waiting to see ServeHTTP get called")
- }
+ <-inHandler
c.Close() // this should trigger the context being done
-
- select {
- case <-handlerDone:
- case <-time.After(4 * time.Second):
- t.Fatalf("timeout waiting to see ServeHTTP exit")
- }
+ <-handlerDone
}
func TestServerContext_ServerContextKey_h1(t *testing.T) {
@@ -5077,10 +5064,11 @@ func benchmarkClientServerParallel(b *testing.B, parallelism int, useTLS bool) {
// The client code runs in a subprocess.
//
// For use like:
-// $ go test -c
-// $ ./http.test -test.run=XX -test.bench=BenchmarkServer -test.benchtime=15s -test.cpuprofile=http.prof
-// $ go tool pprof http.test http.prof
-// (pprof) web
+//
+// $ go test -c
+// $ ./http.test -test.run=XX -test.bench=BenchmarkServer -test.benchtime=15s -test.cpuprofile=http.prof
+// $ go tool pprof http.test http.prof
+// (pprof) web
func BenchmarkServer(b *testing.B) {
b.ReportAllocs()
// Child process mode;
diff --git a/src/net/http/server.go b/src/net/http/server.go
index b91069f9a16..d44b0fb2565 100644
--- a/src/net/http/server.go
+++ b/src/net/http/server.go
@@ -494,8 +494,9 @@ type response struct {
// prior to the headers being written. If the set of trailers is fixed
// or known before the header is written, the normal Go trailers mechanism
// is preferred:
-// https://pkg.go.dev/net/http#ResponseWriter
-// https://pkg.go.dev/net/http#example-ResponseWriter-Trailers
+//
+// https://pkg.go.dev/net/http#ResponseWriter
+// https://pkg.go.dev/net/http#example-ResponseWriter-Trailers
const TrailerPrefix = "Trailer:"
// finalTrailers is called after the Handler exits and returns a non-nil
@@ -1515,7 +1516,7 @@ func writeStatusLine(bw *bufio.Writer, is11 bool, code int, scratch []byte) {
} else {
bw.WriteString("HTTP/1.0 ")
}
- if text, ok := statusText[code]; ok {
+ if text := StatusText(code); text != "" {
bw.Write(strconv.AppendInt(scratch[:0], int64(code), 10))
bw.WriteByte(' ')
bw.WriteString(text)
@@ -1721,7 +1722,7 @@ type closeWriter interface {
var _ closeWriter = (*net.TCPConn)(nil)
// closeWrite flushes any outstanding data and sends a FIN packet (if
-// client is connected via TCP), signalling that we're done. We then
+// client is connected via TCP), signaling that we're done. We then
// pause for a bit, hoping the client processes it before any
// subsequent RST.
//
@@ -2101,7 +2102,7 @@ func Error(w ResponseWriter, error string, code int) {
func NotFound(w ResponseWriter, r *Request) { Error(w, "404 page not found", StatusNotFound) }
// NotFoundHandler returns a simple request handler
-// that replies to each request with a ``404 page not found'' reply.
+// that replies to each request with a “404 page not found” reply.
func NotFoundHandler() Handler { return HandlerFunc(NotFound) }
// StripPrefix returns a handler that serves HTTP requests by removing the
@@ -2191,7 +2192,7 @@ func Redirect(w ResponseWriter, r *Request, url string, code int) {
// Shouldn't send the body for POST or HEAD; that leaves GET.
if !hadCT && r.Method == "GET" {
- body := "<a href=\"" + htmlEscape(url) + "\">" + statusText[code] + "</a>.\n"
+ body := "<a href=\"" + htmlEscape(url) + "\">" + StatusText(code) + "</a>.\n"
fmt.Fprintln(w, body)
}
}
@@ -2394,7 +2395,7 @@ func (mux *ServeMux) shouldRedirectRLocked(host, path string) bool {
// the pattern that will match after following the redirect.
//
// If there is no registered handler that applies to the request,
-// Handler returns a ``page not found'' handler and an empty pattern.
+// Handler returns a “page not found” handler and an empty pattern.
func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {
// CONNECT requests are not canonicalized.
diff --git a/src/net/http/status.go b/src/net/http/status.go
index 286315f6395..75fea0ca35a 100644
--- a/src/net/http/status.go
+++ b/src/net/http/status.go
@@ -76,77 +76,135 @@ const (
StatusNetworkAuthenticationRequired = 511 // RFC 6585, 6
)
-var statusText = map[int]string{
- StatusContinue: "Continue",
- StatusSwitchingProtocols: "Switching Protocols",
- StatusProcessing: "Processing",
- StatusEarlyHints: "Early Hints",
-
- StatusOK: "OK",
- StatusCreated: "Created",
- StatusAccepted: "Accepted",
- StatusNonAuthoritativeInfo: "Non-Authoritative Information",
- StatusNoContent: "No Content",
- StatusResetContent: "Reset Content",
- StatusPartialContent: "Partial Content",
- StatusMultiStatus: "Multi-Status",
- StatusAlreadyReported: "Already Reported",
- StatusIMUsed: "IM Used",
-
- StatusMultipleChoices: "Multiple Choices",
- StatusMovedPermanently: "Moved Permanently",
- StatusFound: "Found",
- StatusSeeOther: "See Other",
- StatusNotModified: "Not Modified",
- StatusUseProxy: "Use Proxy",
- StatusTemporaryRedirect: "Temporary Redirect",
- StatusPermanentRedirect: "Permanent Redirect",
-
- StatusBadRequest: "Bad Request",
- StatusUnauthorized: "Unauthorized",
- StatusPaymentRequired: "Payment Required",
- StatusForbidden: "Forbidden",
- StatusNotFound: "Not Found",
- StatusMethodNotAllowed: "Method Not Allowed",
- StatusNotAcceptable: "Not Acceptable",
- StatusProxyAuthRequired: "Proxy Authentication Required",
- StatusRequestTimeout: "Request Timeout",
- StatusConflict: "Conflict",
- StatusGone: "Gone",
- StatusLengthRequired: "Length Required",
- StatusPreconditionFailed: "Precondition Failed",
- StatusRequestEntityTooLarge: "Request Entity Too Large",
- StatusRequestURITooLong: "Request URI Too Long",
- StatusUnsupportedMediaType: "Unsupported Media Type",
- StatusRequestedRangeNotSatisfiable: "Requested Range Not Satisfiable",
- StatusExpectationFailed: "Expectation Failed",
- StatusTeapot: "I'm a teapot",
- StatusMisdirectedRequest: "Misdirected Request",
- StatusUnprocessableEntity: "Unprocessable Entity",
- StatusLocked: "Locked",
- StatusFailedDependency: "Failed Dependency",
- StatusTooEarly: "Too Early",
- StatusUpgradeRequired: "Upgrade Required",
- StatusPreconditionRequired: "Precondition Required",
- StatusTooManyRequests: "Too Many Requests",
- StatusRequestHeaderFieldsTooLarge: "Request Header Fields Too Large",
- StatusUnavailableForLegalReasons: "Unavailable For Legal Reasons",
-
- StatusInternalServerError: "Internal Server Error",
- StatusNotImplemented: "Not Implemented",
- StatusBadGateway: "Bad Gateway",
- StatusServiceUnavailable: "Service Unavailable",
- StatusGatewayTimeout: "Gateway Timeout",
- StatusHTTPVersionNotSupported: "HTTP Version Not Supported",
- StatusVariantAlsoNegotiates: "Variant Also Negotiates",
- StatusInsufficientStorage: "Insufficient Storage",
- StatusLoopDetected: "Loop Detected",
- StatusNotExtended: "Not Extended",
- StatusNetworkAuthenticationRequired: "Network Authentication Required",
-}
-
// StatusText returns a text for the HTTP status code. It returns the empty
// string if the code is unknown.
func StatusText(code int) string {
- return statusText[code]
+ switch code {
+ case StatusContinue:
+ return "Continue"
+ case StatusSwitchingProtocols:
+ return "Switching Protocols"
+ case StatusProcessing:
+ return "Processing"
+ case StatusEarlyHints:
+ return "Early Hints"
+ case StatusOK:
+ return "OK"
+ case StatusCreated:
+ return "Created"
+ case StatusAccepted:
+ return "Accepted"
+ case StatusNonAuthoritativeInfo:
+ return "Non-Authoritative Information"
+ case StatusNoContent:
+ return "No Content"
+ case StatusResetContent:
+ return "Reset Content"
+ case StatusPartialContent:
+ return "Partial Content"
+ case StatusMultiStatus:
+ return "Multi-Status"
+ case StatusAlreadyReported:
+ return "Already Reported"
+ case StatusIMUsed:
+ return "IM Used"
+ case StatusMultipleChoices:
+ return "Multiple Choices"
+ case StatusMovedPermanently:
+ return "Moved Permanently"
+ case StatusFound:
+ return "Found"
+ case StatusSeeOther:
+ return "See Other"
+ case StatusNotModified:
+ return "Not Modified"
+ case StatusUseProxy:
+ return "Use Proxy"
+ case StatusTemporaryRedirect:
+ return "Temporary Redirect"
+ case StatusPermanentRedirect:
+ return "Permanent Redirect"
+ case StatusBadRequest:
+ return "Bad Request"
+ case StatusUnauthorized:
+ return "Unauthorized"
+ case StatusPaymentRequired:
+ return "Payment Required"
+ case StatusForbidden:
+ return "Forbidden"
+ case StatusNotFound:
+ return "Not Found"
+ case StatusMethodNotAllowed:
+ return "Method Not Allowed"
+ case StatusNotAcceptable:
+ return "Not Acceptable"
+ case StatusProxyAuthRequired:
+ return "Proxy Authentication Required"
+ case StatusRequestTimeout:
+ return "Request Timeout"
+ case StatusConflict:
+ return "Conflict"
+ case StatusGone:
+ return "Gone"
+ case StatusLengthRequired:
+ return "Length Required"
+ case StatusPreconditionFailed:
+ return "Precondition Failed"
+ case StatusRequestEntityTooLarge:
+ return "Request Entity Too Large"
+ case StatusRequestURITooLong:
+ return "Request URI Too Long"
+ case StatusUnsupportedMediaType:
+ return "Unsupported Media Type"
+ case StatusRequestedRangeNotSatisfiable:
+ return "Requested Range Not Satisfiable"
+ case StatusExpectationFailed:
+ return "Expectation Failed"
+ case StatusTeapot:
+ return "I'm a teapot"
+ case StatusMisdirectedRequest:
+ return "Misdirected Request"
+ case StatusUnprocessableEntity:
+ return "Unprocessable Entity"
+ case StatusLocked:
+ return "Locked"
+ case StatusFailedDependency:
+ return "Failed Dependency"
+ case StatusTooEarly:
+ return "Too Early"
+ case StatusUpgradeRequired:
+ return "Upgrade Required"
+ case StatusPreconditionRequired:
+ return "Precondition Required"
+ case StatusTooManyRequests:
+ return "Too Many Requests"
+ case StatusRequestHeaderFieldsTooLarge:
+ return "Request Header Fields Too Large"
+ case StatusUnavailableForLegalReasons:
+ return "Unavailable For Legal Reasons"
+ case StatusInternalServerError:
+ return "Internal Server Error"
+ case StatusNotImplemented:
+ return "Not Implemented"
+ case StatusBadGateway:
+ return "Bad Gateway"
+ case StatusServiceUnavailable:
+ return "Service Unavailable"
+ case StatusGatewayTimeout:
+ return "Gateway Timeout"
+ case StatusHTTPVersionNotSupported:
+ return "HTTP Version Not Supported"
+ case StatusVariantAlsoNegotiates:
+ return "Variant Also Negotiates"
+ case StatusInsufficientStorage:
+ return "Insufficient Storage"
+ case StatusLoopDetected:
+ return "Loop Detected"
+ case StatusNotExtended:
+ return "Not Extended"
+ case StatusNetworkAuthenticationRequired:
+ return "Network Authentication Required"
+ default:
+ return ""
+ }
}
diff --git a/src/net/http/transfer.go b/src/net/http/transfer.go
index 6d51178ee90..d9edf8c7259 100644
--- a/src/net/http/transfer.go
+++ b/src/net/http/transfer.go
@@ -196,10 +196,11 @@ func (t *transferWriter) shouldSendChunkedRequestBody() bool {
// headers before the pipe is fed data), we need to be careful and bound how
// long we wait for it. This delay will only affect users if all the following
// are true:
-// * the request body blocks
-// * the content length is not set (or set to -1)
-// * the method doesn't usually have a body (GET, HEAD, DELETE, ...)
-// * there is no transfer-encoding=chunked already set.
+// - the request body blocks
+// - the content length is not set (or set to -1)
+// - the method doesn't usually have a body (GET, HEAD, DELETE, ...)
+// - there is no transfer-encoding=chunked already set.
+//
// In other words, this delay will not normally affect anybody, and there
// are workarounds if it does.
func (t *transferWriter) probeRequestBody() {
diff --git a/src/net/http/transfer_test.go b/src/net/http/transfer_test.go
index f0c28b26299..5e0df896d80 100644
--- a/src/net/http/transfer_test.go
+++ b/src/net/http/transfer_test.go
@@ -267,7 +267,7 @@ func TestTransferWriterWriteBodyReaderTypes(t *testing.T) {
}
if tc.expectedReader != actualReader {
- t.Fatalf("got reader %T want %T", actualReader, tc.expectedReader)
+ t.Fatalf("got reader %s want %s", actualReader, tc.expectedReader)
}
}
diff --git a/src/net/http/transport_test.go b/src/net/http/transport_test.go
index 440d6b969b0..6fcb458296f 100644
--- a/src/net/http/transport_test.go
+++ b/src/net/http/transport_test.go
@@ -2099,17 +2099,21 @@ func TestTransportConcurrency(t *testing.T) {
for req := range reqs {
res, err := c.Get(ts.URL + "/?echo=" + req)
if err != nil {
- t.Errorf("error on req %s: %v", req, err)
+ if runtime.GOOS == "netbsd" && strings.HasSuffix(err.Error(), ": connection reset by peer") {
+ // https://go.dev/issue/52168: this test was observed to fail with
+ // ECONNRESET errors in Dial on various netbsd builders.
+ t.Logf("error on req %s: %v", req, err)
+ t.Logf("(see https://go.dev/issue/52168)")
+ } else {
+ t.Errorf("error on req %s: %v", req, err)
+ }
wg.Done()
continue
}
all, err := io.ReadAll(res.Body)
if err != nil {
t.Errorf("read error on req %s: %v", req, err)
- wg.Done()
- continue
- }
- if string(all) != req {
+ } else if string(all) != req {
t.Errorf("body of req %s = %q; want %q", req, all, req)
}
res.Body.Close()
@@ -3435,6 +3439,7 @@ func (c writerFuncConn) Write(p []byte) (n int, err error) { return c.write(p) }
// - we reused a keep-alive connection
// - we haven't yet received any header data
// - either we wrote no bytes to the server, or the request is idempotent
+//
// This automatically prevents an infinite resend loop because we'll run out of
// the cached keep-alive connections eventually.
func TestRetryRequestsOnError(t *testing.T) {
diff --git a/src/net/ipsock_posix.go b/src/net/ipsock_posix.go
index 2c724478481..9a961b96ab2 100644
--- a/src/net/ipsock_posix.go
+++ b/src/net/ipsock_posix.go
@@ -78,29 +78,29 @@ func (p *ipStackCapabilities) probe() {
// address family, both AF_INET and AF_INET6, and a wildcard address
// like the following:
//
-// - A listen for a wildcard communication domain, "tcp" or
-// "udp", with a wildcard address: If the platform supports
-// both IPv6 and IPv4-mapped IPv6 communication capabilities,
-// or does not support IPv4, we use a dual stack, AF_INET6 and
-// IPV6_V6ONLY=0, wildcard address listen. The dual stack
-// wildcard address listen may fall back to an IPv6-only,
-// AF_INET6 and IPV6_V6ONLY=1, wildcard address listen.
-// Otherwise we prefer an IPv4-only, AF_INET, wildcard address
-// listen.
+// - A listen for a wildcard communication domain, "tcp" or
+// "udp", with a wildcard address: If the platform supports
+// both IPv6 and IPv4-mapped IPv6 communication capabilities,
+// or does not support IPv4, we use a dual stack, AF_INET6 and
+// IPV6_V6ONLY=0, wildcard address listen. The dual stack
+// wildcard address listen may fall back to an IPv6-only,
+// AF_INET6 and IPV6_V6ONLY=1, wildcard address listen.
+// Otherwise we prefer an IPv4-only, AF_INET, wildcard address
+// listen.
//
-// - A listen for a wildcard communication domain, "tcp" or
-// "udp", with an IPv4 wildcard address: same as above.
+// - A listen for a wildcard communication domain, "tcp" or
+// "udp", with an IPv4 wildcard address: same as above.
//
-// - A listen for a wildcard communication domain, "tcp" or
-// "udp", with an IPv6 wildcard address: same as above.
+// - A listen for a wildcard communication domain, "tcp" or
+// "udp", with an IPv6 wildcard address: same as above.
//
-// - A listen for an IPv4 communication domain, "tcp4" or "udp4",
-// with an IPv4 wildcard address: We use an IPv4-only, AF_INET,
-// wildcard address listen.
+// - A listen for an IPv4 communication domain, "tcp4" or "udp4",
+// with an IPv4 wildcard address: We use an IPv4-only, AF_INET,
+// wildcard address listen.
//
-// - A listen for an IPv6 communication domain, "tcp6" or "udp6",
-// with an IPv6 wildcard address: We use an IPv6-only, AF_INET6
-// and IPV6_V6ONLY=1, wildcard address listen.
+// - A listen for an IPv6 communication domain, "tcp6" or "udp6",
+// with an IPv6 wildcard address: We use an IPv6-only, AF_INET6
+// and IPV6_V6ONLY=1, wildcard address listen.
//
// Otherwise guess: If the addresses are IPv4 then returns AF_INET,
// or else returns AF_INET6. It also returns a boolean value what
diff --git a/src/net/mac.go b/src/net/mac.go
index 373ac3d7e20..53d5b2dbf59 100644
--- a/src/net/mac.go
+++ b/src/net/mac.go
@@ -26,6 +26,7 @@ func (a HardwareAddr) String() string {
// ParseMAC parses s as an IEEE 802 MAC-48, EUI-48, EUI-64, or a 20-octet
// IP over InfiniBand link-layer address using one of the following formats:
+//
// 00:00:5e:00:53:01
// 02:00:5e:10:00:00:00:01
// 00:00:00:00:fe:80:00:00:00:00:00:00:02:00:5e:10:00:00:00:01
diff --git a/src/net/mail/message.go b/src/net/mail/message.go
index 61a3a26b012..c91aa3af124 100644
--- a/src/net/mail/message.go
+++ b/src/net/mail/message.go
@@ -8,12 +8,12 @@ Package mail implements parsing of mail messages.
For the most part, this package follows the syntax as specified by RFC 5322 and
extended by RFC 6532.
Notable divergences:
- * Obsolete address formats are not parsed, including addresses with
- embedded route information.
- * The full range of spacing (the CFWS syntax element) is not supported,
- such as breaking addresses across lines.
- * No unicode normalization is performed.
- * The special characters ()[]:;@\, are allowed to appear unquoted in names.
+ - Obsolete address formats are not parsed, including addresses with
+ embedded route information.
+ - The full range of spacing (the CFWS syntax element) is not supported,
+ such as breaking addresses across lines.
+ - No unicode normalization is performed.
+ - The special characters ()[]:;@\, are allowed to appear unquoted in names.
*/
package mail
diff --git a/src/net/net.go b/src/net/net.go
index ec718d5e432..7a97b9dcfd2 100644
--- a/src/net/net.go
+++ b/src/net/net.go
@@ -36,7 +36,7 @@ The Listen function creates servers:
go handleConnection(conn)
}
-Name Resolution
+# Name Resolution
The method for resolving domain names, whether indirectly with functions like Dial
or directly with functions like LookupHost and LookupAddr, varies by operating system.
@@ -74,7 +74,6 @@ join the two settings by a plus sign, as in GODEBUG=netdns=go+1.
On Plan 9, the resolver always accesses /net/cs and /net/dns.
On Windows, the resolver always uses C library functions, such as GetAddrInfo and DnsQuery.
-
*/
package net
diff --git a/src/net/netip/slow_test.go b/src/net/netip/slow_test.go
index 5b46a39a834..d7c80251641 100644
--- a/src/net/netip/slow_test.go
+++ b/src/net/netip/slow_test.go
@@ -21,22 +21,22 @@ var zeros = []string{"0", "0", "0", "0", "0", "0", "0", "0"}
// and against which we measure optimized parsers.
//
// parseIPSlow understands the following forms of IP addresses:
-// - Regular IPv4: 1.2.3.4
-// - IPv4 with many leading zeros: 0000001.0000002.0000003.0000004
-// - Regular IPv6: 1111:2222:3333:4444:5555:6666:7777:8888
-// - IPv6 with many leading zeros: 00000001:0000002:0000003:0000004:0000005:0000006:0000007:0000008
-// - IPv6 with zero blocks elided: 1111:2222::7777:8888
-// - IPv6 with trailing 32 bits expressed as IPv4: 1111:2222:3333:4444:5555:6666:77.77.88.88
+// - Regular IPv4: 1.2.3.4
+// - IPv4 with many leading zeros: 0000001.0000002.0000003.0000004
+// - Regular IPv6: 1111:2222:3333:4444:5555:6666:7777:8888
+// - IPv6 with many leading zeros: 00000001:0000002:0000003:0000004:0000005:0000006:0000007:0000008
+// - IPv6 with zero blocks elided: 1111:2222::7777:8888
+// - IPv6 with trailing 32 bits expressed as IPv4: 1111:2222:3333:4444:5555:6666:77.77.88.88
//
// It does not process the following IP address forms, which have been
// varyingly accepted by some programs due to an under-specification
// of the shapes of IPv4 addresses:
//
-// - IPv4 as a single 32-bit uint: 4660 (same as "1.2.3.4")
-// - IPv4 with octal numbers: 0300.0250.0.01 (same as "192.168.0.1")
-// - IPv4 with hex numbers: 0xc0.0xa8.0x0.0x1 (same as "192.168.0.1")
-// - IPv4 in "class-B style": 1.2.52 (same as "1.2.3.4")
-// - IPv4 in "class-A style": 1.564 (same as "1.2.3.4")
+// - IPv4 as a single 32-bit uint: 4660 (same as "1.2.3.4")
+// - IPv4 with octal numbers: 0300.0250.0.01 (same as "192.168.0.1")
+// - IPv4 with hex numbers: 0xc0.0xa8.0x0.0x1 (same as "192.168.0.1")
+// - IPv4 in "class-B style": 1.2.52 (same as "1.2.3.4")
+// - IPv4 in "class-A style": 1.564 (same as "1.2.3.4")
func parseIPSlow(s string) (Addr, error) {
// Identify and strip out the zone, if any. There should be 0 or 1
// '%' in the string.
@@ -94,13 +94,13 @@ func parseIPSlow(s string) (Addr, error) {
// function does not verify the contents of each field.
//
// This function performs two transformations:
-// - The last 32 bits of an IPv6 address may be represented in
-// IPv4-style dotted quad form, as in 1:2:3:4:5:6:7.8.9.10. That
-// address is transformed to its hex equivalent,
-// e.g. 1:2:3:4:5:6:708:90a.
-// - An address may contain one "::", which expands into as many
-// 16-bit blocks of zeros as needed to make the address its correct
-// full size. For example, fe80::1:2 expands to fe80:0:0:0:0:0:1:2.
+// - The last 32 bits of an IPv6 address may be represented in
+// IPv4-style dotted quad form, as in 1:2:3:4:5:6:7.8.9.10. That
+// address is transformed to its hex equivalent,
+// e.g. 1:2:3:4:5:6:708:90a.
+// - An address may contain one "::", which expands into as many
+// 16-bit blocks of zeros as needed to make the address its correct
+// full size. For example, fe80::1:2 expands to fe80:0:0:0:0:0:1:2.
//
// Both short forms may be present in a single address,
// e.g. fe80::1.2.3.4.
diff --git a/src/net/rpc/server.go b/src/net/rpc/server.go
index 0b3e6e3c58b..109ebba5413 100644
--- a/src/net/rpc/server.go
+++ b/src/net/rpc/server.go
@@ -13,11 +13,11 @@ objects of the same type.
Only methods that satisfy these criteria will be made available for remote access;
other methods will be ignored:
- - the method's type is exported.
- - the method is exported.
- - the method has two arguments, both exported (or builtin) types.
- - the method's second argument is a pointer.
- - the method has return type error.
+ - the method's type is exported.
+ - the method is exported.
+ - the method has two arguments, both exported (or builtin) types.
+ - the method's second argument is a pointer.
+ - the method has return type error.
In effect, the method must look schematically like
@@ -213,10 +213,11 @@ func isExportedOrBuiltinType(t reflect.Type) bool {
// Register publishes in the server the set of methods of the
// receiver value that satisfy the following conditions:
-// - exported method of exported type
-// - two arguments, both of exported type
-// - the second argument is a pointer
-// - one return value, of type error
+// - exported method of exported type
+// - two arguments, both of exported type
+// - the second argument is a pointer
+// - one return value, of type error
+//
// It returns an error if the receiver is not an exported type or has
// no suitable methods. It also logs the error using package log.
// The client accesses each method using a string of the form "Type.Method",
diff --git a/src/net/smtp/smtp.go b/src/net/smtp/smtp.go
index c1f00a04e1f..3bd2061b0c9 100644
--- a/src/net/smtp/smtp.go
+++ b/src/net/smtp/smtp.go
@@ -4,15 +4,17 @@
// Package smtp implements the Simple Mail Transfer Protocol as defined in RFC 5321.
// It also implements the following extensions:
+//
// 8BITMIME RFC 1652
// AUTH RFC 2554
// STARTTLS RFC 3207
+//
// Additional extensions may be handled by clients.
//
// The smtp package is frozen and is not accepting new features.
// Some external packages provide more functionality. See:
//
-// https://godoc.org/?q=smtp
+// https://godoc.org/?q=smtp
package smtp
import (
diff --git a/src/net/sock_linux.go b/src/net/sock_linux.go
index 9f62ed3deed..2513f9ba7b9 100644
--- a/src/net/sock_linux.go
+++ b/src/net/sock_linux.go
@@ -43,8 +43,8 @@ func kernelVersion() (major int, minor int) {
// Linux stores the backlog as:
//
-// - uint16 in kernel version < 4.1,
-// - uint32 in kernel version >= 4.1
+// - uint16 in kernel version < 4.1,
+// - uint32 in kernel version >= 4.1
//
// Truncate number to avoid wrapping.
//
diff --git a/src/net/textproto/reader.go b/src/net/textproto/reader.go
index ac47f007007..1f7afc57665 100644
--- a/src/net/textproto/reader.go
+++ b/src/net/textproto/reader.go
@@ -28,7 +28,6 @@ type Reader struct {
// should be reading from an io.LimitReader or similar Reader to bound
// the size of responses.
func NewReader(r *bufio.Reader) *Reader {
- commonHeaderOnce.Do(initCommonHeader)
return &Reader{R: r}
}
@@ -216,9 +215,12 @@ func parseCodeLine(line string, expectCode int) (code int, continued bool, messa
}
// ReadCodeLine reads a response code line of the form
+//
// code message
+//
// where code is a three-digit status code and the message
// extends to the rest of the line. An example of such a line is:
+//
// 220 plan9.bell-labs.com ESMTP
//
// If the prefix of the status does not match the digits in expectCode,
@@ -252,10 +254,10 @@ func (r *Reader) ReadCodeLine(expectCode int) (code int, message string, err err
// See page 36 of RFC 959 (https://www.ietf.org/rfc/rfc959.txt) for
// details of another form of response accepted:
//
-// code-message line 1
-// message line 2
-// ...
-// code message line n
+// code-message line 1
+// message line 2
+// ...
+// code message line n
//
// If the prefix of the status does not match the digits in expectCode,
// ReadResponse returns with err set to &Error{code, message}.
@@ -579,8 +581,6 @@ func (r *Reader) upcomingHeaderNewlines() (n int) {
// If s contains a space or invalid header field bytes, it is
// returned without modifications.
func CanonicalMIMEHeaderKey(s string) string {
- commonHeaderOnce.Do(initCommonHeader)
-
// Quick check for canonical encoding.
upper := true
for i := 0; i < len(s); i++ {
@@ -603,11 +603,12 @@ const toLower = 'a' - 'A'
// validHeaderFieldByte reports whether b is a valid byte in a header
// field name. RFC 7230 says:
-// header-field = field-name ":" OWS field-value OWS
-// field-name = token
-// tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." /
-// "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA
-// token = 1*tchar
+//
+// header-field = field-name ":" OWS field-value OWS
+// field-name = token
+// tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." /
+// "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA
+// token = 1*tchar
func validHeaderFieldByte(b byte) bool {
return int(b) < len(isTokenTable) && isTokenTable[b]
}
@@ -642,6 +643,7 @@ func canonicalMIMEHeaderKey(a []byte) string {
a[i] = c
upper = c == '-' // for next time
}
+ commonHeaderOnce.Do(initCommonHeader)
// The compiler recognizes m[string(byteSlice)] as a special
// case, so a copy of a's bytes into a new string does not
// happen in this map lookup:
diff --git a/src/net/textproto/reader_test.go b/src/net/textproto/reader_test.go
index 3124d438fa5..d11d40f1cf1 100644
--- a/src/net/textproto/reader_test.go
+++ b/src/net/textproto/reader_test.go
@@ -8,8 +8,10 @@ import (
"bufio"
"bytes"
"io"
+ "net"
"reflect"
"strings"
+ "sync"
"testing"
)
@@ -324,6 +326,33 @@ func TestCommonHeaders(t *testing.T) {
}
}
+func TestIssue46363(t *testing.T) {
+ // Regression test for data race reported in issue 46363:
+ // ReadMIMEHeader reads commonHeader before commonHeader has been initialized.
+ // Run this test with the race detector enabled to catch the reported data race.
+
+ // Reset commonHeaderOnce, so that commonHeader will have to be initialized
+ commonHeaderOnce = sync.Once{}
+ commonHeader = nil
+
+ // Test for data race by calling ReadMIMEHeader and CanonicalMIMEHeaderKey concurrently
+
+ // Send MIME header over net.Conn
+ r, w := net.Pipe()
+ go func() {
+ // ReadMIMEHeader calls canonicalMIMEHeaderKey, which reads from commonHeader
+ NewConn(r).ReadMIMEHeader()
+ }()
+ w.Write([]byte("A: 1\r\nB: 2\r\nC: 3\r\n\r\n"))
+
+ // CanonicalMIMEHeaderKey calls commonHeaderOnce.Do(initCommonHeader) which initializes commonHeader
+ CanonicalMIMEHeaderKey("a")
+
+ if commonHeader == nil {
+ t.Fatal("CanonicalMIMEHeaderKey should initialize commonHeader")
+ }
+}
+
var clientHeaders = strings.Replace(`Host: golang.org
Connection: keep-alive
Cache-Control: max-age=0
diff --git a/src/net/textproto/textproto.go b/src/net/textproto/textproto.go
index 3487a7dfaf4..70038d58886 100644
--- a/src/net/textproto/textproto.go
+++ b/src/net/textproto/textproto.go
@@ -22,7 +22,6 @@
//
// Conn, a convenient packaging of Reader, Writer, and Pipeline for use
// with a single network connection.
-//
package textproto
import (
diff --git a/src/net/url/url.go b/src/net/url/url.go
index ecfd1d9e94b..58b30411a4c 100644
--- a/src/net/url/url.go
+++ b/src/net/url/url.go
@@ -381,9 +381,9 @@ func User(username string) *Userinfo {
//
// This functionality should only be used with legacy web sites.
// RFC 2396 warns that interpreting Userinfo this way
-// ``is NOT RECOMMENDED, because the passing of authentication
+// “is NOT RECOMMENDED, because the passing of authentication
// information in clear text (such as URI) has proven to be a
-// security risk in almost every case where it has been used.''
+// security risk in almost every case where it has been used.”
func UserPassword(username, password string) *Userinfo {
return &Userinfo{username, password, true}
}
@@ -793,15 +793,15 @@ func validOptionalPort(port string) bool {
// To obtain the path, String uses u.EscapedPath().
//
// In the second form, the following rules apply:
-// - if u.Scheme is empty, scheme: is omitted.
-// - if u.User is nil, userinfo@ is omitted.
-// - if u.Host is empty, host/ is omitted.
-// - if u.Scheme and u.Host are empty and u.User is nil,
-// the entire scheme://userinfo@host/ is omitted.
-// - if u.Host is non-empty and u.Path begins with a /,
-// the form host/path does not add its own /.
-// - if u.RawQuery is empty, ?query is omitted.
-// - if u.Fragment is empty, #fragment is omitted.
+// - if u.Scheme is empty, scheme: is omitted.
+// - if u.User is nil, userinfo@ is omitted.
+// - if u.Host is empty, host/ is omitted.
+// - if u.Scheme and u.Host are empty and u.User is nil,
+// the entire scheme://userinfo@host/ is omitted.
+// - if u.Host is non-empty and u.Path begins with a /,
+// the form host/path does not add its own /.
+// - if u.RawQuery is empty, ?query is omitted.
+// - if u.Fragment is empty, #fragment is omitted.
func (u *URL) String() string {
var buf strings.Builder
if u.Scheme != "" {
@@ -960,7 +960,7 @@ func parseQuery(m Values, query string) (err error) {
return err
}
-// Encode encodes the values into ``URL encoded'' form
+// Encode encodes the values into “URL encoded” form
// ("bar=baz&foo=quux") sorted by key.
func (v Values) Encode() string {
if v == nil {
@@ -1189,21 +1189,29 @@ func (u *URL) UnmarshalBinary(text []byte) error {
// JoinPath returns a new URL with the provided path elements joined to
// any existing path and the resulting path cleaned of any ./ or ../ elements.
+// Any sequences of multiple / characters will be reduced to a single /.
func (u *URL) JoinPath(elem ...string) *URL {
url := *u
if len(elem) > 0 {
elem = append([]string{u.Path}, elem...)
- url.setPath(path.Join(elem...))
+ p := path.Join(elem...)
+ // path.Join will remove any trailing slashes.
+ // Preserve at least one.
+ if strings.HasSuffix(elem[len(elem)-1], "/") && !strings.HasSuffix(p, "/") {
+ p += "/"
+ }
+ url.setPath(p)
}
return &url
}
// validUserinfo reports whether s is a valid userinfo string per RFC 3986
// Section 3.2.1:
-// userinfo = *( unreserved / pct-encoded / sub-delims / ":" )
-// unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
-// sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
-// / "*" / "+" / "," / ";" / "="
+//
+// userinfo = *( unreserved / pct-encoded / sub-delims / ":" )
+// unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
+// sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
+// / "*" / "+" / "," / ";" / "="
//
// It doesn't validate pct-encoded. The caller does that via func unescape.
func validUserinfo(s string) bool {
diff --git a/src/net/url/url_test.go b/src/net/url/url_test.go
index 18aa5f8a1c5..478cc348727 100644
--- a/src/net/url/url_test.go
+++ b/src/net/url/url_test.go
@@ -2099,6 +2099,31 @@ func TestJoinPath(t *testing.T) {
base: "http://[fe80::1%en0]:8080/",
elem: []string{"/go"},
},
+ {
+ base: "https://go.googlesource.com",
+ elem: []string{"go/"},
+ out: "https://go.googlesource.com/go/",
+ },
+ {
+ base: "https://go.googlesource.com",
+ elem: []string{"go//"},
+ out: "https://go.googlesource.com/go/",
+ },
+ {
+ base: "https://go.googlesource.com",
+ elem: nil,
+ out: "https://go.googlesource.com",
+ },
+ {
+ base: "https://go.googlesource.com/",
+ elem: nil,
+ out: "https://go.googlesource.com/",
+ },
+ {
+ base: "/",
+ elem: nil,
+ out: "/",
+ },
}
for _, tt := range tests {
wantErr := "nil"
diff --git a/src/os/file.go b/src/os/file.go
index ea64a662cc2..ab017d4af79 100644
--- a/src/os/file.go
+++ b/src/os/file.go
@@ -37,7 +37,6 @@
// Note: The maximum number of concurrent operations on a File may be limited by
// the OS or the system. The number should be high, but exceeding it may degrade
// performance or cause other issues.
-//
package os
import (
diff --git a/src/os/file_plan9.go b/src/os/file_plan9.go
index 887e1c88923..93eb233e004 100644
--- a/src/os/file_plan9.go
+++ b/src/os/file_plan9.go
@@ -66,7 +66,7 @@ type dirInfo struct {
func epipecheck(file *File, e error) {
}
-// DevNull is the name of the operating system's ``null device.''
+// DevNull is the name of the operating system's “null device.”
// On Unix-like systems, it is "/dev/null"; on Windows, "NUL".
const DevNull = "/dev/null"
diff --git a/src/os/file_unix.go b/src/os/file_unix.go
index 666143b0de3..c30a6890de0 100644
--- a/src/os/file_unix.go
+++ b/src/os/file_unix.go
@@ -196,7 +196,7 @@ func epipecheck(file *File, e error) {
}
}
-// DevNull is the name of the operating system's ``null device.''
+// DevNull is the name of the operating system's “null device.”
// On Unix-like systems, it is "/dev/null"; on Windows, "NUL".
const DevNull = "/dev/null"
diff --git a/src/os/file_windows.go b/src/os/file_windows.go
index 75b4707eaf7..db5c27dd30f 100644
--- a/src/os/file_windows.go
+++ b/src/os/file_windows.go
@@ -95,7 +95,7 @@ type dirInfo struct {
func epipecheck(file *File, e error) {
}
-// DevNull is the name of the operating system's ``null device.''
+// DevNull is the name of the operating system's “null device.”
// On Unix-like systems, it is "/dev/null"; on Windows, "NUL".
const DevNull = "NUL"
@@ -402,9 +402,10 @@ func openSymlink(path string) (syscall.Handle, error) {
// DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, ...)
// into paths acceptable by all Windows APIs.
// For example, it converts
-// \??\C:\foo\bar into C:\foo\bar
-// \??\UNC\foo\bar into \\foo\bar
-// \??\Volume{abc}\ into C:\
+//
+// \??\C:\foo\bar into C:\foo\bar
+// \??\UNC\foo\bar into \\foo\bar
+// \??\Volume{abc}\ into C:\
func normaliseLinkPath(path string) (string, error) {
if len(path) < 4 || path[:4] != `\??\` {
// unexpected path, return it as is
diff --git a/src/os/pipe_test.go b/src/os/pipe_test.go
index 20716bce1e1..26565853e1f 100644
--- a/src/os/pipe_test.go
+++ b/src/os/pipe_test.go
@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file.
// Test broken pipes on Unix systems.
+//
//go:build !plan9 && !js
package os_test
diff --git a/src/os/rawconn_test.go b/src/os/rawconn_test.go
index fd2038a2332..62b99f87843 100644
--- a/src/os/rawconn_test.go
+++ b/src/os/rawconn_test.go
@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file.
// Test use of raw connections.
+//
//go:build !plan9 && !js
package os_test
diff --git a/src/os/signal/doc.go b/src/os/signal/doc.go
index 7af61d2d817..ab262edc58a 100644
--- a/src/os/signal/doc.go
+++ b/src/os/signal/doc.go
@@ -8,7 +8,7 @@ Package signal implements access to incoming signals.
Signals are primarily used on Unix-like systems. For the use of this
package on Windows and Plan 9, see below.
-Types of signals
+# Types of signals
The signals SIGKILL and SIGSTOP may not be caught by a program, and
therefore cannot be affected by this package.
@@ -33,7 +33,7 @@ by default is ^\ (Control-Backslash). In general you can cause a
program to simply exit by pressing ^C, and you can cause it to exit
with a stack dump by pressing ^\.
-Default behavior of signals in Go programs
+# Default behavior of signals in Go programs
By default, a synchronous signal is converted into a run-time panic. A
SIGHUP, SIGINT, or SIGTERM signal causes the program to exit. A
@@ -55,7 +55,7 @@ and, on Linux, signals 32 (SIGCANCEL) and 33 (SIGSETXID)
started by os.Exec, or by the os/exec package, will inherit the
modified signal mask.
-Changing the behavior of signals in Go programs
+# Changing the behavior of signals in Go programs
The functions in this package allow a program to change the way Go
programs handle signals.
@@ -88,7 +88,7 @@ for a blocked signal, it will be unblocked. If, later, Reset is
called for that signal, or Stop is called on all channels passed to
Notify for that signal, the signal will once again be blocked.
-SIGPIPE
+# SIGPIPE
When a Go program writes to a broken pipe, the kernel will raise a
SIGPIPE signal.
@@ -109,7 +109,7 @@ This means that, by default, command line programs will behave like
typical Unix command line programs, while other programs will not
crash with SIGPIPE when writing to a closed network connection.
-Go programs that use cgo or SWIG
+# Go programs that use cgo or SWIG
In a Go program that includes non-Go code, typically C/C++ code
accessed using cgo or SWIG, Go's startup code normally runs first. It
@@ -164,7 +164,7 @@ signal, and raises it again, to invoke any non-Go handler or default
system handler. If the program does not exit, the Go handler then
reinstalls itself and continues execution of the program.
-Non-Go programs that call Go code
+# Non-Go programs that call Go code
When Go code is built with options like -buildmode=c-shared, it will
be run as part of an existing non-Go program. The non-Go code may
@@ -201,7 +201,7 @@ non-Go thread, it will act as described above, except that if there is
an existing non-Go signal handler, that handler will be installed
before raising the signal.
-Windows
+# Windows
On Windows a ^C (Control-C) or ^BREAK (Control-Break) normally cause
the program to exit. If Notify is called for os.Interrupt, ^C or ^BREAK
@@ -217,11 +217,10 @@ CTRL_LOGOFF_EVENT or CTRL_SHUTDOWN_EVENT is received - the process will
still get terminated unless it exits. But receiving syscall.SIGTERM will
give the process an opportunity to clean up before termination.
-Plan 9
+# Plan 9
On Plan 9, signals have type syscall.Note, which is a string. Calling
Notify with a syscall.Note will cause that value to be sent on the
channel when that string is posted as a note.
-
*/
package signal
diff --git a/src/os/signal/signal_test.go b/src/os/signal/signal_test.go
index 3182e83b4e9..086ecdbcd5e 100644
--- a/src/os/signal/signal_test.go
+++ b/src/os/signal/signal_test.go
@@ -713,7 +713,7 @@ func TestNotifyContextNotifications(t *testing.T) {
}
wg.Wait()
<-ctx.Done()
- fmt.Print("received SIGINT")
+ fmt.Println("received SIGINT")
// Sleep to give time to simultaneous signals to reach the process.
// These signals must be ignored given stop() is not called on this code.
// We want to guarantee a SIGINT doesn't cause a premature termination of the program.
@@ -730,11 +730,17 @@ func TestNotifyContextNotifications(t *testing.T) {
{"multiple", 10},
}
for _, tc := range testCases {
+ tc := tc
t.Run(tc.name, func(t *testing.T) {
+ t.Parallel()
+
var subTimeout time.Duration
if deadline, ok := t.Deadline(); ok {
- subTimeout := time.Until(deadline)
- subTimeout -= subTimeout / 10 // Leave 10% headroom for cleaning up subprocess.
+ timeout := time.Until(deadline)
+ if timeout < 2*settleTime {
+ t.Fatalf("starting test with less than %v remaining", 2*settleTime)
+ }
+ subTimeout = timeout - (timeout / 10) // Leave 10% headroom for cleaning up subprocess.
}
args := []string{
@@ -750,7 +756,7 @@ func TestNotifyContextNotifications(t *testing.T) {
if err != nil {
t.Errorf("ran test with -check_notify_ctx_notification and it failed with %v.\nOutput:\n%s", err, out)
}
- if want := []byte("received SIGINT"); !bytes.Contains(out, want) {
+ if want := []byte("received SIGINT\n"); !bytes.Contains(out, want) {
t.Errorf("got %q, wanted %q", out, want)
}
})
diff --git a/src/os/stat_solaris.go b/src/os/stat_solaris.go
index 316c26c7cab..4e00ecb075f 100644
--- a/src/os/stat_solaris.go
+++ b/src/os/stat_solaris.go
@@ -9,6 +9,14 @@ import (
"time"
)
+// These constants aren't in the syscall package, which is frozen.
+// Values taken from golang.org/x/sys/unix.
+const (
+ _S_IFNAM = 0x5000
+ _S_IFDOOR = 0xd000
+ _S_IFPORT = 0xe000
+)
+
func fillFileStatFromSys(fs *fileStat, name string) {
fs.name = basename(name)
fs.size = fs.sys.Size
@@ -29,6 +37,8 @@ func fillFileStatFromSys(fs *fileStat, name string) {
// nothing to do
case syscall.S_IFSOCK:
fs.mode |= ModeSocket
+ case _S_IFNAM, _S_IFDOOR, _S_IFPORT:
+ fs.mode |= ModeIrregular
}
if fs.sys.Mode&syscall.S_ISGID != 0 {
fs.mode |= ModeSetgid
diff --git a/src/path/filepath/path.go b/src/path/filepath/path.go
index 0554deb2ffe..ec9e6d8a1f8 100644
--- a/src/path/filepath/path.go
+++ b/src/path/filepath/path.go
@@ -67,13 +67,13 @@ const (
// by purely lexical processing. It applies the following rules
// iteratively until no further processing can be done:
//
-// 1. Replace multiple Separator elements with a single one.
-// 2. Eliminate each . path name element (the current directory).
-// 3. Eliminate each inner .. path name element (the parent directory)
-// along with the non-.. element that precedes it.
-// 4. Eliminate .. elements that begin a rooted path:
-// that is, replace "/.." by "/" at the beginning of a path,
-// assuming Separator is '/'.
+// 1. Replace multiple Separator elements with a single one.
+// 2. Eliminate each . path name element (the current directory).
+// 3. Eliminate each inner .. path name element (the parent directory)
+// along with the non-.. element that precedes it.
+// 4. Eliminate .. elements that begin a rooted path:
+// that is, replace "/.." by "/" at the beginning of a path,
+// assuming Separator is '/'.
//
// The returned path ends in a slash only if it represents a root directory,
// such as "/" on Unix or `C:\` on Windows.
@@ -83,8 +83,8 @@ const (
// If the result of this process is an empty string, Clean
// returns the string ".".
//
-// See also Rob Pike, ``Lexical File Names in Plan 9 or
-// Getting Dot-Dot Right,''
+// See also Rob Pike, “Lexical File Names in Plan 9 or
+// Getting Dot-Dot Right,”
// https://9p.io/sys/doc/lexnames.html
func Clean(path string) string {
originalPath := path
diff --git a/src/path/filepath/path_windows_test.go b/src/path/filepath/path_windows_test.go
index 76a459ac96b..37019210fa0 100644
--- a/src/path/filepath/path_windows_test.go
+++ b/src/path/filepath/path_windows_test.go
@@ -197,13 +197,17 @@ func TestEvalSymlinksCanonicalNames(t *testing.T) {
// (where c: is vol parameter) to discover "8dot3 name creation state".
// The state is combination of 2 flags. The global flag controls if it
// is per volume or global setting:
-// 0 - Enable 8dot3 name creation on all volumes on the system
-// 1 - Disable 8dot3 name creation on all volumes on the system
-// 2 - Set 8dot3 name creation on a per volume basis
-// 3 - Disable 8dot3 name creation on all volumes except the system volume
+//
+// 0 - Enable 8dot3 name creation on all volumes on the system
+// 1 - Disable 8dot3 name creation on all volumes on the system
+// 2 - Set 8dot3 name creation on a per volume basis
+// 3 - Disable 8dot3 name creation on all volumes except the system volume
+//
// If global flag is set to 2, then per-volume flag needs to be examined:
-// 0 - Enable 8dot3 name creation on this volume
-// 1 - Disable 8dot3 name creation on this volume
+//
+// 0 - Enable 8dot3 name creation on this volume
+// 1 - Disable 8dot3 name creation on this volume
+//
// checkVolume8dot3Setting verifies that "8dot3 name creation" flags
// are set to 2 and 0, if enabled parameter is true, or 2 and 1, if enabled
// is false. Otherwise checkVolume8dot3Setting returns error.
diff --git a/src/path/filepath/symlink_windows.go b/src/path/filepath/symlink_windows.go
index d72279e2bbc..9a436d59780 100644
--- a/src/path/filepath/symlink_windows.go
+++ b/src/path/filepath/symlink_windows.go
@@ -49,11 +49,12 @@ func baseIsDotDot(path string) bool {
// toNorm returns the normalized path that is guaranteed to be unique.
// It should accept the following formats:
-// * UNC paths (e.g \\server\share\foo\bar)
-// * absolute paths (e.g C:\foo\bar)
-// * relative paths begin with drive letter (e.g C:foo\bar, C:..\foo\bar, C:.., C:.)
-// * relative paths begin with '\' (e.g \foo\bar)
-// * relative paths begin without '\' (e.g foo\bar, ..\foo\bar, .., .)
+// - UNC paths (e.g \\server\share\foo\bar)
+// - absolute paths (e.g C:\foo\bar)
+// - relative paths begin with drive letter (e.g C:foo\bar, C:..\foo\bar, C:.., C:.)
+// - relative paths begin with '\' (e.g \foo\bar)
+// - relative paths begin without '\' (e.g foo\bar, ..\foo\bar, .., .)
+//
// The returned normalized path will be in the same form (of 5 listed above) as the input path.
// If two paths A and B are indicating the same file with the same format, toNorm(A) should be equal to toNorm(B).
// The normBase parameter should be equal to the normBase func, except for in tests. See docs on the normBase func.
diff --git a/src/path/path.go b/src/path/path.go
index f1f3499f63e..547b9debce1 100644
--- a/src/path/path.go
+++ b/src/path/path.go
@@ -52,20 +52,20 @@ func (b *lazybuf) string() string {
// by purely lexical processing. It applies the following rules
// iteratively until no further processing can be done:
//
-// 1. Replace multiple slashes with a single slash.
-// 2. Eliminate each . path name element (the current directory).
-// 3. Eliminate each inner .. path name element (the parent directory)
-// along with the non-.. element that precedes it.
-// 4. Eliminate .. elements that begin a rooted path:
-// that is, replace "/.." by "/" at the beginning of a path.
+// 1. Replace multiple slashes with a single slash.
+// 2. Eliminate each . path name element (the current directory).
+// 3. Eliminate each inner .. path name element (the parent directory)
+// along with the non-.. element that precedes it.
+// 4. Eliminate .. elements that begin a rooted path:
+// that is, replace "/.." by "/" at the beginning of a path.
//
// The returned path ends in a slash only if it is the root "/".
//
// If the result of this process is an empty string, Clean
// returns the string ".".
//
-// See also Rob Pike, ``Lexical File Names in Plan 9 or
-// Getting Dot-Dot Right,''
+// See also Rob Pike, “Lexical File Names in Plan 9 or
+// Getting Dot-Dot Right,”
// https://9p.io/sys/doc/lexnames.html
func Clean(path string) string {
if path == "" {
diff --git a/src/plugin/plugin_dlopen.go b/src/plugin/plugin_dlopen.go
index c59f11ef719..6ba0f780657 100644
--- a/src/plugin/plugin_dlopen.go
+++ b/src/plugin/plugin_dlopen.go
@@ -150,5 +150,6 @@ var (
func lastmoduleinit() (pluginpath string, syms map[string]any, errstr string)
// doInit is defined in package runtime
+//
//go:linkname doInit runtime.doInit
func doInit(t unsafe.Pointer) // t should be a *runtime.initTask
diff --git a/src/reflect/abi_test.go b/src/reflect/abi_test.go
index c9a4cd1c8e3..9d934727790 100644
--- a/src/reflect/abi_test.go
+++ b/src/reflect/abi_test.go
@@ -545,6 +545,7 @@ func passEmptyStruct(a int, b struct{}, c float64) (int, struct{}, float64) {
// This test case forces a large argument to the stack followed by more
// in-register arguments.
+//
//go:registerparams
//go:noinline
func passStruct10AndSmall(a Struct10, b byte, c uint) (Struct10, byte, uint) {
diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go
index 5a35d98b51f..a886f9f64ab 100644
--- a/src/reflect/all_test.go
+++ b/src/reflect/all_test.go
@@ -11,6 +11,7 @@ import (
"fmt"
"go/token"
"internal/goarch"
+ "internal/testenv"
"io"
"math"
"math/rand"
@@ -363,6 +364,10 @@ func TestMapIterSet(t *testing.T) {
}
}
+ if strings.HasSuffix(testenv.Builder(), "-noopt") {
+ return // no inlining with the noopt builder
+ }
+
got := int(testing.AllocsPerRun(10, func() {
iter := v.MapRange()
for iter.Next() {
@@ -370,9 +375,12 @@ func TestMapIterSet(t *testing.T) {
e.SetIterValue(iter)
}
}))
- // Making a *MapIter allocates. This should be the only allocation.
- if got != 1 {
- t.Errorf("wanted 1 alloc, got %d", got)
+ // Calling MapRange should not allocate even though it returns a *MapIter.
+ // The function is inlineable, so if the local usage does not escape
+ // the *MapIter, it can remain stack allocated.
+ want := 0
+ if got != want {
+ t.Errorf("wanted %d alloc, got %d", want, got)
}
}
diff --git a/src/reflect/deepequal.go b/src/reflect/deepequal.go
index eaab1012215..50b436e5f6e 100644
--- a/src/reflect/deepequal.go
+++ b/src/reflect/deepequal.go
@@ -174,7 +174,7 @@ func deepValueEqual(v1, v2 Value, visited map[visit]bool) bool {
}
}
-// DeepEqual reports whether x and y are ``deeply equal,'' defined as follows.
+// DeepEqual reports whether x and y are “deeply equal,” defined as follows.
// Two values of identical type are deeply equal if one of the following cases applies.
// Values of distinct types are never deeply equal.
//
diff --git a/src/reflect/makefunc.go b/src/reflect/makefunc.go
index 3d9279ceaa6..ee0729903e3 100644
--- a/src/reflect/makefunc.go
+++ b/src/reflect/makefunc.go
@@ -26,9 +26,9 @@ type makeFuncImpl struct {
// that wraps the function fn. When called, that new function
// does the following:
//
-// - converts its arguments to a slice of Values.
-// - runs results := fn(args).
-// - returns the results as a slice of Values, one per formal result.
+// - converts its arguments to a slice of Values.
+// - runs results := fn(args).
+// - returns the results as a slice of Values, one per formal result.
//
// The implementation fn can assume that the argument Value slice
// has the number and type of arguments given by typ.
@@ -158,6 +158,7 @@ type makeFuncCtxt struct {
// nosplit because pointers are being held in uintptr slots in args, so
// having our stack scanned now could lead to accidentally freeing
// memory.
+//
//go:nosplit
func moveMakeFuncArgPtrs(ctxt *makeFuncCtxt, args *abi.RegArgs) {
for i, arg := range args.Ints {
diff --git a/src/reflect/type.go b/src/reflect/type.go
index 53c17f9e552..e8882664750 100644
--- a/src/reflect/type.go
+++ b/src/reflect/type.go
@@ -277,6 +277,7 @@ const Ptr = Pointer
// available in the memory directly following the rtype value.
//
// tflag values must be kept in sync with copies in:
+//
// cmd/compile/internal/reflectdata/reflect.go
// cmd/link/internal/ld/decodesym.go
// runtime/type.go
diff --git a/src/reflect/value.go b/src/reflect/value.go
index f1454b8ae22..6fe3cee0171 100644
--- a/src/reflect/value.go
+++ b/src/reflect/value.go
@@ -1437,7 +1437,9 @@ func (v Value) CanInterface() bool {
// Interface returns v's current value as an interface{}.
// It is equivalent to:
+//
// var i interface{} = (v's underlying value)
+//
// It panics if the Value was obtained by accessing
// unexported struct fields.
func (v Value) Interface() (i any) {
@@ -1825,16 +1827,26 @@ func (iter *MapIter) Reset(v Value) {
// Example:
//
// iter := reflect.ValueOf(m).MapRange()
-// for iter.Next() {
+// for iter.Next() {
// k := iter.Key()
// v := iter.Value()
// ...
// }
func (v Value) MapRange() *MapIter {
- v.mustBe(Map)
+ // This is inlinable to take advantage of "function outlining".
+ // The allocation of MapIter can be stack allocated if the caller
+ // does not allow it to escape.
+ // See https://blog.filippo.io/efficient-go-apis-with-the-inliner/
+ if v.kind() != Map {
+ v.panicNotMap()
+ }
return &MapIter{m: v}
}
+func (f flag) panicNotMap() {
+ f.mustBe(Map)
+}
+
// copyVal returns a Value containing the map key or value at ptr,
// allocating a new variable as needed.
func copyVal(typ *rtype, fl flag, ptr unsafe.Pointer) Value {
@@ -2463,12 +2475,17 @@ func (v Value) TrySend(x Value) bool {
// Type returns v's type.
func (v Value) Type() Type {
- f := v.flag
- if f == 0 {
+ if v.flag != 0 && v.flag&flagMethod == 0 {
+ return v.typ
+ }
+ return v.typeSlow()
+}
+
+func (v Value) typeSlow() Type {
+ if v.flag == 0 {
panic(&ValueError{"reflect.Value.Type", Invalid})
}
- if f&flagMethod == 0 {
- // Easy case
+ if v.flag&flagMethod == 0 {
return v.typ
}
@@ -2757,6 +2774,7 @@ type runtimeSelect struct {
// If the case was a receive, val is filled in with the received value.
// The conventional OK bool indicates whether the receive corresponds
// to a sent value.
+//
//go:noescape
func rselect([]runtimeSelect) (chosen int, recvOK bool)
@@ -3493,6 +3511,7 @@ func maplen(m unsafe.Pointer) int
// Arguments passed through to call do not escape. The type is used only in a
// very limited callee of call, the stackArgs are copied, and regArgs is only
// used in the call frame.
+//
//go:noescape
//go:linkname call runtime.reflectcall
func call(stackArgsType *rtype, f, stackArgs unsafe.Pointer, stackArgsSize, stackRetOffset, frameSize uint32, regArgs *abi.RegArgs)
@@ -3500,29 +3519,35 @@ func call(stackArgsType *rtype, f, stackArgs unsafe.Pointer, stackArgsSize, stac
func ifaceE2I(t *rtype, src any, dst unsafe.Pointer)
// memmove copies size bytes to dst from src. No write barriers are used.
+//
//go:noescape
func memmove(dst, src unsafe.Pointer, size uintptr)
// typedmemmove copies a value of type t to dst from src.
+//
//go:noescape
func typedmemmove(t *rtype, dst, src unsafe.Pointer)
// typedmemmovepartial is like typedmemmove but assumes that
// dst and src point off bytes into the value and only copies size bytes.
+//
//go:noescape
func typedmemmovepartial(t *rtype, dst, src unsafe.Pointer, off, size uintptr)
// typedmemclr zeros the value at ptr of type t.
+//
//go:noescape
func typedmemclr(t *rtype, ptr unsafe.Pointer)
// typedmemclrpartial is like typedmemclr but assumes that
// dst points off bytes into the value and only clears size bytes.
+//
//go:noescape
func typedmemclrpartial(t *rtype, ptr unsafe.Pointer, off, size uintptr)
// typedslicecopy copies a slice of elemType values from src to dst,
// returning the number of elements copied.
+//
//go:noescape
func typedslicecopy(elemType *rtype, dst, src unsafeheader.Slice) int
diff --git a/src/reflect/visiblefields_test.go b/src/reflect/visiblefields_test.go
index fdedc21f738..66d545dd1f7 100644
--- a/src/reflect/visiblefields_test.go
+++ b/src/reflect/visiblefields_test.go
@@ -78,7 +78,7 @@ var fieldsTests = []struct {
index: []int{0, 1},
}},
}, {
- testName: "TwoEmbeddedStructsWithCancellingMembers",
+ testName: "TwoEmbeddedStructsWithCancelingMembers",
val: struct {
SFG
SF
diff --git a/src/regexp/exec_test.go b/src/regexp/exec_test.go
index a6e833050bb..16942303451 100644
--- a/src/regexp/exec_test.go
+++ b/src/regexp/exec_test.go
@@ -52,8 +52,8 @@ import (
// submatch indices. An unmatched subexpression formats
// its pair as a single - (not illustrated above). For now
// each regexp run produces two match results, one for a
-// ``full match'' that restricts the regexp to matching the entire
-// string or nothing, and one for a ``partial match'' that gives
+// “full match” that restricts the regexp to matching the entire
+// string or nothing, and one for a “partial match” that gives
// the leftmost first match found in the string.
//
// Lines beginning with # are comments. Lines beginning with
diff --git a/src/regexp/regexp.go b/src/regexp/regexp.go
index 26ac5f48b23..7958a397285 100644
--- a/src/regexp/regexp.go
+++ b/src/regexp/regexp.go
@@ -9,14 +9,17 @@
// More precisely, it is the syntax accepted by RE2 and described at
// https://golang.org/s/re2syntax, except for \C.
// For an overview of the syntax, run
-// go doc regexp/syntax
+//
+// go doc regexp/syntax
//
// The regexp implementation provided by this package is
// guaranteed to run in time linear in the size of the input.
// (This is a property not guaranteed by most open source
// implementations of regular expressions.) For more information
// about this property, see
+//
// https://swtch.com/~rsc/regexp/regexp1.html
+//
// or any book about automata theory.
//
// All characters are UTF-8-encoded code points.
@@ -64,7 +67,6 @@
// before returning.
//
// (There are a few other methods that do not match this pattern.)
-//
package regexp
import (
@@ -1239,13 +1241,15 @@ func (re *Regexp) FindAllStringSubmatchIndex(s string, n int) [][]int {
// that contains no metacharacters, it is equivalent to strings.SplitN.
//
// Example:
-// s := regexp.MustCompile("a*").Split("abaabaccadaaae", 5)
-// // s: ["", "b", "b", "c", "cadaaae"]
+//
+// s := regexp.MustCompile("a*").Split("abaabaccadaaae", 5)
+// // s: ["", "b", "b", "c", "cadaaae"]
//
// The count determines the number of substrings to return:
-// n > 0: at most n substrings; the last substring will be the unsplit remainder.
-// n == 0: the result is nil (zero substrings)
-// n < 0: all substrings
+//
+// n > 0: at most n substrings; the last substring will be the unsplit remainder.
+// n == 0: the result is nil (zero substrings)
+// n < 0: all substrings
func (re *Regexp) Split(s string, n int) []string {
if n == 0 {
diff --git a/src/regexp/syntax/doc.go b/src/regexp/syntax/doc.go
index b3f9136b5f3..f6a4b43f7ae 100644
--- a/src/regexp/syntax/doc.go
+++ b/src/regexp/syntax/doc.go
@@ -9,123 +9,132 @@ Package syntax parses regular expressions into parse trees and compiles
parse trees into programs. Most clients of regular expressions will use the
facilities of package regexp (such as Compile and Match) instead of this package.
-Syntax
+# Syntax
The regular expression syntax understood by this package when parsing with the Perl flag is as follows.
Parts of the syntax can be disabled by passing alternate flags to Parse.
-
Single characters:
- . any character, possibly including newline (flag s=true)
- [xyz] character class
- [^xyz] negated character class
- \d Perl character class
- \D negated Perl character class
- [[:alpha:]] ASCII character class
- [[:^alpha:]] negated ASCII character class
- \pN Unicode character class (one-letter name)
- \p{Greek} Unicode character class
- \PN negated Unicode character class (one-letter name)
- \P{Greek} negated Unicode character class
+
+ . any character, possibly including newline (flag s=true)
+ [xyz] character class
+ [^xyz] negated character class
+ \d Perl character class
+ \D negated Perl character class
+ [[:alpha:]] ASCII character class
+ [[:^alpha:]] negated ASCII character class
+ \pN Unicode character class (one-letter name)
+ \p{Greek} Unicode character class
+ \PN negated Unicode character class (one-letter name)
+ \P{Greek} negated Unicode character class
Composites:
- xy x followed by y
- x|y x or y (prefer x)
+
+ xy x followed by y
+ x|y x or y (prefer x)
Repetitions:
- x* zero or more x, prefer more
- x+ one or more x, prefer more
- x? zero or one x, prefer one
- x{n,m} n or n+1 or ... or m x, prefer more
- x{n,} n or more x, prefer more
- x{n} exactly n x
- x*? zero or more x, prefer fewer
- x+? one or more x, prefer fewer
- x?? zero or one x, prefer zero
- x{n,m}? n or n+1 or ... or m x, prefer fewer
- x{n,}? n or more x, prefer fewer
- x{n}? exactly n x
+
+ x* zero or more x, prefer more
+ x+ one or more x, prefer more
+ x? zero or one x, prefer one
+ x{n,m} n or n+1 or ... or m x, prefer more
+ x{n,} n or more x, prefer more
+ x{n} exactly n x
+ x*? zero or more x, prefer fewer
+ x+? one or more x, prefer fewer
+ x?? zero or one x, prefer zero
+ x{n,m}? n or n+1 or ... or m x, prefer fewer
+ x{n,}? n or more x, prefer fewer
+ x{n}? exactly n x
Implementation restriction: The counting forms x{n,m}, x{n,}, and x{n}
reject forms that create a minimum or maximum repetition count above 1000.
Unlimited repetitions are not subject to this restriction.
Grouping:
- (re) numbered capturing group (submatch)
- (?P<name>re) named & numbered capturing group (submatch)
- (?:re) non-capturing group
- (?flags) set flags within current group; non-capturing
- (?flags:re) set flags during re; non-capturing
- Flag syntax is xyz (set) or -xyz (clear) or xy-z (set xy, clear z). The flags are:
+ (re) numbered capturing group (submatch)
+ (?P<name>re) named & numbered capturing group (submatch)
+ (?:re) non-capturing group
+ (?flags) set flags within current group; non-capturing
+ (?flags:re) set flags during re; non-capturing
+
+ Flag syntax is xyz (set) or -xyz (clear) or xy-z (set xy, clear z). The flags are:
- i case-insensitive (default false)
- m multi-line mode: ^ and $ match begin/end line in addition to begin/end text (default false)
- s let . match \n (default false)
- U ungreedy: swap meaning of x* and x*?, x+ and x+?, etc (default false)
+ i case-insensitive (default false)
+ m multi-line mode: ^ and $ match begin/end line in addition to begin/end text (default false)
+ s let . match \n (default false)
+ U ungreedy: swap meaning of x* and x*?, x+ and x+?, etc (default false)
Empty strings:
- ^ at beginning of text or line (flag m=true)
- $ at end of text (like \z not \Z) or line (flag m=true)
- \A at beginning of text
- \b at ASCII word boundary (\w on one side and \W, \A, or \z on the other)
- \B not at ASCII word boundary
- \z at end of text
+
+ ^ at beginning of text or line (flag m=true)
+ $ at end of text (like \z not \Z) or line (flag m=true)
+ \A at beginning of text
+ \b at ASCII word boundary (\w on one side and \W, \A, or \z on the other)
+ \B not at ASCII word boundary
+ \z at end of text
Escape sequences:
- \a bell (== \007)
- \f form feed (== \014)
- \t horizontal tab (== \011)
- \n newline (== \012)
- \r carriage return (== \015)
- \v vertical tab character (== \013)
- \* literal *, for any punctuation character *
- \123 octal character code (up to three digits)
- \x7F hex character code (exactly two digits)
- \x{10FFFF} hex character code
- \Q...\E literal text ... even if ... has punctuation
+
+ \a bell (== \007)
+ \f form feed (== \014)
+ \t horizontal tab (== \011)
+ \n newline (== \012)
+ \r carriage return (== \015)
+ \v vertical tab character (== \013)
+ \* literal *, for any punctuation character *
+ \123 octal character code (up to three digits)
+ \x7F hex character code (exactly two digits)
+ \x{10FFFF} hex character code
+ \Q...\E literal text ... even if ... has punctuation
Character class elements:
- x single character
- A-Z character range (inclusive)
- \d Perl character class
- [:foo:] ASCII character class foo
- \p{Foo} Unicode character class Foo
- \pF Unicode character class F (one-letter name)
+
+ x single character
+ A-Z character range (inclusive)
+ \d Perl character class
+ [:foo:] ASCII character class foo
+ \p{Foo} Unicode character class Foo
+ \pF Unicode character class F (one-letter name)
Named character classes as character class elements:
- [\d] digits (== \d)
- [^\d] not digits (== \D)
- [\D] not digits (== \D)
- [^\D] not not digits (== \d)
- [[:name:]] named ASCII class inside character class (== [:name:])
- [^[:name:]] named ASCII class inside negated character class (== [:^name:])
- [\p{Name}] named Unicode property inside character class (== \p{Name})
- [^\p{Name}] named Unicode property inside negated character class (== \P{Name})
+
+ [\d] digits (== \d)
+ [^\d] not digits (== \D)
+ [\D] not digits (== \D)
+ [^\D] not not digits (== \d)
+ [[:name:]] named ASCII class inside character class (== [:name:])
+ [^[:name:]] named ASCII class inside negated character class (== [:^name:])
+ [\p{Name}] named Unicode property inside character class (== \p{Name})
+ [^\p{Name}] named Unicode property inside negated character class (== \P{Name})
Perl character classes (all ASCII-only):
- \d digits (== [0-9])
- \D not digits (== [^0-9])
- \s whitespace (== [\t\n\f\r ])
- \S not whitespace (== [^\t\n\f\r ])
- \w word characters (== [0-9A-Za-z_])
- \W not word characters (== [^0-9A-Za-z_])
+
+ \d digits (== [0-9])
+ \D not digits (== [^0-9])
+ \s whitespace (== [\t\n\f\r ])
+ \S not whitespace (== [^\t\n\f\r ])
+ \w word characters (== [0-9A-Za-z_])
+ \W not word characters (== [^0-9A-Za-z_])
ASCII character classes:
- [[:alnum:]] alphanumeric (== [0-9A-Za-z])
- [[:alpha:]] alphabetic (== [A-Za-z])
- [[:ascii:]] ASCII (== [\x00-\x7F])
- [[:blank:]] blank (== [\t ])
- [[:cntrl:]] control (== [\x00-\x1F\x7F])
- [[:digit:]] digits (== [0-9])
- [[:graph:]] graphical (== [!-~] == [A-Za-z0-9!"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~])
- [[:lower:]] lower case (== [a-z])
- [[:print:]] printable (== [ -~] == [ [:graph:]])
- [[:punct:]] punctuation (== [!-/:-@[-`{-~])
- [[:space:]] whitespace (== [\t\n\v\f\r ])
- [[:upper:]] upper case (== [A-Z])
- [[:word:]] word characters (== [0-9A-Za-z_])
- [[:xdigit:]] hex digit (== [0-9A-Fa-f])
+
+ [[:alnum:]] alphanumeric (== [0-9A-Za-z])
+ [[:alpha:]] alphabetic (== [A-Za-z])
+ [[:ascii:]] ASCII (== [\x00-\x7F])
+ [[:blank:]] blank (== [\t ])
+ [[:cntrl:]] control (== [\x00-\x1F\x7F])
+ [[:digit:]] digits (== [0-9])
+ [[:graph:]] graphical (== [!-~] == [A-Za-z0-9!"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~])
+ [[:lower:]] lower case (== [a-z])
+ [[:print:]] printable (== [ -~] == [ [:graph:]])
+ [[:punct:]] punctuation (== [!-/:-@[-`{-~])
+ [[:space:]] whitespace (== [\t\n\v\f\r ])
+ [[:upper:]] upper case (== [A-Z])
+ [[:word:]] word characters (== [0-9A-Za-z_])
+ [[:xdigit:]] hex digit (== [0-9A-Fa-f])
Unicode character classes are those in unicode.Categories and unicode.Scripts.
*/
diff --git a/src/regexp/syntax/parse.go b/src/regexp/syntax/parse.go
index ebf8e11915d..cfb703d2855 100644
--- a/src/regexp/syntax/parse.go
+++ b/src/regexp/syntax/parse.go
@@ -445,11 +445,16 @@ func (p *parser) collapse(subs []*Regexp, op Op) *Regexp {
// frees (passes to p.reuse) any removed *Regexps.
//
// For example,
-// ABC|ABD|AEF|BCX|BCY
+//
+// ABC|ABD|AEF|BCX|BCY
+//
// simplifies by literal prefix extraction to
-// A(B(C|D)|EF)|BC(X|Y)
+//
+// A(B(C|D)|EF)|BC(X|Y)
+//
// which simplifies by character class introduction to
-// A(B[CD]|EF)|BC[XY]
+//
+// A(B[CD]|EF)|BC[XY]
func (p *parser) factor(sub []*Regexp) []*Regexp {
if len(sub) < 2 {
return sub
diff --git a/src/regexp/syntax/prog.go b/src/regexp/syntax/prog.go
index 8583f55e542..ee71decb35f 100644
--- a/src/regexp/syntax/prog.go
+++ b/src/regexp/syntax/prog.go
@@ -102,7 +102,7 @@ func EmptyOpContext(r1, r2 rune) EmptyOp {
return op
}
-// IsWordChar reports whether r is consider a ``word character''
+// IsWordChar reports whether r is consider a “word character”
// during the evaluation of the \b and \B zero-width assertions.
// These assertions are ASCII-only: the word characters are [A-Za-z0-9_].
func IsWordChar(r rune) bool {
diff --git a/src/run.bash b/src/run.bash
index 2123c509f8b..99b09fcbde9 100755
--- a/src/run.bash
+++ b/src/run.bash
@@ -21,14 +21,10 @@ if [ ! -f ../bin/go ]; then
exit 1
fi
-eval $(../bin/go env)
+eval $(../bin/go tool dist env)
export GOROOT # The api test requires GOROOT to be set, so set it to match ../bin/go.
-export GOPATH=/nonexist-gopath
unset CDPATH # in case user has it set
-export GOBIN=$GOROOT/bin # Issue 14340
-unset GOFLAGS
-unset GO111MODULE
export GOHOSTOS
export CC
@@ -53,4 +49,5 @@ if ulimit -T &> /dev/null; then
[ "$(ulimit -H -T)" = "unlimited" ] || ulimit -S -T $(ulimit -H -T)
fi
+export GOPATH=/nonexist-gopath
exec ../bin/go tool dist test -rebuild "$@"
diff --git a/src/run.bat b/src/run.bat
index edcaf526593..b4bab85a932 100644
--- a/src/run.bat
+++ b/src/run.bat
@@ -18,32 +18,21 @@ setlocal
set GOBUILDFAIL=0
-set GOPATH=c:\nonexist-gopath
-:: Issue 14340: ignore GOBIN during all.bat.
-set GOBIN=
-set GOFLAGS=
-set GO111MODULE=
+..\bin\go tool dist env > env.bat
+if errorlevel 1 goto fail
+call env.bat
+del env.bat
-rem TODO avoid rebuild if possible
+set GOPATH=c:\nonexist-gopath
if x%1==x--no-rebuild goto norebuild
-echo ##### Building packages and commands.
-..\bin\go install -a -v std cmd
+..\bin\go tool dist test --rebuild
if errorlevel 1 goto fail
-echo.
-:norebuild
-
-:: get CGO_ENABLED
-..\bin\go env > env.bat
-if errorlevel 1 goto fail
-call env.bat
-del env.bat
-echo.
+goto end
+:norebuild
..\bin\go tool dist test
if errorlevel 1 goto fail
-echo.
-
goto end
:fail
diff --git a/src/run.rc b/src/run.rc
index a7b48012070..2a0bb7f7a1f 100755
--- a/src/run.rc
+++ b/src/run.rc
@@ -10,11 +10,7 @@ if(! test -f ../bin/go){
exit wrongdir
}
-eval `{../bin/go env}
+eval `{../bin/go tool dist env}
GOPATH=/nonexist-gopath
-GOBIN=() # Issue 14340
-GOFLAGS=()
-GO111MODULE=()
-
exec ../bin/go tool dist test -rebuild $*
diff --git a/src/runtime/asan.go b/src/runtime/asan.go
index 5f1e6370d2f..8c41e418f7c 100644
--- a/src/runtime/asan.go
+++ b/src/runtime/asan.go
@@ -56,6 +56,7 @@ func asanunpoison(addr unsafe.Pointer, sz uintptr)
func asanpoison(addr unsafe.Pointer, sz uintptr)
// These are called from asan_GOARCH.s
+//
//go:cgo_import_static __asan_read_go
//go:cgo_import_static __asan_write_go
//go:cgo_import_static __asan_unpoison_go
diff --git a/src/runtime/callers_test.go b/src/runtime/callers_test.go
index 3cf3fbe5acf..d245cbd2d2f 100644
--- a/src/runtime/callers_test.go
+++ b/src/runtime/callers_test.go
@@ -309,3 +309,33 @@ func TestCallersDeferNilFuncPanicWithLoop(t *testing.T) {
// function exit, rather than at the defer statement.
state = 2
}
+
+// issue #51988
+// Func.Endlineno was lost when instantiating generic functions, leading to incorrect
+// stack trace positions.
+func TestCallersEndlineno(t *testing.T) {
+ testNormalEndlineno(t)
+ testGenericEndlineno[int](t)
+}
+
+func testNormalEndlineno(t *testing.T) {
+ defer testCallerLine(t, callerLine(t, 0)+1)
+}
+
+func testGenericEndlineno[_ any](t *testing.T) {
+ defer testCallerLine(t, callerLine(t, 0)+1)
+}
+
+func testCallerLine(t *testing.T, want int) {
+ if have := callerLine(t, 1); have != want {
+ t.Errorf("callerLine(1) returned %d, but want %d\n", have, want)
+ }
+}
+
+func callerLine(t *testing.T, skip int) int {
+ _, _, line, ok := runtime.Caller(skip + 1)
+ if !ok {
+ t.Fatalf("runtime.Caller(%d) failed", skip+1)
+ }
+ return line
+}
diff --git a/src/runtime/cgo/callbacks.go b/src/runtime/cgo/callbacks.go
index cd8b7953874..e7c8ef3e07c 100644
--- a/src/runtime/cgo/callbacks.go
+++ b/src/runtime/cgo/callbacks.go
@@ -21,6 +21,7 @@ import "unsafe"
// that pattern working. In particular, crosscall2 actually takes four
// arguments, but it works to call it with three arguments when
// calling _cgo_panic.
+//
//go:cgo_export_static crosscall2
//go:cgo_export_dynamic crosscall2
diff --git a/src/runtime/cgo/callbacks_aix.go b/src/runtime/cgo/callbacks_aix.go
index f4b6fe25fa2..8f756fbdd9f 100644
--- a/src/runtime/cgo/callbacks_aix.go
+++ b/src/runtime/cgo/callbacks_aix.go
@@ -6,6 +6,7 @@ package cgo
// These functions must be exported in order to perform
// longcall on cgo programs (cf gcc_aix_ppc64.c).
+//
//go:cgo_export_static __cgo_topofstack
//go:cgo_export_static runtime.rt0_go
//go:cgo_export_static _rt0_ppc64_aix_lib
diff --git a/src/runtime/cgo/openbsd.go b/src/runtime/cgo/openbsd.go
index 872d02e3345..26b62fbdaf7 100644
--- a/src/runtime/cgo/openbsd.go
+++ b/src/runtime/cgo/openbsd.go
@@ -17,4 +17,5 @@ var _guard_local uintptr
// This is normally marked as hidden and placed in the
// .openbsd.randomdata section.
+//
//go:cgo_export_dynamic __guard_local __guard_local
diff --git a/src/runtime/cgo_mmap.go b/src/runtime/cgo_mmap.go
index 0cb25bdcdab..4cb3e65f14d 100644
--- a/src/runtime/cgo_mmap.go
+++ b/src/runtime/cgo_mmap.go
@@ -12,11 +12,13 @@ import "unsafe"
// _cgo_mmap is filled in by runtime/cgo when it is linked into the
// program, so it is only non-nil when using cgo.
+//
//go:linkname _cgo_mmap _cgo_mmap
var _cgo_mmap unsafe.Pointer
// _cgo_munmap is filled in by runtime/cgo when it is linked into the
// program, so it is only non-nil when using cgo.
+//
//go:linkname _cgo_munmap _cgo_munmap
var _cgo_munmap unsafe.Pointer
@@ -24,6 +26,7 @@ var _cgo_munmap unsafe.Pointer
// support sanitizer interceptors. Don't allow stack splits, since this function
// (used by sysAlloc) is called in a lot of low-level parts of the runtime and
// callers often assume it won't acquire any locks.
+//
//go:nosplit
func mmap(addr unsafe.Pointer, n uintptr, prot, flags, fd int32, off uint32) (unsafe.Pointer, int) {
if _cgo_mmap != nil {
diff --git a/src/runtime/cgo_ppc64x.go b/src/runtime/cgo_ppc64x.go
index 97b962e40f6..c723213809e 100644
--- a/src/runtime/cgo_ppc64x.go
+++ b/src/runtime/cgo_ppc64x.go
@@ -9,4 +9,5 @@ package runtime
// crosscall_ppc64 calls into the runtime to set up the registers the
// Go runtime expects and so the symbol it calls needs to be exported
// for external linking to work.
+//
//go:cgo_export_static _cgo_reginit
diff --git a/src/runtime/cgo_sigaction.go b/src/runtime/cgo_sigaction.go
index a2e12f0f0e2..9500c522059 100644
--- a/src/runtime/cgo_sigaction.go
+++ b/src/runtime/cgo_sigaction.go
@@ -12,6 +12,7 @@ import "unsafe"
// _cgo_sigaction is filled in by runtime/cgo when it is linked into the
// program, so it is only non-nil when using cgo.
+//
//go:linkname _cgo_sigaction _cgo_sigaction
var _cgo_sigaction unsafe.Pointer
@@ -88,5 +89,6 @@ func sigaction(sig uint32, new, old *sigactiont) {
// callCgoSigaction calls the sigaction function in the runtime/cgo package
// using the GCC calling convention. It is implemented in assembly.
+//
//go:noescape
func callCgoSigaction(sig uintptr, new, old *sigactiont) int32
diff --git a/src/runtime/cgocall.go b/src/runtime/cgocall.go
index a0c9560fd0f..977d049378e 100644
--- a/src/runtime/cgocall.go
+++ b/src/runtime/cgocall.go
@@ -102,6 +102,7 @@ type argset struct {
}
// wrapper for syscall package to call cgocall for libc (cgo) calls.
+//
//go:linkname syscall_cgocaller syscall.cgocaller
//go:nosplit
//go:uintptrescapes
@@ -199,6 +200,7 @@ func cgocall(fn, arg unsafe.Pointer) int32 {
}
// Call from C back to Go. fn must point to an ABIInternal Go entry-point.
+//
//go:nosplit
func cgocallbackg(fn, frame unsafe.Pointer, ctxt uintptr) {
gp := getg()
@@ -598,6 +600,7 @@ func cgoCheckUnknownPointer(p unsafe.Pointer, msg string) (base, i uintptr) {
// cgoIsGoPointer reports whether the pointer is a Go pointer--a
// pointer to Go memory. We only care about Go memory that might
// contain pointers.
+//
//go:nosplit
//go:nowritebarrierrec
func cgoIsGoPointer(p unsafe.Pointer) bool {
@@ -619,6 +622,7 @@ func cgoIsGoPointer(p unsafe.Pointer) bool {
}
// cgoInRange reports whether p is between start and end.
+//
//go:nosplit
//go:nowritebarrierrec
func cgoInRange(p unsafe.Pointer, start, end uintptr) bool {
diff --git a/src/runtime/cgocheck.go b/src/runtime/cgocheck.go
index 3acbadf803d..74a2ec09bcd 100644
--- a/src/runtime/cgocheck.go
+++ b/src/runtime/cgocheck.go
@@ -61,6 +61,7 @@ func cgoCheckWriteBarrier(dst *uintptr, src uintptr) {
// size is the number of bytes to copy.
// It throws if the program is copying a block that contains a Go pointer
// into non-Go memory.
+//
//go:nosplit
//go:nowritebarrier
func cgoCheckMemmove(typ *_type, dst, src unsafe.Pointer, off, size uintptr) {
@@ -81,6 +82,7 @@ func cgoCheckMemmove(typ *_type, dst, src unsafe.Pointer, off, size uintptr) {
// typ is the element type of the slice.
// It throws if the program is copying slice elements that contain Go pointers
// into non-Go memory.
+//
//go:nosplit
//go:nowritebarrier
func cgoCheckSliceCopy(typ *_type, dst, src unsafe.Pointer, n int) {
@@ -103,6 +105,7 @@ func cgoCheckSliceCopy(typ *_type, dst, src unsafe.Pointer, n int) {
// cgoCheckTypedBlock checks the block of memory at src, for up to size bytes,
// and throws if it finds a Go pointer. The type of the memory is typ,
// and src is off bytes into that type.
+//
//go:nosplit
//go:nowritebarrier
func cgoCheckTypedBlock(typ *_type, src unsafe.Pointer, off, size uintptr) {
@@ -166,6 +169,7 @@ func cgoCheckTypedBlock(typ *_type, src unsafe.Pointer, off, size uintptr) {
// cgoCheckBits checks the block of memory at src, for up to size
// bytes, and throws if it finds a Go pointer. The gcbits mark each
// pointer value. The src pointer is off bytes into the gcbits.
+//
//go:nosplit
//go:nowritebarrier
func cgoCheckBits(src unsafe.Pointer, gcbits *byte, off, size uintptr) {
@@ -201,6 +205,7 @@ func cgoCheckBits(src unsafe.Pointer, gcbits *byte, off, size uintptr) {
// We only use this when looking at a value on the stack when the type
// uses a GC program, because otherwise it's more efficient to use the
// GC bits. This is called on the system stack.
+//
//go:nowritebarrier
//go:systemstack
func cgoCheckUsingType(typ *_type, src unsafe.Pointer, off, size uintptr) {
diff --git a/src/runtime/chan.go b/src/runtime/chan.go
index a16782ae949..993af7063b9 100644
--- a/src/runtime/chan.go
+++ b/src/runtime/chan.go
@@ -139,6 +139,7 @@ func full(c *hchan) bool {
}
// entry point for c <- x from compiled code
+//
//go:nosplit
func chansend1(c *hchan, elem unsafe.Pointer) {
chansend(c, elem, true, getcallerpc())
@@ -435,6 +436,7 @@ func empty(c *hchan) bool {
}
// entry points for <- c from compiled code
+//
//go:nosplit
func chanrecv1(c *hchan, elem unsafe.Pointer) {
chanrecv(c, elem, true)
@@ -508,24 +510,28 @@ func chanrecv(c *hchan, ep unsafe.Pointer, block bool) (selected, received bool)
lock(&c.lock)
- if c.closed != 0 && c.qcount == 0 {
- if raceenabled {
- raceacquire(c.raceaddr())
+ if c.closed != 0 {
+ if c.qcount == 0 {
+ if raceenabled {
+ raceacquire(c.raceaddr())
+ }
+ unlock(&c.lock)
+ if ep != nil {
+ typedmemclr(c.elemtype, ep)
+ }
+ return true, false
}
- unlock(&c.lock)
- if ep != nil {
- typedmemclr(c.elemtype, ep)
+ // The channel has been closed, but the channel's buffer have data.
+ } else {
+ // Just found waiting sender with not closed.
+ if sg := c.sendq.dequeue(); sg != nil {
+ // Found a waiting sender. If buffer is size 0, receive value
+ // directly from sender. Otherwise, receive from head of queue
+ // and add sender's value to the tail of the queue (both map to
+ // the same buffer slot because the queue is full).
+ recv(c, sg, ep, func() { unlock(&c.lock) }, 3)
+ return true, true
}
- return true, false
- }
-
- if sg := c.sendq.dequeue(); sg != nil {
- // Found a waiting sender. If buffer is size 0, receive value
- // directly from sender. Otherwise, receive from head of queue
- // and add sender's value to the tail of the queue (both map to
- // the same buffer slot because the queue is full).
- recv(c, sg, ep, func() { unlock(&c.lock) }, 3)
- return true, true
}
if c.qcount > 0 {
@@ -594,10 +600,11 @@ func chanrecv(c *hchan, ep unsafe.Pointer, block bool) (selected, received bool)
// recv processes a receive operation on a full channel c.
// There are 2 parts:
-// 1) The value sent by the sender sg is put into the channel
+// 1. The value sent by the sender sg is put into the channel
// and the sender is woken up to go on its merry way.
-// 2) The value received by the receiver (the current G) is
+// 2. The value received by the receiver (the current G) is
// written to ep.
+//
// For synchronous channels, both values are the same.
// For asynchronous channels, the receiver gets its data from
// the channel buffer and the sender's data is put in the
diff --git a/src/runtime/chan_test.go b/src/runtime/chan_test.go
index 9471d4596c2..a8627e98988 100644
--- a/src/runtime/chan_test.go
+++ b/src/runtime/chan_test.go
@@ -226,11 +226,13 @@ func TestNonblockRecvRace(t *testing.T) {
// This test checks that select acts on the state of the channels at one
// moment in the execution, not over a smeared time window.
// In the test, one goroutine does:
+//
// create c1, c2
// make c1 ready for receiving
// create second goroutine
// make c2 ready for receiving
// make c1 no longer ready for receiving (if possible)
+//
// The second goroutine does a non-blocking select receiving from c1 and c2.
// From the time the second goroutine is created, at least one of c1 and c2
// is always ready for receiving, so the select in the second goroutine must
@@ -1125,6 +1127,19 @@ func BenchmarkSelectProdCons(b *testing.B) {
}
}
+func BenchmarkReceiveDataFromClosedChan(b *testing.B) {
+ count := b.N
+ ch := make(chan struct{}, count)
+ for i := 0; i < count; i++ {
+ ch <- struct{}{}
+ }
+ close(ch)
+
+ b.ResetTimer()
+ for range ch {
+ }
+}
+
func BenchmarkChanCreation(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
diff --git a/src/runtime/cpuprof.go b/src/runtime/cpuprof.go
index 48cef46fe9c..07673c9bd0b 100644
--- a/src/runtime/cpuprof.go
+++ b/src/runtime/cpuprof.go
@@ -88,6 +88,7 @@ func SetCPUProfileRate(hz int) {
// 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.
+//
//go:nowritebarrierrec
func (p *cpuProfile) add(tagPtr *unsafe.Pointer, stk []uintptr) {
// Simple cas-lock to coordinate with setcpuprofilerate.
@@ -117,6 +118,7 @@ func (p *cpuProfile) add(tagPtr *unsafe.Pointer, stk []uintptr) {
// Instead, we copy the stack into cpuprof.extra,
// which will be drained the next time a Go thread
// gets the signal handling event.
+//
//go:nosplit
//go:nowritebarrierrec
func (p *cpuProfile) addNonGo(stk []uintptr) {
diff --git a/src/runtime/crash_test.go b/src/runtime/crash_test.go
index d8cabcdda28..01d7cbeb290 100644
--- a/src/runtime/crash_test.go
+++ b/src/runtime/crash_test.go
@@ -800,3 +800,47 @@ func TestDoublePanic(t *testing.T) {
}
}
}
+
+// Test that panic while panicking discards error message
+// See issue 52257
+func TestPanicWhilePanicking(t *testing.T) {
+ tests := []struct {
+ Want string
+ Func string
+ }{
+ {
+ "panic while printing panic value: important error message",
+ "ErrorPanic",
+ },
+ {
+ "panic while printing panic value: important stringer message",
+ "StringerPanic",
+ },
+ {
+ "panic while printing panic value: type",
+ "DoubleErrorPanic",
+ },
+ {
+ "panic while printing panic value: type",
+ "DoubleStringerPanic",
+ },
+ {
+ "panic while printing panic value: type",
+ "CircularPanic",
+ },
+ {
+ "important string message",
+ "StringPanic",
+ },
+ {
+ "nil",
+ "NilPanic",
+ },
+ }
+ for _, x := range tests {
+ output := runTestProg(t, "testprog", x.Func)
+ if !strings.Contains(output, x.Want) {
+ t.Errorf("output does not contain %q:\n%s", x.Want, output)
+ }
+ }
+}
diff --git a/src/runtime/debug.go b/src/runtime/debug.go
index 2703a0ce019..0ab23e0eb79 100644
--- a/src/runtime/debug.go
+++ b/src/runtime/debug.go
@@ -69,7 +69,7 @@ func debug_modinfo() string {
// preemption points. To apply this to all preemption points in the
// runtime and runtime-like code, use the following in bash or zsh:
//
-// X=(-{gc,asm}flags={runtime/...,reflect,sync}=-d=maymorestack=runtime.mayMoreStackPreempt) GOFLAGS=${X[@]}
+// X=(-{gc,asm}flags={runtime/...,reflect,sync}=-d=maymorestack=runtime.mayMoreStackPreempt) GOFLAGS=${X[@]}
//
// This must be deeply nosplit because it is called from a function
// prologue before the stack is set up and because the compiler will
@@ -79,10 +79,9 @@ func debug_modinfo() string {
// Ideally it should also use very little stack because the linker
// doesn't currently account for this in nosplit stack depth checking.
//
-//go:nosplit
-//
// Ensure mayMoreStackPreempt can be called for all ABIs.
//
+//go:nosplit
//go:linkname mayMoreStackPreempt
func mayMoreStackPreempt() {
// Don't do anything on the g0 or gsignal stack.
diff --git a/src/runtime/debug/garbage.go b/src/runtime/debug/garbage.go
index 00f92c3ddfb..ce4bb10407f 100644
--- a/src/runtime/debug/garbage.go
+++ b/src/runtime/debug/garbage.go
@@ -142,7 +142,9 @@ func SetMaxThreads(threads int) int {
// dramatic situations; SetPanicOnFault allows such programs to request
// that the runtime trigger only a panic, not a crash.
// The runtime.Error that the runtime panics with may have an additional method:
-// Addr() uintptr
+//
+// Addr() uintptr
+//
// If that method exists, it returns the memory address which triggered the fault.
// The results of Addr are best-effort and the veracity of the result
// may depend on the platform.
diff --git a/src/runtime/debug/heapdump_test.go b/src/runtime/debug/heapdump_test.go
index 768934d05db..ee6b054b117 100644
--- a/src/runtime/debug/heapdump_test.go
+++ b/src/runtime/debug/heapdump_test.go
@@ -67,3 +67,29 @@ func TestWriteHeapDumpFinalizers(t *testing.T) {
WriteHeapDump(f.Fd())
println("done dump")
}
+
+type G[T any] struct{}
+type I interface {
+ M()
+}
+
+//go:noinline
+func (g G[T]) M() {}
+
+var dummy I = G[int]{}
+var dummy2 I = G[G[int]]{}
+
+func TestWriteHeapDumpTypeName(t *testing.T) {
+ if runtime.GOOS == "js" {
+ t.Skipf("WriteHeapDump is not available on %s.", runtime.GOOS)
+ }
+ f, err := os.CreateTemp("", "heapdumptest")
+ if err != nil {
+ t.Fatalf("TempFile failed: %v", err)
+ }
+ defer os.Remove(f.Name())
+ defer f.Close()
+ WriteHeapDump(f.Fd())
+ dummy.M()
+ dummy2.M()
+}
diff --git a/src/runtime/env_plan9.go b/src/runtime/env_plan9.go
index f1ac4760a75..65480c82177 100644
--- a/src/runtime/env_plan9.go
+++ b/src/runtime/env_plan9.go
@@ -25,6 +25,7 @@ const (
// For Plan 9 shared environment semantics, instead of Getenv(key) and
// Setenv(key, value), one can use os.ReadFile("/env/" + key) and
// os.WriteFile("/env/" + key, value, 0666) respectively.
+//
//go:nosplit
func goenvs() {
buf := make([]byte, envBufSize)
@@ -71,6 +72,7 @@ func goenvs() {
// Dofiles reads the directory opened with file descriptor fd, applying function f
// to each filename in it.
+//
//go:nosplit
func dofiles(dirfd int32, f func([]byte)) {
dirbuf := new([dirBufSize]byte)
@@ -96,6 +98,7 @@ func dofiles(dirfd int32, f func([]byte)) {
// Gdirname returns the first filename from a buffer of directory entries,
// and a slice containing the remaining directory entries.
// If the buffer doesn't start with a valid directory entry, the returned name is nil.
+//
//go:nosplit
func gdirname(buf []byte) (name []byte, rest []byte) {
if 2+nameOffset+2 > len(buf) {
@@ -116,6 +119,7 @@ func gdirname(buf []byte) (name []byte, rest []byte) {
// Gbit16 reads a 16-bit little-endian binary number from b and returns it
// with the remaining slice of b.
+//
//go:nosplit
func gbit16(b []byte) (int, []byte) {
return int(b[0]) | int(b[1])<<8, b[2:]
diff --git a/src/runtime/env_posix.go b/src/runtime/env_posix.go
index 7d01ab4dd7d..94a19d80d8e 100644
--- a/src/runtime/env_posix.go
+++ b/src/runtime/env_posix.go
@@ -49,6 +49,7 @@ var _cgo_unsetenv unsafe.Pointer // pointer to C function
// Update the C environment if cgo is loaded.
// Called from syscall.Setenv.
+//
//go:linkname syscall_setenv_c syscall.setenv_c
func syscall_setenv_c(k string, v string) {
if _cgo_setenv == nil {
@@ -60,6 +61,7 @@ func syscall_setenv_c(k string, v string) {
// Update the C environment if cgo is loaded.
// Called from syscall.unsetenv.
+//
//go:linkname syscall_unsetenv_c syscall.unsetenv_c
func syscall_unsetenv_c(k string) {
if _cgo_unsetenv == nil {
diff --git a/src/runtime/error.go b/src/runtime/error.go
index 43114f092e1..b11473c6346 100644
--- a/src/runtime/error.go
+++ b/src/runtime/error.go
@@ -53,10 +53,11 @@ func (e *TypeAssertionError) Error() string {
": missing method " + e.missingMethod
}
-//go:nosplit
// itoa converts val to a decimal representation. The result is
// written somewhere within buf and the location of the result is returned.
// buf must be at least 20 bytes.
+//
+//go:nosplit
func itoa(buf []byte, val uint64) []byte {
i := len(buf) - 1
for val >= 10 {
diff --git a/src/runtime/export_test.go b/src/runtime/export_test.go
index 01569815249..af27050bfd0 100644
--- a/src/runtime/export_test.go
+++ b/src/runtime/export_test.go
@@ -1143,6 +1143,7 @@ func SemNwait(addr *uint32) uint32 {
}
// mspan wrapper for testing.
+//
//go:notinheap
type MSpan mspan
diff --git a/src/runtime/extern.go b/src/runtime/extern.go
index f1f6ea51231..9dd59e0985b 100644
--- a/src/runtime/extern.go
+++ b/src/runtime/extern.go
@@ -8,7 +8,7 @@ such as functions to control goroutines. It also includes the low-level type inf
used by the reflect package; see reflect's documentation for the programmable
interface to the run-time type system.
-Environment Variables
+# Environment Variables
The following environment variables ($name or %name%, depending on the host
operating system) control the run-time behavior of Go programs. The meanings
@@ -64,13 +64,15 @@ It is a comma-separated list of name=val pairs setting these named variables:
Currently, it is:
gc # @#s #%: #+#+# ms clock, #+#/#/#+# ms cpu, #->#-># MB, # MB goal, # P
where the fields are as follows:
- gc # the GC number, incremented at each GC
- @#s time in seconds since program start
- #% percentage of time spent in GC since program start
- #+...+# wall-clock/CPU times for the phases of the GC
- #->#-># MB heap size at GC start, at GC end, and live heap
- # MB goal goal heap size
- # P number of processors used
+ gc # the GC number, incremented at each GC
+ @#s time in seconds since program start
+ #% percentage of time spent in GC since program start
+ #+...+# wall-clock/CPU times for the phases of the GC
+ #->#-># MB heap size at GC start, at GC end, and live heap
+ # MB goal goal heap size
+ # MB stacks estimated scannable stack size
+ # MB globals scannable global size
+ # P number of processors used
The phases are stop-the-world (STW) sweep termination, concurrent
mark and scan, and STW mark termination. The CPU times
for mark/scan are broken down in to assist time (GC performed in
@@ -170,9 +172,9 @@ or the failure is internal to the run-time.
GOTRACEBACK=none omits the goroutine stack traces entirely.
GOTRACEBACK=single (the default) behaves as described above.
GOTRACEBACK=all adds stack traces for all user-created goroutines.
-GOTRACEBACK=system is like ``all'' but adds stack frames for run-time functions
+GOTRACEBACK=system is like “all” but adds stack frames for run-time functions
and shows goroutines created internally by the run-time.
-GOTRACEBACK=crash is like ``system'' but crashes in an operating system-specific
+GOTRACEBACK=crash is like “system” but crashes in an operating system-specific
manner instead of exiting. For example, on Unix systems, the crash raises
SIGABRT to trigger a core dump.
For historical reasons, the GOTRACEBACK settings 0, 1, and 2 are synonyms for
diff --git a/src/runtime/float.go b/src/runtime/float.go
index 459e58dd7ef..c80c8b7abfb 100644
--- a/src/runtime/float.go
+++ b/src/runtime/float.go
@@ -8,7 +8,7 @@ import "unsafe"
var inf = float64frombits(0x7FF0000000000000)
-// isNaN reports whether f is an IEEE 754 ``not-a-number'' value.
+// isNaN reports whether f is an IEEE 754 “not-a-number” value.
func isNaN(f float64) (is bool) {
// IEEE 754 says that only NaNs satisfy f != f.
return f != f
@@ -27,6 +27,7 @@ func isInf(f float64) bool {
// Abs returns the absolute value of x.
//
// Special cases are:
+//
// Abs(±Inf) = +Inf
// Abs(NaN) = NaN
func abs(x float64) float64 {
diff --git a/src/runtime/histogram.go b/src/runtime/histogram.go
index cd7e29a8c8b..eddfbab3bc5 100644
--- a/src/runtime/histogram.go
+++ b/src/runtime/histogram.go
@@ -84,6 +84,7 @@ type timeHistogram struct {
//
// Disallow preemptions and stack growths because this function
// may run in sensitive locations.
+//
//go:nosplit
func (h *timeHistogram) record(duration int64) {
if duration < 0 {
diff --git a/src/runtime/internal/atomic/atomic_386.go b/src/runtime/internal/atomic/atomic_386.go
index 27a77ec37a9..bf2f4b92291 100644
--- a/src/runtime/internal/atomic/atomic_386.go
+++ b/src/runtime/internal/atomic/atomic_386.go
@@ -9,6 +9,7 @@ package atomic
import "unsafe"
// Export some functions via linkname to assembly in sync/atomic.
+//
//go:linkname Load
//go:linkname Loadp
diff --git a/src/runtime/internal/atomic/atomic_amd64.go b/src/runtime/internal/atomic/atomic_amd64.go
index e36eb83a113..52a83620c8f 100644
--- a/src/runtime/internal/atomic/atomic_amd64.go
+++ b/src/runtime/internal/atomic/atomic_amd64.go
@@ -7,6 +7,7 @@ package atomic
import "unsafe"
// Export some functions via linkname to assembly in sync/atomic.
+//
//go:linkname Load
//go:linkname Loadp
//go:linkname Load64
diff --git a/src/runtime/internal/atomic/atomic_arm.go b/src/runtime/internal/atomic/atomic_arm.go
index e2539b6c7ec..bdb18472790 100644
--- a/src/runtime/internal/atomic/atomic_arm.go
+++ b/src/runtime/internal/atomic/atomic_arm.go
@@ -12,6 +12,7 @@ import (
)
// Export some functions via linkname to assembly in sync/atomic.
+//
//go:linkname Xchg
//go:linkname Xchguintptr
@@ -43,6 +44,7 @@ func addrLock(addr *uint64) *spinlock {
}
// Atomic add and return new value.
+//
//go:nosplit
func Xadd(val *uint32, delta int32) uint32 {
for {
diff --git a/src/runtime/internal/atomic/atomic_mipsx.go b/src/runtime/internal/atomic/atomic_mipsx.go
index e552e574952..5dd15a0b022 100644
--- a/src/runtime/internal/atomic/atomic_mipsx.go
+++ b/src/runtime/internal/atomic/atomic_mipsx.go
@@ -5,6 +5,7 @@
//go:build mips || mipsle
// Export some functions via linkname to assembly in sync/atomic.
+//
//go:linkname Xadd64
//go:linkname Xchg64
//go:linkname Cas64
diff --git a/src/runtime/internal/atomic/atomic_s390x.go b/src/runtime/internal/atomic/atomic_s390x.go
index a058d601029..9855bf0780f 100644
--- a/src/runtime/internal/atomic/atomic_s390x.go
+++ b/src/runtime/internal/atomic/atomic_s390x.go
@@ -7,6 +7,7 @@ package atomic
import "unsafe"
// Export some functions via linkname to assembly in sync/atomic.
+//
//go:linkname Load
//go:linkname Loadp
//go:linkname Load64
diff --git a/src/runtime/internal/atomic/atomic_wasm.go b/src/runtime/internal/atomic/atomic_wasm.go
index 3f77f16b4ef..835fc43ccf9 100644
--- a/src/runtime/internal/atomic/atomic_wasm.go
+++ b/src/runtime/internal/atomic/atomic_wasm.go
@@ -6,6 +6,7 @@
// See https://github.com/WebAssembly/design/issues/1073
// Export some functions via linkname to assembly in sync/atomic.
+//
//go:linkname Load
//go:linkname Loadp
//go:linkname Load64
diff --git a/src/runtime/lock_futex.go b/src/runtime/lock_futex.go
index 575df7a1d57..1578984ce23 100644
--- a/src/runtime/lock_futex.go
+++ b/src/runtime/lock_futex.go
@@ -38,6 +38,7 @@ const (
// affect mutex's state.
// We use the uintptr mutex.key and note.key as a uint32.
+//
//go:nosplit
func key32(p *uintptr) *uint32 {
return (*uint32)(unsafe.Pointer(p))
diff --git a/src/runtime/lock_sema.go b/src/runtime/lock_sema.go
index 6961c2ea9bc..c5e8cfe24a7 100644
--- a/src/runtime/lock_sema.go
+++ b/src/runtime/lock_sema.go
@@ -96,8 +96,9 @@ func unlock(l *mutex) {
unlockWithRank(l)
}
-//go:nowritebarrier
// We might not be holding a p in this code.
+//
+//go:nowritebarrier
func unlock2(l *mutex) {
gp := getg()
var mp *m
diff --git a/src/runtime/lockrank_off.go b/src/runtime/lockrank_off.go
index daa45b542dd..bf046a10412 100644
--- a/src/runtime/lockrank_off.go
+++ b/src/runtime/lockrank_off.go
@@ -23,6 +23,7 @@ func lockWithRank(l *mutex, rank lockRank) {
}
// This function may be called in nosplit context and thus must be nosplit.
+//
//go:nosplit
func acquireLockRank(rank lockRank) {
}
@@ -32,6 +33,7 @@ func unlockWithRank(l *mutex) {
}
// This function may be called in nosplit context and thus must be nosplit.
+//
//go:nosplit
func releaseLockRank(rank lockRank) {
}
diff --git a/src/runtime/lockrank_on.go b/src/runtime/lockrank_on.go
index 3c8c367c19f..a170569d6e2 100644
--- a/src/runtime/lockrank_on.go
+++ b/src/runtime/lockrank_on.go
@@ -82,6 +82,7 @@ func lockWithRank(l *mutex, rank lockRank) {
}
// nosplit to ensure it can be called in as many contexts as possible.
+//
//go:nosplit
func printHeldLocks(gp *g) {
if gp.m.locksHeldLen == 0 {
@@ -97,6 +98,7 @@ func printHeldLocks(gp *g) {
// acquireLockRank acquires a rank which is not associated with a mutex lock
//
// This function may be called in nosplit context and thus must be nosplit.
+//
//go:nosplit
func acquireLockRank(rank lockRank) {
gp := getg()
@@ -181,6 +183,7 @@ func unlockWithRank(l *mutex) {
// releaseLockRank releases a rank which is not associated with a mutex lock
//
// This function may be called in nosplit context and thus must be nosplit.
+//
//go:nosplit
func releaseLockRank(rank lockRank) {
gp := getg()
@@ -226,6 +229,7 @@ func lockWithRankMayAcquire(l *mutex, rank lockRank) {
}
// nosplit to ensure it can be called in as many contexts as possible.
+//
//go:nosplit
func checkLockHeld(gp *g, l *mutex) bool {
for i := gp.m.locksHeldLen - 1; i >= 0; i-- {
@@ -239,6 +243,7 @@ func checkLockHeld(gp *g, l *mutex) bool {
// assertLockHeld throws if l is not held by the caller.
//
// nosplit to ensure it can be called in as many contexts as possible.
+//
//go:nosplit
func assertLockHeld(l *mutex) {
gp := getg()
@@ -264,6 +269,7 @@ func assertLockHeld(l *mutex) {
// pointer to the exact mutex is not available.
//
// nosplit to ensure it can be called in as many contexts as possible.
+//
//go:nosplit
func assertRankHeld(r lockRank) {
gp := getg()
@@ -289,6 +295,7 @@ func assertRankHeld(r lockRank) {
// Caller must hold worldsema.
//
// nosplit to ensure it can be called in as many contexts as possible.
+//
//go:nosplit
func worldStopped() {
if stopped := atomic.Xadd(&worldIsStopped, 1); stopped != 1 {
@@ -304,6 +311,7 @@ func worldStopped() {
// Caller must hold worldsema.
//
// nosplit to ensure it can be called in as many contexts as possible.
+//
//go:nosplit
func worldStarted() {
if stopped := atomic.Xadd(&worldIsStopped, -1); stopped != 0 {
@@ -315,6 +323,7 @@ func worldStarted() {
}
// nosplit to ensure it can be called in as many contexts as possible.
+//
//go:nosplit
func checkWorldStopped() bool {
stopped := atomic.Load(&worldIsStopped)
@@ -332,6 +341,7 @@ func checkWorldStopped() bool {
// which M stopped the world.
//
// nosplit to ensure it can be called in as many contexts as possible.
+//
//go:nosplit
func assertWorldStopped() {
if checkWorldStopped() {
@@ -345,6 +355,7 @@ func assertWorldStopped() {
// passed lock is not held.
//
// nosplit to ensure it can be called in as many contexts as possible.
+//
//go:nosplit
func assertWorldStoppedOrLockHeld(l *mutex) {
if checkWorldStopped() {
diff --git a/src/runtime/malloc.go b/src/runtime/malloc.go
index a00878a11c2..c1821977821 100644
--- a/src/runtime/malloc.go
+++ b/src/runtime/malloc.go
@@ -1326,6 +1326,7 @@ func persistentalloc(size, align uintptr, sysStat *sysMemStat) unsafe.Pointer {
// Must run on system stack because stack growth can (re)invoke it.
// See issue 9174.
+//
//go:systemstack
func persistentalloc1(size, align uintptr, sysStat *sysMemStat) *notInHeap {
const (
@@ -1395,6 +1396,7 @@ func persistentalloc1(size, align uintptr, sysStat *sysMemStat) *notInHeap {
// inPersistentAlloc reports whether p points to memory allocated by
// persistentalloc. This must be nosplit because it is called by the
// cgo checker code, which is called by the write barrier code.
+//
//go:nosplit
func inPersistentAlloc(p uintptr) bool {
chunk := atomic.Loaduintptr((*uintptr)(unsafe.Pointer(&persistentChunks)))
diff --git a/src/runtime/map_test.go b/src/runtime/map_test.go
index 0c83dd4ddf8..5c458b4a493 100644
--- a/src/runtime/map_test.go
+++ b/src/runtime/map_test.go
@@ -29,8 +29,9 @@ func TestHmapSize(t *testing.T) {
}
// negative zero is a good test because:
-// 1) 0 and -0 are equal, yet have distinct representations.
-// 2) 0 is represented as all zeros, -0 isn't.
+// 1. 0 and -0 are equal, yet have distinct representations.
+// 2. 0 is represented as all zeros, -0 isn't.
+//
// I'm not sure the language spec actually requires this behavior,
// but it's what the current map implementation does.
func TestNegativeZero(t *testing.T) {
diff --git a/src/runtime/mbarrier.go b/src/runtime/mbarrier.go
index a0d145ec761..c3b45415a95 100644
--- a/src/runtime/mbarrier.go
+++ b/src/runtime/mbarrier.go
@@ -199,6 +199,7 @@ func reflectlite_typedmemmove(typ *_type, dst, src unsafe.Pointer) {
// typedmemmovepartial is like typedmemmove but assumes that
// dst and src point off bytes into the value and only copies size bytes.
// off must be a multiple of goarch.PtrSize.
+//
//go:linkname reflect_typedmemmovepartial reflect.typedmemmovepartial
func reflect_typedmemmovepartial(typ *_type, dst, src unsafe.Pointer, off, size uintptr) {
if writeBarrier.needed && typ.ptrdata > off && size >= goarch.PtrSize {
diff --git a/src/runtime/mbitmap.go b/src/runtime/mbitmap.go
index 937968807b0..a3a6590d65f 100644
--- a/src/runtime/mbitmap.go
+++ b/src/runtime/mbitmap.go
@@ -65,6 +65,7 @@ const (
)
// addb returns the byte pointer p+n.
+//
//go:nowritebarrier
//go:nosplit
func addb(p *byte, n uintptr) *byte {
@@ -75,6 +76,7 @@ func addb(p *byte, n uintptr) *byte {
}
// subtractb returns the byte pointer p-n.
+//
//go:nowritebarrier
//go:nosplit
func subtractb(p *byte, n uintptr) *byte {
@@ -85,6 +87,7 @@ func subtractb(p *byte, n uintptr) *byte {
}
// add1 returns the byte pointer p+1.
+//
//go:nowritebarrier
//go:nosplit
func add1(p *byte) *byte {
@@ -95,9 +98,10 @@ func add1(p *byte) *byte {
}
// subtract1 returns the byte pointer p-1.
-//go:nowritebarrier
//
// nosplit because it is used during write barriers and must not be preempted.
+//
+//go:nowritebarrier
//go:nosplit
func subtract1(p *byte) *byte {
// Note: wrote out full expression instead of calling subtractb(p, 1)
@@ -314,6 +318,7 @@ func (m *markBits) advance() {
// In particular, be careful not to point past the end of an object.
//
// nosplit because it is used during write barriers and must not be preempted.
+//
//go:nosplit
func heapBitsForAddr(addr uintptr) (h heapBits) {
// 2 bits per word, 4 pairs per byte, and a mask is hard coded.
@@ -381,6 +386,7 @@ func badPointer(s *mspan, p, refBase, refOff uintptr) {
//
// It is nosplit so it is safe for p to be a pointer to the current goroutine's stack.
// Since p is a uintptr, it would not be adjusted if the stack were to move.
+//
//go:nosplit
func findObject(p, refBase, refOff uintptr) (base uintptr, s *mspan, objIndex uintptr) {
s = spanOf(p)
@@ -418,6 +424,7 @@ func findObject(p, refBase, refOff uintptr) (base uintptr, s *mspan, objIndex ui
}
// verifyNotInHeapPtr reports whether converting the not-in-heap pointer into a unsafe.Pointer is ok.
+//
//go:linkname reflect_verifyNotInHeapPtr reflect.verifyNotInHeapPtr
func reflect_verifyNotInHeapPtr(p uintptr) bool {
// Conversion to a pointer is ok as long as findObject above does not call badPointer.
@@ -431,6 +438,7 @@ func reflect_verifyNotInHeapPtr(p uintptr) bool {
// Note that next does not modify h. The caller must record the result.
//
// nosplit because it is used during write barriers and must not be preempted.
+//
//go:nosplit
func (h heapBits) next() heapBits {
if h.shift < 3*heapBitsShift {
@@ -477,6 +485,7 @@ func (h heapBits) nextArena() heapBits {
// h.forward(1) is equivalent to h.next(), just slower.
// Note that forward does not modify h. The caller must record the result.
// bits returns the heap bits for the current word.
+//
//go:nosplit
func (h heapBits) forward(n uintptr) heapBits {
n += uintptr(h.shift) / heapBitsShift
@@ -517,6 +526,7 @@ func (h heapBits) forwardOrBoundary(n uintptr) (heapBits, uintptr) {
// described by the same bitmap byte.
//
// nosplit because it is used during write barriers and must not be preempted.
+//
//go:nosplit
func (h heapBits) bits() uint32 {
// The (shift & 31) eliminates a test and conditional branch
@@ -534,6 +544,7 @@ func (h heapBits) morePointers() bool {
// isPointer reports whether the heap bits describe a pointer word.
//
// nosplit because it is used during write barriers and must not be preempted.
+//
//go:nosplit
func (h heapBits) isPointer() bool {
return h.bits()&bitPointer != 0
@@ -633,6 +644,7 @@ func bulkBarrierPreWrite(dst, src, size uintptr) {
//
// This is used for special cases where e.g. dst was just
// created and zeroed with malloc.
+//
//go:nosplit
func bulkBarrierPreWriteSrcOnly(dst, src, size uintptr) {
if (dst|src|size)&(goarch.PtrSize-1) != 0 {
@@ -1951,6 +1963,7 @@ func getgcmaskcb(frame *stkframe, ctxt unsafe.Pointer) bool {
// gcbits returns the GC type info for x, for testing.
// The result is the bitmap entries (0 or 1), one entry per byte.
+//
//go:linkname reflect_gcbits reflect.gcbits
func reflect_gcbits(x any) []byte {
ret := getgcmask(x)
diff --git a/src/runtime/mem_aix.go b/src/runtime/mem_aix.go
index d6a181ad4dc..21726b56aec 100644
--- a/src/runtime/mem_aix.go
+++ b/src/runtime/mem_aix.go
@@ -10,6 +10,7 @@ import (
// Don't split the stack as this method may be invoked without a valid G, which
// prevents us from allocating more stack.
+//
//go:nosplit
func sysAllocOS(n uintptr) unsafe.Pointer {
p, err := mmap(nil, n, _PROT_READ|_PROT_WRITE, _MAP_ANON|_MAP_PRIVATE, -1, 0)
@@ -39,6 +40,7 @@ func sysHugePageOS(v unsafe.Pointer, n uintptr) {
// Don't split the stack as this function may be invoked without a valid G,
// which prevents us from allocating more stack.
+//
//go:nosplit
func sysFreeOS(v unsafe.Pointer, n uintptr) {
munmap(v, n)
diff --git a/src/runtime/mem_bsd.go b/src/runtime/mem_bsd.go
index e83145e86b9..782465ae262 100644
--- a/src/runtime/mem_bsd.go
+++ b/src/runtime/mem_bsd.go
@@ -12,6 +12,7 @@ import (
// Don't split the stack as this function may be invoked without a valid G,
// which prevents us from allocating more stack.
+//
//go:nosplit
func sysAllocOS(n uintptr) unsafe.Pointer {
v, err := mmap(nil, n, _PROT_READ|_PROT_WRITE, _MAP_ANON|_MAP_PRIVATE, -1, 0)
@@ -33,6 +34,7 @@ func sysHugePageOS(v unsafe.Pointer, n uintptr) {
// Don't split the stack as this function may be invoked without a valid G,
// which prevents us from allocating more stack.
+//
//go:nosplit
func sysFreeOS(v unsafe.Pointer, n uintptr) {
munmap(v, n)
diff --git a/src/runtime/mem_darwin.go b/src/runtime/mem_darwin.go
index d63b5559aac..25862cf1617 100644
--- a/src/runtime/mem_darwin.go
+++ b/src/runtime/mem_darwin.go
@@ -10,6 +10,7 @@ import (
// Don't split the stack as this function may be invoked without a valid G,
// which prevents us from allocating more stack.
+//
//go:nosplit
func sysAllocOS(n uintptr) unsafe.Pointer {
v, err := mmap(nil, n, _PROT_READ|_PROT_WRITE, _MAP_ANON|_MAP_PRIVATE, -1, 0)
@@ -37,6 +38,7 @@ func sysHugePageOS(v unsafe.Pointer, n uintptr) {
// Don't split the stack as this function may be invoked without a valid G,
// which prevents us from allocating more stack.
+//
//go:nosplit
func sysFreeOS(v unsafe.Pointer, n uintptr) {
munmap(v, n)
diff --git a/src/runtime/mem_js.go b/src/runtime/mem_js.go
index c66b91eedd2..e87c5f26ae2 100644
--- a/src/runtime/mem_js.go
+++ b/src/runtime/mem_js.go
@@ -12,6 +12,7 @@ import (
// Don't split the stack as this function may be invoked without a valid G,
// which prevents us from allocating more stack.
+//
//go:nosplit
func sysAllocOS(n uintptr) unsafe.Pointer {
p := sysReserveOS(nil, n)
@@ -30,6 +31,7 @@ func sysHugePageOS(v unsafe.Pointer, n uintptr) {
// Don't split the stack as this function may be invoked without a valid G,
// which prevents us from allocating more stack.
+//
//go:nosplit
func sysFreeOS(v unsafe.Pointer, n uintptr) {
}
diff --git a/src/runtime/mem_linux.go b/src/runtime/mem_linux.go
index 980f7bb53dd..1630664cff8 100644
--- a/src/runtime/mem_linux.go
+++ b/src/runtime/mem_linux.go
@@ -16,6 +16,7 @@ const (
// Don't split the stack as this method may be invoked without a valid G, which
// prevents us from allocating more stack.
+//
//go:nosplit
func sysAllocOS(n uintptr) unsafe.Pointer {
p, err := mmap(nil, n, _PROT_READ|_PROT_WRITE, _MAP_ANON|_MAP_PRIVATE, -1, 0)
@@ -162,6 +163,7 @@ func sysHugePageOS(v unsafe.Pointer, n uintptr) {
// Don't split the stack as this function may be invoked without a valid G,
// which prevents us from allocating more stack.
+//
//go:nosplit
func sysFreeOS(v unsafe.Pointer, n uintptr) {
munmap(v, n)
diff --git a/src/runtime/mem_windows.go b/src/runtime/mem_windows.go
index c8f039f50be..b1292fc7252 100644
--- a/src/runtime/mem_windows.go
+++ b/src/runtime/mem_windows.go
@@ -23,6 +23,7 @@ const (
// Don't split the stack as this function may be invoked without a valid G,
// which prevents us from allocating more stack.
+//
//go:nosplit
func sysAllocOS(n uintptr) unsafe.Pointer {
return unsafe.Pointer(stdcall4(_VirtualAlloc, 0, n, _MEM_COMMIT|_MEM_RESERVE, _PAGE_READWRITE))
@@ -95,6 +96,7 @@ func sysHugePageOS(v unsafe.Pointer, n uintptr) {
// Don't split the stack as this function may be invoked without a valid G,
// which prevents us from allocating more stack.
+//
//go:nosplit
func sysFreeOS(v unsafe.Pointer, n uintptr) {
r := stdcall3(_VirtualFree, uintptr(v), 0, _MEM_RELEASE)
diff --git a/src/runtime/memclr_ppc64x.s b/src/runtime/memclr_ppc64x.s
index ad84ea9600e..354325585d6 100644
--- a/src/runtime/memclr_ppc64x.s
+++ b/src/runtime/memclr_ppc64x.s
@@ -52,37 +52,50 @@ byte4:
BR zero512xsetup // ptr should now be 8 byte aligned
under512:
- MOVD R6, CTR // R6 = number of double words
- SRDCC $2, R6, R7 // 32 byte chunks?
- BNE zero32setup
-
- // Clear double words
-
-zero8:
- MOVD R0, 0(R3) // double word
- ADD $8, R3
- ADD $-8, R4
- BC 16, 0, zero8 // dec ctr, br zero8 if ctr not 0
- BR nozerolarge // handle leftovers
+ SRDCC $3, R6, R7 // 64 byte chunks?
+ XXLXOR VS32, VS32, VS32 // clear VS32 (V0)
+ BEQ lt64gt8
- // Prepare to clear 32 bytes at a time.
+ // Prepare to clear 64 bytes at a time.
-zero32setup:
+zero64setup:
DCBTST (R3) // prepare data cache
- XXLXOR VS32, VS32, VS32 // clear VS32 (V0)
- MOVD R7, CTR // number of 32 byte chunks
+ MOVD R7, CTR // number of 64 byte chunks
MOVD $16, R8
+ MOVD $32, R16
+ MOVD $48, R17
-zero32:
+zero64:
STXVD2X VS32, (R3+R0) // store 16 bytes
STXVD2X VS32, (R3+R8)
- ADD $32, R3
- ADD $-32, R4
- BC 16, 0, zero32 // dec ctr, br zero32 if ctr not 0
- RLDCLCC $61, R4, $3, R6 // remaining doublewords
+ STXVD2X VS32, (R3+R16)
+ STXVD2X VS32, (R3+R17)
+ ADD $64, R3
+ ADD $-64, R4
+ BDNZ zero64 // dec ctr, br zero64 if ctr not 0
+ SRDCC $3, R4, R6 // remaining doublewords
BEQ nozerolarge
- MOVD R6, CTR // set up the CTR for doublewords
- BR zero8
+
+lt64gt8:
+ CMP R4, $32
+ BLT lt32gt8
+ MOVD $16, R8
+ STXVD2X VS32, (R3+R0)
+ STXVD2X VS32, (R3+R8)
+ ADD $-32, R4
+ ADD $32, R3
+lt32gt8:
+ CMP R4, $16
+ BLT lt16gt8
+ STXVD2X VS32, (R3+R0)
+ ADD $16, R3
+ ADD $-16, R4
+lt16gt8:
+ CMP R4, $8
+ BLT nozerolarge
+ MOVD R0, 0(R3)
+ ADD $8, R3
+ ADD $-8, R4
nozerolarge:
ANDCC $7, R4, R5 // any remaining bytes
@@ -94,7 +107,7 @@ zerotail:
zerotailloop:
MOVB R0, 0(R3) // clear single bytes
ADD $1, R3
- BC 16, 0, zerotailloop // dec ctr, br zerotailloop if ctr not 0
+ BDNZ zerotailloop // dec ctr, br zerotailloop if ctr not 0
RET
zero512xsetup: // 512 chunk with extra needed
@@ -119,7 +132,7 @@ zero512preloop: // clear up to 128 alignment
STXVD2X VS32, (R3+R0) // clear 16 bytes
ADD $16, R3 // update ptr
ADD $-16, R4 // dec count
- BC 16, 0, zero512preloop
+ BDNZ zero512preloop
zero512setup: // setup for dcbz loop
CMP R4, $512 // check if at least 512
@@ -129,6 +142,7 @@ zero512setup: // setup for dcbz loop
MOVD $128, R9 // index regs for 128 bytes
MOVD $256, R10
MOVD $384, R11
+ PCALIGN $32
zero512:
DCBZ (R3+R0) // clear first chunk
@@ -136,8 +150,8 @@ zero512:
DCBZ (R3+R10) // clear third chunk
DCBZ (R3+R11) // clear fourth chunk
ADD $512, R3
- ADD $-512, R4
- BC 16, 0, zero512
+ BDNZ zero512
+ ANDCC $511, R4
remain:
CMP R4, $128 // check if 128 byte chunks left
@@ -150,16 +164,11 @@ remain:
smaller:
ANDCC $127, R4, R7 // find leftovers
BEQ done
- CMP R7, $64 // more than 64, do 32 at a time
- BLT zero8setup // less than 64, do 8 at a time
- SRD $5, R7, R7 // set up counter for 32
- BR zero32setup
-
-zero8setup:
- SRDCC $3, R7, R7 // less than 8 bytes
- BEQ nozerolarge
- MOVD R7, CTR
- BR zero8
+ CMP R7, $64 // more than 64, do 64 at a time
+ XXLXOR VS32, VS32, VS32
+ BLT lt64gt8 // less than 64
+ SRD $6, R7, R7 // set up counter for 64
+ BR zero64setup
done:
RET
diff --git a/src/runtime/memclr_riscv64.s b/src/runtime/memclr_riscv64.s
index 54ddaa4560c..f0e517a547e 100644
--- a/src/runtime/memclr_riscv64.s
+++ b/src/runtime/memclr_riscv64.s
@@ -7,40 +7,42 @@
// See memclrNoHeapPointers Go doc for important implementation constraints.
// void runtime·memclrNoHeapPointers(void*, uintptr)
-TEXT runtime·memclrNoHeapPointers(SB),NOSPLIT,$0-16
- MOV ptr+0(FP), T1
- MOV n+8(FP), T2
- ADD T1, T2, T4
+TEXT runtime·memclrNoHeapPointers<ABIInternal>(SB),NOSPLIT,$0-16
+#ifndef GOEXPERIMENT_regabiargs
+ MOV ptr+0(FP), A0
+ MOV n+8(FP), A1
+#endif
+ ADD A0, A1, T4
// If less than eight bytes, do one byte at a time.
- SLTU $8, T2, T3
+ SLTU $8, A1, T3
BNE T3, ZERO, outcheck
// Do one byte at a time until eight-aligned.
JMP aligncheck
align:
- MOVB ZERO, (T1)
- ADD $1, T1
+ MOVB ZERO, (A0)
+ ADD $1, A0
aligncheck:
- AND $7, T1, T3
+ AND $7, A0, T3
BNE T3, ZERO, align
// Do eight bytes at a time as long as there is room.
ADD $-7, T4, T5
JMP wordscheck
words:
- MOV ZERO, (T1)
- ADD $8, T1
+ MOV ZERO, (A0)
+ ADD $8, A0
wordscheck:
- SLTU T5, T1, T3
+ SLTU T5, A0, T3
BNE T3, ZERO, words
JMP outcheck
out:
- MOVB ZERO, (T1)
- ADD $1, T1
+ MOVB ZERO, (A0)
+ ADD $1, A0
outcheck:
- BNE T1, T4, out
+ BNE A0, T4, out
done:
RET
diff --git a/src/runtime/memmove_amd64.s b/src/runtime/memmove_amd64.s
index eeb5033fd95..018bb0b19d5 100644
--- a/src/runtime/memmove_amd64.s
+++ b/src/runtime/memmove_amd64.s
@@ -418,9 +418,9 @@ gobble_mem_fwd_loop:
PREFETCHNTA 0x1C0(SI)
PREFETCHNTA 0x280(SI)
// Prefetch values were chosen empirically.
- // Approach for prefetch usage as in 7.6.6 of [1]
+ // Approach for prefetch usage as in 9.5.6 of [1]
// [1] 64-ia-32-architectures-optimization-manual.pdf
- // https://www.intel.ru/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-optimization-manual.pdf
+ // https://www.intel.com/content/dam/www/public/us/en/documents/manuals/64-ia-32-architectures-optimization-manual.pdf
VMOVDQU (SI), Y0
VMOVDQU 0x20(SI), Y1
VMOVDQU 0x40(SI), Y2
diff --git a/src/runtime/memmove_ppc64x.s b/src/runtime/memmove_ppc64x.s
index 25101a28c75..5fa51c0a4cf 100644
--- a/src/runtime/memmove_ppc64x.s
+++ b/src/runtime/memmove_ppc64x.s
@@ -24,8 +24,12 @@
#define IDX16 R8
// temp used for copies, etc.
#define TMP R9
-// number of 32 byte chunks
+// number of 64 byte chunks
#define QWORDS R10
+// index values
+#define IDX32 R14
+#define IDX48 R15
+#define OCTWORDS R16
TEXT runtime·memmove<ABIInternal>(SB), NOSPLIT|NOFRAME, $0-24
// R3 = TGT = to
@@ -52,28 +56,46 @@ check:
// Copying forward if no overlap.
BC 12, 6, checkbytes // BEQ CR1, checkbytes
- SRDCC $2, DWORDS, QWORDS // 32 byte chunks?
- BEQ lt32gt8 // < 32 bytes
+ SRDCC $3, DWORDS, OCTWORDS // 64 byte chunks?
+ MOVD $16, IDX16
+ BEQ lt64gt8 // < 64 bytes
- // Prepare for moves of 32 bytes at a time.
+ // Prepare for moves of 64 bytes at a time.
-forward32setup:
+forward64setup:
DCBTST (TGT) // prepare data cache
DCBT (SRC)
- MOVD QWORDS, CTR // Number of 32 byte chunks
- MOVD $16, IDX16 // 16 for index
+ MOVD OCTWORDS, CTR // Number of 64 byte chunks
+ MOVD $32, IDX32
+ MOVD $48, IDX48
+ PCALIGN $32
-forward32:
- LXVD2X (R0)(SRC), VS32 // load 16 bytes
- LXVD2X (IDX16)(SRC), VS33 // load 16 bytes
- ADD $32, SRC
- STXVD2X VS32, (R0)(TGT) // store 16 bytes
+forward64:
+ LXVD2X (R0)(SRC), VS32 // load 64 bytes
+ LXVD2X (IDX16)(SRC), VS33
+ LXVD2X (IDX32)(SRC), VS34
+ LXVD2X (IDX48)(SRC), VS35
+ ADD $64, SRC
+ STXVD2X VS32, (R0)(TGT) // store 64 bytes
STXVD2X VS33, (IDX16)(TGT)
- ADD $32,TGT // bump up for next set
- BC 16, 0, forward32 // continue
- ANDCC $3, DWORDS // remaining doublewords
+ STXVD2X VS34, (IDX32)(TGT)
+ STXVD2X VS35, (IDX48)(TGT)
+ ADD $64,TGT // bump up for next set
+ BC 16, 0, forward64 // continue
+ ANDCC $7, DWORDS // remaining doublewords
BEQ checkbytes // only bytes remain
+lt64gt8:
+ CMP DWORDS, $4
+ BLT lt32gt8
+ LXVD2X (R0)(SRC), VS32
+ LXVD2X (IDX16)(SRC), VS33
+ ADD $-4, DWORDS
+ STXVD2X VS32, (R0)(TGT)
+ STXVD2X VS33, (IDX16)(TGT)
+ ADD $32, SRC
+ ADD $32, TGT
+
lt32gt8:
// At this point >= 8 and < 32
// Move 16 bytes if possible
@@ -134,7 +156,7 @@ backwardtailloop:
SUB $1,SRC
MOVBZ TMP, -1(TGT)
SUB $1,TGT
- BC 16, 0, backwardtailloop // bndz
+ BDNZ backwardtailloop
nobackwardtail:
BC 4, 5, LR // blelr cr1, return if DWORDS == 0
@@ -169,6 +191,6 @@ backward32loop:
LXVD2X (IDX16)(SRC), VS33
STXVD2X VS32, (R0)(TGT) // store 16x2 bytes
STXVD2X VS33, (IDX16)(TGT)
- BC 16, 0, backward32loop // bndz
+ BDNZ backward32loop
BC 12, 2, LR // beqlr, return if DWORDS == 0
BR backward24
diff --git a/src/runtime/memmove_riscv64.s b/src/runtime/memmove_riscv64.s
index 5dec8d0a33f..538aee36422 100644
--- a/src/runtime/memmove_riscv64.s
+++ b/src/runtime/memmove_riscv64.s
@@ -7,59 +7,61 @@
// See memmove Go doc for important implementation constraints.
// void runtime·memmove(void*, void*, uintptr)
-TEXT runtime·memmove(SB),NOSPLIT,$-0-24
- MOV to+0(FP), T0
- MOV from+8(FP), T1
- MOV n+16(FP), T2
- ADD T1, T2, T5
+TEXT runtime·memmove<ABIInternal>(SB),NOSPLIT,$-0-24
+#ifndef GOEXPERIMENT_regabiargs
+ MOV to+0(FP), A0
+ MOV from+8(FP), A1
+ MOV n+16(FP), A2
+#endif
+ ADD A1, A2, T5
// If the destination is ahead of the source, start at the end of the
// buffer and go backward.
- BLTU T1, T0, b
+ BLTU A1, A0, b
// If less than eight bytes, do one byte at a time.
- SLTU $8, T2, T3
+ SLTU $8, A2, T3
BNE T3, ZERO, f_outcheck
// Do one byte at a time until from is eight-aligned.
JMP f_aligncheck
f_align:
- MOVB (T1), T3
- MOVB T3, (T0)
- ADD $1, T0
- ADD $1, T1
+ MOVB (A1), T3
+ MOVB T3, (A0)
+ ADD $1, A0
+ ADD $1, A1
f_aligncheck:
- AND $7, T1, T3
+ AND $7, A1, T3
BNE T3, ZERO, f_align
// Do eight bytes at a time as long as there is room.
ADD $-7, T5, T6
JMP f_wordscheck
f_words:
- MOV (T1), T3
- MOV T3, (T0)
- ADD $8, T0
- ADD $8, T1
+ MOV (A1), T3
+ MOV T3, (A0)
+ ADD $8, A0
+ ADD $8, A1
f_wordscheck:
- SLTU T6, T1, T3
+ SLTU T6, A1, T3
BNE T3, ZERO, f_words
// Finish off the remaining partial word.
JMP f_outcheck
f_out:
- MOVB (T1), T3
- MOVB T3, (T0)
- ADD $1, T0
- ADD $1, T1
+ MOVB (A1), T3
+ MOVB T3, (A0)
+ ADD $1, A0
+ ADD $1, A1
f_outcheck:
- BNE T1, T5, f_out
+ BNE A1, T5, f_out
RET
b:
- ADD T0, T2, T4
+ ADD A0, A2, T4
// If less than eight bytes, do one byte at a time.
- SLTU $8, T2, T3
+ SLTU $8, A2, T3
BNE T3, ZERO, b_outcheck
// Do one byte at a time until from+n is eight-aligned.
@@ -74,7 +76,7 @@ b_aligncheck:
BNE T3, ZERO, b_align
// Do eight bytes at a time as long as there is room.
- ADD $7, T1, T6
+ ADD $7, A1, T6
JMP b_wordscheck
b_words:
ADD $-8, T4
@@ -93,6 +95,6 @@ b_out:
MOVB (T5), T3
MOVB T3, (T4)
b_outcheck:
- BNE T5, T1, b_out
+ BNE T5, A1, b_out
RET
diff --git a/src/runtime/metrics/doc.go b/src/runtime/metrics/doc.go
index 91ef03072de..63bea8c4488 100644
--- a/src/runtime/metrics/doc.go
+++ b/src/runtime/metrics/doc.go
@@ -11,7 +11,7 @@ The set of metrics defined by this package may evolve as the runtime itself
evolves, and also enables variation across Go implementations, whose relevant
metric sets may not intersect.
-Interface
+# Interface
Metrics are designated by a string key, rather than, for example, a field name in
a struct. The full list of supported metrics is always available in the slice of
@@ -30,7 +30,7 @@ In the interest of not breaking users of this package, the "kind" for a given me
is guaranteed not to change. If it must change, then a new metric will be introduced
with a new key and a new "kind."
-Metric key format
+# Metric key format
As mentioned earlier, metric keys are strings. Their format is simple and well-defined,
designed to be both human and machine readable. It is split into two components,
@@ -41,13 +41,13 @@ did also, and a new key should be introduced.
For more details on the precise definition of the metric key's path and unit formats, see
the documentation of the Name field of the Description struct.
-A note about floats
+# A note about floats
This package supports metrics whose values have a floating-point representation. In
order to improve ease-of-use, this package promises to never produce the following
classes of floating-point values: NaN, infinity.
-Supported metrics
+# Supported metrics
Below is the full list of supported metrics, ordered lexicographically.
diff --git a/src/runtime/mfinal.go b/src/runtime/mfinal.go
index 10623e4d670..bf537b417cb 100644
--- a/src/runtime/mfinal.go
+++ b/src/runtime/mfinal.go
@@ -439,6 +439,7 @@ okarg:
}
// Mark KeepAlive as noinline so that it is easily detectable as an intrinsic.
+//
//go:noinline
// KeepAlive marks its argument as currently reachable.
@@ -446,16 +447,17 @@ okarg:
// before the point in the program where KeepAlive is called.
//
// A very simplified example showing where KeepAlive is required:
-// type File struct { d int }
-// d, err := syscall.Open("/file/path", syscall.O_RDONLY, 0)
-// // ... do something if err != nil ...
-// p := &File{d}
-// runtime.SetFinalizer(p, func(p *File) { syscall.Close(p.d) })
-// var buf [10]byte
-// n, err := syscall.Read(p.d, buf[:])
-// // Ensure p is not finalized until Read returns.
-// runtime.KeepAlive(p)
-// // No more uses of p after this point.
+//
+// type File struct { d int }
+// d, err := syscall.Open("/file/path", syscall.O_RDONLY, 0)
+// // ... do something if err != nil ...
+// p := &File{d}
+// runtime.SetFinalizer(p, func(p *File) { syscall.Close(p.d) })
+// var buf [10]byte
+// n, err := syscall.Read(p.d, buf[:])
+// // Ensure p is not finalized until Read returns.
+// runtime.KeepAlive(p)
+// // No more uses of p after this point.
//
// Without the KeepAlive call, the finalizer could run at the start of
// syscall.Read, closing the file descriptor before syscall.Read makes
diff --git a/src/runtime/mgc.go b/src/runtime/mgc.go
index ba679e0af57..9f17e474889 100644
--- a/src/runtime/mgc.go
+++ b/src/runtime/mgc.go
@@ -760,7 +760,7 @@ var gcMarkDoneFlushed uint32
// This should be called when all local mark work has been drained and
// there are no remaining workers. Specifically, when
//
-// work.nwait == work.nproc && !gcMarkWorkAvailable(p)
+// work.nwait == work.nproc && !gcMarkWorkAvailable(p)
//
// The calling context must be preemptible.
//
diff --git a/src/runtime/mgcmark.go b/src/runtime/mgcmark.go
index 3e1a0b560ac..cd0ec007f3b 100644
--- a/src/runtime/mgcmark.go
+++ b/src/runtime/mgcmark.go
@@ -888,6 +888,7 @@ func scanstack(gp *g, gcw *gcWork) int64 {
}
// Scan a stack frame: local variables and function arguments/results.
+//
//go:nowritebarrier
func scanframeworker(frame *stkframe, state *stackScanState, gcw *gcWork) {
if _DebugGC > 1 && frame.continpc != 0 {
@@ -1185,6 +1186,7 @@ func gcDrainN(gcw *gcWork, scanWork int64) int64 {
// gcw.bytesMarked or gcw.heapScanWork.
//
// If stk != nil, possible stack pointers are also reported to stk.putPtr.
+//
//go:nowritebarrier
func scanblock(b0, n0 uintptr, ptrmask *uint8, gcw *gcWork, stk *stackScanState) {
// Use local copies of original parameters, so that a stack trace
@@ -1413,6 +1415,7 @@ func scanConservative(b, n uintptr, ptrmask *uint8, gcw *gcWork, state *stackSca
// Shade the object if it isn't already.
// The object is not nil and known to be in the heap.
// Preemption must be disabled.
+//
//go:nowritebarrier
func shade(b uintptr) {
if obj, span, objIndex := findObject(b, 0, 0); obj != 0 {
diff --git a/src/runtime/mgcstack.go b/src/runtime/mgcstack.go
index 49dc54e1652..472c61a491a 100644
--- a/src/runtime/mgcstack.go
+++ b/src/runtime/mgcstack.go
@@ -158,6 +158,7 @@ type stackObject struct {
}
// obj.r = r, but with no write barrier.
+//
//go:nowritebarrier
func (obj *stackObject) setRecord(r *stackObjectRecord) {
// Types of stack objects are always in read-only memory, not the heap.
diff --git a/src/runtime/mgcsweep.go b/src/runtime/mgcsweep.go
index a46f4ec2c6c..d0b81fd3dfb 100644
--- a/src/runtime/mgcsweep.go
+++ b/src/runtime/mgcsweep.go
@@ -387,7 +387,7 @@ func sweepone() uintptr {
// concurrent sweeps running, but we're at least very
// close to done sweeping.
- // Move the scavenge gen forward (signalling
+ // Move the scavenge gen forward (signaling
// that there's new work to do) and wake the scavenger.
//
// The scavenger is signaled by the last sweeper because once
@@ -424,6 +424,7 @@ func isSweepDone() bool {
}
// Returns only when span s has been swept.
+//
//go:nowritebarrier
func (s *mspan) ensureSwept() {
// Caller must disable preemption.
diff --git a/src/runtime/mgcwork.go b/src/runtime/mgcwork.go
index 56d0b1cd621..424de2fcca3 100644
--- a/src/runtime/mgcwork.go
+++ b/src/runtime/mgcwork.go
@@ -44,9 +44,9 @@ func init() {
//
// A gcWork can be used on the stack as follows:
//
-// (preemption must be disabled)
-// gcw := &getg().m.p.ptr().gcw
-// .. call gcw.put() to produce and gcw.tryGet() to consume ..
+// (preemption must be disabled)
+// gcw := &getg().m.p.ptr().gcw
+// .. call gcw.put() to produce and gcw.tryGet() to consume ..
//
// It's important that any use of gcWork during the mark phase prevent
// the garbage collector from transitioning to mark termination since
@@ -107,6 +107,7 @@ func (w *gcWork) init() {
// put enqueues a pointer for the garbage collector to trace.
// obj must point to the beginning of a heap object or an oblet.
+//
//go:nowritebarrierrec
func (w *gcWork) put(obj uintptr) {
flushed := false
@@ -145,6 +146,7 @@ func (w *gcWork) put(obj uintptr) {
// putFast does a put and reports whether it can be done quickly
// otherwise it returns false and the caller needs to call put.
+//
//go:nowritebarrierrec
func (w *gcWork) putFast(obj uintptr) bool {
wbuf := w.wbuf1
@@ -196,6 +198,7 @@ func (w *gcWork) putBatch(obj []uintptr) {
// If there are no pointers remaining in this gcWork or in the global
// queue, tryGet returns 0. Note that there may still be pointers in
// other gcWork instances or other caches.
+//
//go:nowritebarrierrec
func (w *gcWork) tryGet() uintptr {
wbuf := w.wbuf1
@@ -225,6 +228,7 @@ func (w *gcWork) tryGet() uintptr {
// tryGetFast dequeues a pointer for the garbage collector to trace
// if one is readily available. Otherwise it returns 0 and
// the caller is expected to call tryGet().
+//
//go:nowritebarrierrec
func (w *gcWork) tryGetFast() uintptr {
wbuf := w.wbuf1
@@ -278,6 +282,7 @@ func (w *gcWork) dispose() {
// balance moves some work that's cached in this gcWork back on the
// global queue.
+//
//go:nowritebarrierrec
func (w *gcWork) balance() {
if w.wbuf1 == nil {
@@ -300,6 +305,7 @@ func (w *gcWork) balance() {
}
// empty reports whether w has no mark work available.
+//
//go:nowritebarrierrec
func (w *gcWork) empty() bool {
return w.wbuf1 == nil || (w.wbuf1.nobj == 0 && w.wbuf2.nobj == 0)
@@ -340,6 +346,7 @@ func (b *workbuf) checkempty() {
// getempty pops an empty work buffer off the work.empty list,
// allocating new buffers if none are available.
+//
//go:nowritebarrier
func getempty() *workbuf {
var b *workbuf
@@ -395,6 +402,7 @@ func getempty() *workbuf {
// putempty puts a workbuf onto the work.empty list.
// Upon entry this goroutine owns b. The lfstack.push relinquishes ownership.
+//
//go:nowritebarrier
func putempty(b *workbuf) {
b.checkempty()
@@ -404,6 +412,7 @@ func putempty(b *workbuf) {
// putfull puts the workbuf on the work.full list for the GC.
// putfull accepts partially full buffers so the GC can avoid competing
// with the mutators for ownership of partially full buffers.
+//
//go:nowritebarrier
func putfull(b *workbuf) {
b.checknonempty()
@@ -412,6 +421,7 @@ func putfull(b *workbuf) {
// trygetfull tries to get a full or partially empty workbuffer.
// If one is not immediately available return nil
+//
//go:nowritebarrier
func trygetfull() *workbuf {
b := (*workbuf)(work.full.pop())
diff --git a/src/runtime/mheap.go b/src/runtime/mheap.go
index d2a63d09380..d99363d9913 100644
--- a/src/runtime/mheap.go
+++ b/src/runtime/mheap.go
@@ -319,16 +319,16 @@ type arenaHint struct {
// mSpanManual, or mSpanFree. Transitions between these states are
// constrained as follows:
//
-// * A span may transition from free to in-use or manual during any GC
-// phase.
+// - A span may transition from free to in-use or manual during any GC
+// phase.
//
-// * During sweeping (gcphase == _GCoff), a span may transition from
-// in-use to free (as a result of sweeping) or manual to free (as a
-// result of stacks being freed).
+// - During sweeping (gcphase == _GCoff), a span may transition from
+// in-use to free (as a result of sweeping) or manual to free (as a
+// result of stacks being freed).
//
-// * During GC (gcphase != _GCoff), a span *must not* transition from
-// manual or in-use to free. Because concurrent GC may read a pointer
-// and then look up its span, the span state must be monotonic.
+// - During GC (gcphase != _GCoff), a span *must not* transition from
+// manual or in-use to free. Because concurrent GC may read a pointer
+// and then look up its span, the span state must be monotonic.
//
// Setting mspan.state to mSpanInUse or mSpanManual must be done
// atomically and only after all other span fields are valid.
@@ -589,6 +589,7 @@ func (i arenaIdx) l2() uint {
// inheap reports whether b is a pointer into a (potentially dead) heap object.
// It returns false for pointers into mSpanManual spans.
// Non-preemptible because it is used by write barriers.
+//
//go:nowritebarrier
//go:nosplit
func inheap(b uintptr) bool {
diff --git a/src/runtime/mpagealloc_64bit.go b/src/runtime/mpagealloc_64bit.go
index 1bacfbe0fa5..76b54baa556 100644
--- a/src/runtime/mpagealloc_64bit.go
+++ b/src/runtime/mpagealloc_64bit.go
@@ -41,7 +41,8 @@ var levelBits = [summaryLevels]uint{
//
// With levelShift, one can compute the index of the summary at level l related to a
// pointer p by doing:
-// p >> levelShift[l]
+//
+// p >> levelShift[l]
var levelShift = [summaryLevels]uint{
heapAddrBits - summaryL0Bits,
heapAddrBits - summaryL0Bits - 1*summaryLevelBits,
diff --git a/src/runtime/msan.go b/src/runtime/msan.go
index 902a1e9e744..c4852165830 100644
--- a/src/runtime/msan.go
+++ b/src/runtime/msan.go
@@ -54,6 +54,7 @@ func msanfree(addr unsafe.Pointer, sz uintptr)
func msanmove(dst, src unsafe.Pointer, sz uintptr)
// These are called from msan_GOARCH.s
+//
//go:cgo_import_static __msan_read_go
//go:cgo_import_static __msan_write_go
//go:cgo_import_static __msan_malloc_go
diff --git a/src/runtime/mstats.go b/src/runtime/mstats.go
index e5c3471ca39..e8b42fbbbef 100644
--- a/src/runtime/mstats.go
+++ b/src/runtime/mstats.go
@@ -550,6 +550,7 @@ func readGCStats(pauses *[]uint64) {
// readGCStats_m must be called on the system stack because it acquires the heap
// lock. See mheap for details.
+//
//go:systemstack
func readGCStats_m(pauses *[]uint64) {
p := *pauses
@@ -622,6 +623,7 @@ type sysMemStat uint64
// load atomically reads the value of the stat.
//
// Must be nosplit as it is called in runtime initialization, e.g. newosproc0.
+//
//go:nosplit
func (s *sysMemStat) load() uint64 {
return atomic.Load64((*uint64)(s))
@@ -630,6 +632,7 @@ func (s *sysMemStat) load() uint64 {
// add atomically adds the sysMemStat by n.
//
// Must be nosplit as it is called in runtime initialization, e.g. newosproc0.
+//
//go:nosplit
func (s *sysMemStat) add(n int64) {
if s == nil {
diff --git a/src/runtime/mwbbuf.go b/src/runtime/mwbbuf.go
index 78d9382620b..39ce0b46a90 100644
--- a/src/runtime/mwbbuf.go
+++ b/src/runtime/mwbbuf.go
@@ -116,11 +116,11 @@ func (b *wbBuf) empty() bool {
// putFast adds old and new to the write barrier buffer and returns
// false if a flush is necessary. Callers should use this as:
//
-// buf := &getg().m.p.ptr().wbBuf
-// if !buf.putFast(old, new) {
-// wbBufFlush(...)
-// }
-// ... actual memory write ...
+// buf := &getg().m.p.ptr().wbBuf
+// if !buf.putFast(old, new) {
+// wbBufFlush(...)
+// }
+// ... actual memory write ...
//
// The arguments to wbBufFlush depend on whether the caller is doing
// its own cgo pointer checks. If it is, then this can be
diff --git a/src/runtime/netpoll.go b/src/runtime/netpoll.go
index 864148b7150..ac6bc89530e 100644
--- a/src/runtime/netpoll.go
+++ b/src/runtime/netpoll.go
@@ -47,16 +47,17 @@ const (
// 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 pdReady,
-// 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 pdReady or nil respectively
-// and unparks the goroutine.
-// nil - none of the above.
+//
+// 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 pdReady,
+// 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 pdReady or nil respectively
+// and unparks the goroutine.
+// nil - none of the above.
const (
pdReady uintptr = 1
pdWait uintptr = 2
@@ -271,6 +272,7 @@ func (c *pollCache) free(pd *pollDesc) {
// poll_runtime_pollReset, which is internal/poll.runtime_pollReset,
// prepares a descriptor for polling in mode, which is 'r' or 'w'.
// This returns an error code; the codes are defined above.
+//
//go:linkname poll_runtime_pollReset internal/poll.runtime_pollReset
func poll_runtime_pollReset(pd *pollDesc, mode int) int {
errcode := netpollcheckerr(pd, int32(mode))
@@ -289,6 +291,7 @@ func poll_runtime_pollReset(pd *pollDesc, mode int) int {
// waits for a descriptor to be ready for reading or writing,
// according to mode, which is 'r' or 'w'.
// This returns an error code; the codes are defined above.
+//
//go:linkname poll_runtime_pollWait internal/poll.runtime_pollWait
func poll_runtime_pollWait(pd *pollDesc, mode int) int {
errcode := netpollcheckerr(pd, int32(mode))
@@ -438,6 +441,7 @@ func poll_runtime_pollUnblock(pd *pollDesc) {
// whether the fd is ready for reading or writing or both.
//
// This may run while the world is stopped, so write barriers are not allowed.
+//
//go:nowritebarrier
func netpollready(toRun *gList, pd *pollDesc, mode int32) {
var rg, wg *g
diff --git a/src/runtime/netpoll_aix.go b/src/runtime/netpoll_aix.go
index 90950af4447..22cc5138815 100644
--- a/src/runtime/netpoll_aix.go
+++ b/src/runtime/netpoll_aix.go
@@ -146,6 +146,7 @@ func netpollBreak() {
// delay < 0: blocks indefinitely
// delay == 0: does not block, just polls
// delay > 0: block for up to that many nanoseconds
+//
//go:nowritebarrierrec
func netpoll(delay int64) gList {
var timeout uintptr
diff --git a/src/runtime/norace_linux_test.go b/src/runtime/norace_linux_test.go
index b188a2e88b7..3521b24655b 100644
--- a/src/runtime/norace_linux_test.go
+++ b/src/runtime/norace_linux_test.go
@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file.
// The file contains tests that cannot run under race detector for some reason.
+//
//go:build !race
package runtime_test
diff --git a/src/runtime/norace_test.go b/src/runtime/norace_test.go
index d49f2ec0dfd..3b5eca5341e 100644
--- a/src/runtime/norace_test.go
+++ b/src/runtime/norace_test.go
@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file.
// The file contains tests that cannot run under race detector for some reason.
+//
//go:build !race
package runtime_test
diff --git a/src/runtime/os2_aix.go b/src/runtime/os2_aix.go
index 4d77f0de6dd..9ad1caa8161 100644
--- a/src/runtime/os2_aix.go
+++ b/src/runtime/os2_aix.go
@@ -452,6 +452,7 @@ func pipe() (r, w int32, errno int32) {
// assembly routine; the higher bits (if required), should be provided
// by the assembly routine as 0.
// The err result is an OS error code such as ENOMEM.
+//
//go:nosplit
func mmap(addr unsafe.Pointer, n uintptr, prot, flags, fd int32, off uint32) (unsafe.Pointer, int) {
r, err0 := syscall6(&libc_mmap, uintptr(addr), uintptr(n), uintptr(prot), uintptr(flags), uintptr(fd), uintptr(off))
diff --git a/src/runtime/os3_solaris.go b/src/runtime/os3_solaris.go
index f465a3aa3f5..8c85b71532d 100644
--- a/src/runtime/os3_solaris.go
+++ b/src/runtime/os3_solaris.go
@@ -141,6 +141,7 @@ func osinit() {
func tstart_sysvicall(newm *m) uint32
// May run with m.p==nil, so write barriers are not allowed.
+//
//go:nowritebarrier
func newosproc(mp *m) {
var (
@@ -267,6 +268,7 @@ func getsig(i uint32) uintptr {
}
// setSignaltstackSP sets the ss_sp field of a stackt.
+//
//go:nosplit
func setSignalstackSP(s *stackt, sp uintptr) {
*(*uintptr)(unsafe.Pointer(&s.ss_sp)) = sp
diff --git a/src/runtime/os_aix.go b/src/runtime/os_aix.go
index 292ff947956..15e4929779a 100644
--- a/src/runtime/os_aix.go
+++ b/src/runtime/os_aix.go
@@ -150,6 +150,7 @@ var failthreadcreate = []byte("runtime: failed to create new OS thread\n")
// Called to do synchronous initialization of Go code built with
// -buildmode=c-archive or -buildmode=c-shared.
// None of the Go runtime is initialized.
+//
//go:nosplit
//go:nowritebarrierrec
func libpreinit() {
@@ -296,6 +297,7 @@ func getsig(i uint32) uintptr {
}
// setSignaltstackSP sets the ss_sp field of a stackt.
+//
//go:nosplit
func setSignalstackSP(s *stackt, sp uintptr) {
*(*uintptr)(unsafe.Pointer(&s.ss_sp)) = sp
diff --git a/src/runtime/os_darwin.go b/src/runtime/os_darwin.go
index 9065b76375d..8562d7d9069 100644
--- a/src/runtime/os_darwin.go
+++ b/src/runtime/os_darwin.go
@@ -195,6 +195,7 @@ func goenvs() {
}
// May run with m.p==nil, so write barriers are not allowed.
+//
//go:nowritebarrierrec
func newosproc(mp *m) {
stk := unsafe.Pointer(mp.g0.stack.hi)
@@ -292,6 +293,7 @@ var failthreadcreate = []byte("runtime: failed to create new OS thread\n")
// Called to do synchronous initialization of Go code built with
// -buildmode=c-archive or -buildmode=c-shared.
// None of the Go runtime is initialized.
+//
//go:nosplit
//go:nowritebarrierrec
func libpreinit() {
@@ -324,6 +326,7 @@ func minit() {
}
// Called from dropm to undo the effect of an minit.
+//
//go:nosplit
func unminit() {
// iOS does not support alternate signal stack.
@@ -410,6 +413,7 @@ func getsig(i uint32) uintptr {
}
// setSignaltstackSP sets the ss_sp field of a stackt.
+//
//go:nosplit
func setSignalstackSP(s *stackt, sp uintptr) {
*(*uintptr)(unsafe.Pointer(&s.ss_sp)) = sp
diff --git a/src/runtime/os_dragonfly.go b/src/runtime/os_dragonfly.go
index a56706b4151..83478143b93 100644
--- a/src/runtime/os_dragonfly.go
+++ b/src/runtime/os_dragonfly.go
@@ -142,6 +142,7 @@ func futexwakeup(addr *uint32, cnt uint32) {
func lwp_start(uintptr)
// May run with m.p==nil, so write barriers are not allowed.
+//
//go:nowritebarrier
func newosproc(mp *m) {
stk := unsafe.Pointer(mp.g0.stack.hi)
@@ -201,6 +202,7 @@ func minit() {
}
// Called from dropm to undo the effect of an minit.
+//
//go:nosplit
func unminit() {
unminitSignals()
@@ -247,6 +249,7 @@ func getsig(i uint32) uintptr {
}
// setSignaltstackSP sets the ss_sp field of a stackt.
+//
//go:nosplit
func setSignalstackSP(s *stackt, sp uintptr) {
s.ss_sp = sp
diff --git a/src/runtime/os_freebsd.go b/src/runtime/os_freebsd.go
index e4d15474d82..23efd1a46e8 100644
--- a/src/runtime/os_freebsd.go
+++ b/src/runtime/os_freebsd.go
@@ -192,6 +192,7 @@ func futexwakeup(addr *uint32, cnt uint32) {
func thr_start()
// May run with m.p==nil, so write barriers are not allowed.
+//
//go:nowritebarrier
func newosproc(mp *m) {
stk := unsafe.Pointer(mp.g0.stack.hi)
@@ -221,6 +222,7 @@ func newosproc(mp *m) {
}
// Version of newosproc that doesn't require a valid G.
+//
//go:nosplit
func newosproc0(stacksize uintptr, fn unsafe.Pointer) {
stack := sysAlloc(stacksize, &memstats.stacks_sys)
@@ -261,6 +263,7 @@ var failthreadcreate = []byte("runtime: failed to create new OS thread\n")
// Called to do synchronous initialization of Go code built with
// -buildmode=c-archive or -buildmode=c-shared.
// None of the Go runtime is initialized.
+//
//go:nosplit
//go:nowritebarrierrec
func libpreinit() {
@@ -318,6 +321,7 @@ func minit() {
}
// Called from dropm to undo the effect of an minit.
+//
//go:nosplit
func unminit() {
unminitSignals()
@@ -359,6 +363,7 @@ func getsig(i uint32) uintptr {
}
// setSignaltstackSP sets the ss_sp field of a stackt.
+//
//go:nosplit
func setSignalstackSP(s *stackt, sp uintptr) {
s.ss_sp = sp
@@ -431,6 +436,7 @@ func sysauxv(auxv []uintptr) {
}
// sysSigaction calls the sigaction system call.
+//
//go:nosplit
func sysSigaction(sig uint32, new, old *sigactiont) {
// Use system stack to avoid split stack overflow on amd64
@@ -442,6 +448,7 @@ func sysSigaction(sig uint32, new, old *sigactiont) {
}
// asmSigaction is implemented in assembly.
+//
//go:noescape
func asmSigaction(sig uintptr, new, old *sigactiont) int32
diff --git a/src/runtime/os_js.go b/src/runtime/os_js.go
index 9ed916705bc..7ec1210b73c 100644
--- a/src/runtime/os_js.go
+++ b/src/runtime/os_js.go
@@ -126,6 +126,7 @@ func initsig(preinit bool) {
}
// May run with m.p==nil, so write barriers are not allowed.
+//
//go:nowritebarrier
func newosproc(mp *m) {
panic("newosproc: not implemented")
diff --git a/src/runtime/os_linux.go b/src/runtime/os_linux.go
index efb54ff20e3..a6e7a331915 100644
--- a/src/runtime/os_linux.go
+++ b/src/runtime/os_linux.go
@@ -52,9 +52,12 @@ const (
)
// Atomically,
+//
// if(*addr == val) sleep
+//
// Might be woken up spuriously; that's allowed.
// Don't sleep longer than ns; ns < 0 means forever.
+//
//go:nosplit
func futexsleep(addr *uint32, val uint32, ns int64) {
// Some Linux kernels have a bug where futex of
@@ -73,6 +76,7 @@ func futexsleep(addr *uint32, val uint32, ns int64) {
}
// If any procs are sleeping on addr, wake up at most cnt.
+//
//go:nosplit
func futexwakeup(addr *uint32, cnt uint32) {
ret := futex(unsafe.Pointer(addr), _FUTEX_WAKE_PRIVATE, cnt, nil, nil, 0)
@@ -157,6 +161,7 @@ const (
func clone(flags int32, stk, mp, gp, fn unsafe.Pointer) int32
// May run with m.p==nil, so write barriers are not allowed.
+//
//go:nowritebarrier
func newosproc(mp *m) {
stk := unsafe.Pointer(mp.g0.stack.hi)
@@ -184,6 +189,7 @@ func newosproc(mp *m) {
}
// Version of newosproc that doesn't require a valid G.
+//
//go:nosplit
func newosproc0(stacksize uintptr, fn unsafe.Pointer) {
stack := sysAlloc(stacksize, &memstats.stacks_sys)
@@ -365,6 +371,7 @@ func goenvs() {
// Called to do synchronous initialization of Go code built with
// -buildmode=c-archive or -buildmode=c-shared.
// None of the Go runtime is initialized.
+//
//go:nosplit
//go:nowritebarrierrec
func libpreinit() {
@@ -392,6 +399,7 @@ func minit() {
}
// Called from dropm to undo the effect of an minit.
+//
//go:nosplit
func unminit() {
unminitSignals()
@@ -497,6 +505,7 @@ func getsig(i uint32) uintptr {
}
// setSignaltstackSP sets the ss_sp field of a stackt.
+//
//go:nosplit
func setSignalstackSP(s *stackt, sp uintptr) {
*(*uintptr)(unsafe.Pointer(&s.ss_sp)) = sp
@@ -507,6 +516,7 @@ func (c *sigctxt) fixsigcode(sig uint32) {
}
// sysSigaction calls the rt_sigaction system call.
+//
//go:nosplit
func sysSigaction(sig uint32, new, old *sigactiont) {
if rt_sigaction(uintptr(sig), new, old, unsafe.Sizeof(sigactiont{}.sa_mask)) != 0 {
@@ -531,6 +541,7 @@ func sysSigaction(sig uint32, new, old *sigactiont) {
}
// rt_sigaction is implemented in assembly.
+//
//go:noescape
func rt_sigaction(sig uintptr, new, old *sigactiont, size uintptr) int32
diff --git a/src/runtime/os_netbsd.go b/src/runtime/os_netbsd.go
index 88a4a8b90e5..3cbace38f99 100644
--- a/src/runtime/os_netbsd.go
+++ b/src/runtime/os_netbsd.go
@@ -201,6 +201,7 @@ func semawakeup(mp *m) {
}
// May run with m.p==nil, so write barriers are not allowed.
+//
//go:nowritebarrier
func newosproc(mp *m) {
stk := unsafe.Pointer(mp.g0.stack.hi)
@@ -248,6 +249,7 @@ func netbsdMstart()
// baroque to remove a signal stack here only to add one in minit, but
// it's a simple change that keeps NetBSD working like other OS's.
// At this point all signals are blocked, so there is no race.
+//
//go:nosplit
func netbsdMstart0() {
st := stackt{ss_flags: _SS_DISABLE}
@@ -304,6 +306,7 @@ func minit() {
}
// Called from dropm to undo the effect of an minit.
+//
//go:nosplit
func unminit() {
unminitSignals()
@@ -350,6 +353,7 @@ func getsig(i uint32) uintptr {
}
// setSignaltstackSP sets the ss_sp field of a stackt.
+//
//go:nosplit
func setSignalstackSP(s *stackt, sp uintptr) {
s.ss_sp = sp
diff --git a/src/runtime/os_openbsd.go b/src/runtime/os_openbsd.go
index 1a00b890db1..2383dc84280 100644
--- a/src/runtime/os_openbsd.go
+++ b/src/runtime/os_openbsd.go
@@ -168,6 +168,7 @@ func minit() {
}
// Called from dropm to undo the effect of an minit.
+//
//go:nosplit
func unminit() {
unminitSignals()
@@ -214,6 +215,7 @@ func getsig(i uint32) uintptr {
}
// setSignaltstackSP sets the ss_sp field of a stackt.
+//
//go:nosplit
func setSignalstackSP(s *stackt, sp uintptr) {
s.ss_sp = sp
diff --git a/src/runtime/os_openbsd_libc.go b/src/runtime/os_openbsd_libc.go
index ff21eccb4b5..4ad2a061bd8 100644
--- a/src/runtime/os_openbsd_libc.go
+++ b/src/runtime/os_openbsd_libc.go
@@ -17,6 +17,7 @@ var failThreadCreate = []byte("runtime: failed to create new OS thread\n")
func mstart_stub()
// May run with m.p==nil, so write barriers are not allowed.
+//
//go:nowritebarrierrec
func newosproc(mp *m) {
if false {
diff --git a/src/runtime/os_openbsd_syscall.go b/src/runtime/os_openbsd_syscall.go
index 8128c20453b..9d67a7ebbda 100644
--- a/src/runtime/os_openbsd_syscall.go
+++ b/src/runtime/os_openbsd_syscall.go
@@ -16,6 +16,7 @@ import (
func tfork(param *tforkt, psize uintptr, mm *m, gg *g, fn uintptr) int32
// May run with m.p==nil, so write barriers are not allowed.
+//
//go:nowritebarrier
func newosproc(mp *m) {
stk := unsafe.Pointer(mp.g0.stack.hi)
diff --git a/src/runtime/os_openbsd_syscall2.go b/src/runtime/os_openbsd_syscall2.go
index a48f5fa88a6..e4c9d2fe893 100644
--- a/src/runtime/os_openbsd_syscall2.go
+++ b/src/runtime/os_openbsd_syscall2.go
@@ -39,6 +39,7 @@ func usleep_no_g(usec uint32) {
// write calls the write system call.
// It returns a non-negative number of bytes written or a negative errno value.
+//
//go:noescape
func write1(fd uintptr, p unsafe.Pointer, n int32) int32
diff --git a/src/runtime/os_plan9.go b/src/runtime/os_plan9.go
index 975d460a7da..1a0c0e9363e 100644
--- a/src/runtime/os_plan9.go
+++ b/src/runtime/os_plan9.go
@@ -444,6 +444,7 @@ func exit(e int32) {
}
// May run with m.p==nil, so write barriers are not allowed.
+//
//go:nowritebarrier
func newosproc(mp *m) {
if false {
@@ -506,6 +507,7 @@ func write1(fd uintptr, buf unsafe.Pointer, n int32) int32 {
var _badsignal = []byte("runtime: signal received on thread not created by Go.\n")
// This runs on a foreign stack, without an m or a g. No stack split.
+//
//go:nosplit
func badsignal2() {
pwrite(2, unsafe.Pointer(&_badsignal[0]), int32(len(_badsignal)), -1)
diff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go
index c76add78025..2f6ec75cf89 100644
--- a/src/runtime/os_windows.go
+++ b/src/runtime/os_windows.go
@@ -902,6 +902,7 @@ func semacreate(mp *m) {
// May run with m.p==nil, so write barriers are not allowed. This
// function is called by newosproc0, so it is also required to
// operate without stack guards.
+//
//go:nowritebarrierrec
//go:nosplit
func newosproc(mp *m) {
@@ -930,6 +931,7 @@ func newosproc(mp *m) {
// Used by the C library build mode. On Linux this function would allocate a
// stack, but that's not necessary for Windows. No stack guards are present
// and the GC has not been initialized, so write barriers will fail.
+//
//go:nowritebarrierrec
//go:nosplit
func newosproc0(mp *m, stk unsafe.Pointer) {
@@ -1019,6 +1021,7 @@ func minit() {
}
// Called from dropm to undo the effect of an minit.
+//
//go:nosplit
func unminit() {
mp := getg().m
@@ -1032,6 +1035,7 @@ func unminit() {
// Called from exitm, but not from drop, to undo the effect of thread-owned
// resources in minit, semacreate, or elsewhere. Do not take locks after calling this.
+//
//go:nosplit
func mdestroy(mp *m) {
if mp.highResTimer != 0 {
@@ -1050,6 +1054,7 @@ func mdestroy(mp *m) {
// Calling stdcall on os stack.
// May run during STW, so write barriers are not allowed.
+//
//go:nowritebarrier
//go:nosplit
func stdcall(fn stdFunction) uintptr {
diff --git a/src/runtime/panic.go b/src/runtime/panic.go
index 6600410cb6c..e4cc7bfb315 100644
--- a/src/runtime/panic.go
+++ b/src/runtime/panic.go
@@ -525,8 +525,14 @@ func Goexit() {
// Used when crashing with panicking.
func preprintpanics(p *_panic) {
defer func() {
- if recover() != nil {
- throw("panic while printing panic value")
+ text := "panic while printing panic value"
+ switch r := recover().(type) {
+ case nil:
+ // nothing to do
+ case string:
+ throw(text + ": " + r)
+ default:
+ throw(text + ": type " + efaceOf(&r)._type.string())
}
}()
for p != nil {
@@ -944,6 +950,7 @@ func gopanic(e any) {
// getargp returns the location where the caller
// writes outgoing function call arguments.
+//
//go:nosplit
//go:noinline
func getargp() uintptr {
@@ -956,6 +963,7 @@ func getargp() uintptr {
//
// TODO(rsc): Once we commit to CopyStackAlways,
// this doesn't need to be nosplit.
+//
//go:nosplit
func gorecover(argp uintptr) any {
// Must be in a function running as part of a deferred call during the panic.
diff --git a/src/runtime/pprof/pprof.go b/src/runtime/pprof/pprof.go
index e3cd6b9d2a2..f0b25c131fe 100644
--- a/src/runtime/pprof/pprof.go
+++ b/src/runtime/pprof/pprof.go
@@ -5,7 +5,7 @@
// Package pprof writes runtime profiling data in the format expected
// by the pprof visualization tool.
//
-// Profiling a Go program
+// # Profiling a Go program
//
// The first step to profiling a Go program is to enable profiling.
// Support for profiling benchmarks built with the standard testing
@@ -13,54 +13,54 @@
// runs benchmarks in the current directory and writes the CPU and
// memory profiles to cpu.prof and mem.prof:
//
-// go test -cpuprofile cpu.prof -memprofile mem.prof -bench .
+// go test -cpuprofile cpu.prof -memprofile mem.prof -bench .
//
// To add equivalent profiling support to a standalone program, add
// code like the following to your main function:
//
-// var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`")
-// var memprofile = flag.String("memprofile", "", "write memory profile to `file`")
+// var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`")
+// var memprofile = flag.String("memprofile", "", "write memory profile to `file`")
//
-// func main() {
-// flag.Parse()
-// if *cpuprofile != "" {
-// f, err := os.Create(*cpuprofile)
-// if err != nil {
-// log.Fatal("could not create CPU profile: ", err)
-// }
-// defer f.Close() // error handling omitted for example
-// if err := pprof.StartCPUProfile(f); err != nil {
-// log.Fatal("could not start CPU profile: ", err)
-// }
-// defer pprof.StopCPUProfile()
-// }
+// func main() {
+// flag.Parse()
+// if *cpuprofile != "" {
+// f, err := os.Create(*cpuprofile)
+// if err != nil {
+// log.Fatal("could not create CPU profile: ", err)
+// }
+// defer f.Close() // error handling omitted for example
+// if err := pprof.StartCPUProfile(f); err != nil {
+// log.Fatal("could not start CPU profile: ", err)
+// }
+// defer pprof.StopCPUProfile()
+// }
//
-// // ... rest of the program ...
+// // ... rest of the program ...
//
-// if *memprofile != "" {
-// f, err := os.Create(*memprofile)
-// if err != nil {
-// log.Fatal("could not create memory profile: ", err)
-// }
-// defer f.Close() // error handling omitted for example
-// runtime.GC() // get up-to-date statistics
-// if err := pprof.WriteHeapProfile(f); err != nil {
-// log.Fatal("could not write memory profile: ", err)
-// }
-// }
-// }
+// if *memprofile != "" {
+// f, err := os.Create(*memprofile)
+// if err != nil {
+// log.Fatal("could not create memory profile: ", err)
+// }
+// defer f.Close() // error handling omitted for example
+// runtime.GC() // get up-to-date statistics
+// if err := pprof.WriteHeapProfile(f); err != nil {
+// log.Fatal("could not write memory profile: ", err)
+// }
+// }
+// }
//
// There is also a standard HTTP interface to profiling data. Adding
// the following line will install handlers under the /debug/pprof/
// URL to download live profiles:
//
-// import _ "net/http/pprof"
+// import _ "net/http/pprof"
//
// See the net/http/pprof package for more details.
//
// Profiles can then be visualized with the pprof tool:
//
-// go tool pprof cpu.prof
+// go tool pprof cpu.prof
//
// There are many commands available from the pprof command line.
// Commonly used commands include "top", which prints a summary of the
diff --git a/src/runtime/pprof/pprof_test.go b/src/runtime/pprof/pprof_test.go
index ff4ecb4c684..1742dc0cdcb 100644
--- a/src/runtime/pprof/pprof_test.go
+++ b/src/runtime/pprof/pprof_test.go
@@ -1175,6 +1175,7 @@ func blockInfrequentLong(rate int) {
}
// Used by TestBlockProfileBias.
+//
//go:linkname blockevent runtime.blockevent
func blockevent(cycles int64, skip int)
diff --git a/src/runtime/pprof/proto.go b/src/runtime/pprof/proto.go
index 68dac42d200..f0769935ae8 100644
--- a/src/runtime/pprof/proto.go
+++ b/src/runtime/pprof/proto.go
@@ -56,9 +56,10 @@ type memMap struct {
}
// symbolizeFlag keeps track of symbolization result.
-// 0 : no symbol lookup was performed
-// 1<<0 (lookupTried) : symbol lookup was performed
-// 1<<1 (lookupFailed): symbol lookup was performed but failed
+//
+// 0 : no symbol lookup was performed
+// 1<<0 (lookupTried) : symbol lookup was performed
+// 1<<1 (lookupFailed): symbol lookup was performed but failed
type symbolizeFlag uint8
const (
@@ -507,9 +508,10 @@ func (b *profileBuilder) appendLocsForStack(locs []uint64, stk []uintptr) (newLo
// and looking up debug info is not ideal, so we use a heuristic to filter
// the fake pcs and restore the inlined and entry functions. Inlined functions
// have the following properties:
-// Frame's Func is nil (note: also true for non-Go functions), and
-// Frame's Entry matches its entry function frame's Entry (note: could also be true for recursive calls and non-Go functions), and
-// Frame's Name does not match its entry function frame's name (note: inlined functions cannot be directly recursive).
+//
+// Frame's Func is nil (note: also true for non-Go functions), and
+// Frame's Entry matches its entry function frame's Entry (note: could also be true for recursive calls and non-Go functions), and
+// Frame's Name does not match its entry function frame's name (note: inlined functions cannot be directly recursive).
//
// As reading and processing the pcs in a stack trace one by one (from leaf to the root),
// we use pcDeck to temporarily hold the observed pcs and their expanded frames
diff --git a/src/runtime/proc.go b/src/runtime/proc.go
index f9f82f38675..4535f620532 100644
--- a/src/runtime/proc.go
+++ b/src/runtime/proc.go
@@ -277,6 +277,7 @@ func main() {
}
// os_beforeExit is called from os.Exit(0).
+//
//go:linkname os_beforeExit os.runtime_beforeExit
func os_beforeExit() {
if raceenabled {
@@ -319,6 +320,7 @@ func Gosched() {
// goschedguarded yields the processor like gosched, but also checks
// for forbidden states and opts out of the yield in those cases.
+//
//go:nosplit
func goschedguarded() {
mcall(goschedguarded_m)
@@ -894,6 +896,7 @@ func freezetheworld() {
// All reads and writes of g's status go through readgstatus, casgstatus
// castogscanstatus, casfrom_Gscanstatus.
+//
//go:nosplit
func readgstatus(gp *g) uint32 {
return atomic.Load(&gp.atomicstatus)
@@ -955,6 +958,7 @@ func castogscanstatus(gp *g, oldval, newval uint32) bool {
// and casfrom_Gscanstatus 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.
+//
//go:nosplit
func casgstatus(gp *g, oldval, newval uint32) {
if (oldval&_Gscan != 0) || (newval&_Gscan != 0) || oldval == newval {
@@ -1028,6 +1032,7 @@ func casgstatus(gp *g, oldval, newval uint32) {
// async wakeup that might come in from netpoll. If we see Gwaiting from the readgstatus,
// it might have become Grunnable by the time we get to the cas. If we called casgstatus,
// it would loop waiting for the status to go back to Gwaiting, which it never will.
+//
//go:nosplit
func casgcopystack(gp *g) uint32 {
for {
@@ -1387,6 +1392,7 @@ func mstart0() {
// The go:noinline is to guarantee the getcallerpc/getcallersp below are safe,
// so that we can set up g0.sched to return to the call of mstart1 above.
+//
//go:noinline
func mstart1() {
_g_ := getg()
@@ -1443,6 +1449,7 @@ func mstartm0() {
}
// mPark causes a thread to park itself, returning once woken.
+//
//go:nosplit
func mPark() {
gp := getg()
@@ -1657,9 +1664,9 @@ func forEachP(fn func(*p)) {
// runSafePointFn runs the safe point function, if any, for this P.
// This should be called like
//
-// if getg().m.p.runSafePointFn != 0 {
-// runSafePointFn()
-// }
+// if getg().m.p.runSafePointFn != 0 {
+// runSafePointFn()
+// }
//
// runSafePointFn must be checked on any transition in to _Pidle or
// _Psyscall to avoid a race where forEachP sees that the P is running
@@ -1795,6 +1802,7 @@ func allocm(_p_ *p, fn func(), id int64) *m {
//
// When the callback is done with the m, it calls dropm to
// put the m back on the list.
+//
//go:nosplit
func needm() {
if (iscgo || GOOS == "windows") && !cgoHasExtraM {
@@ -2000,6 +2008,7 @@ var extraMWaiters uint32
// to extram. If nilokay is true, then lockextra will
// return a nil list head if that's what it finds. If nilokay is false,
// lockextra will keep waiting until the list head is no longer nil.
+//
//go:nosplit
func lockextra(nilokay bool) *m {
const locked = 1
@@ -2073,6 +2082,7 @@ var newmHandoff struct {
// May run with m.p==nil, so write barriers are not allowed.
//
// id is optional pre-allocated m ID. Omit by passing -1.
+//
//go:nowritebarrierrec
func newm(fn func(), _p_ *p, id int64) {
// allocm adds a new M to allm, but they do not start until created by
@@ -2245,6 +2255,7 @@ func mspinning() {
// comment on acquirem below.
//
// Must not have write barriers because this may be called without a P.
+//
//go:nowritebarrierrec
func startm(_p_ *p, spinning bool) {
// Disable preemption.
@@ -2329,6 +2340,7 @@ func startm(_p_ *p, spinning bool) {
// Hands off P from syscall or locked M.
// Always runs without a P, so write barriers are not allowed.
+//
//go:nowritebarrierrec
func handoffp(_p_ *p) {
// handoffp must start an M in any situation where
@@ -2432,6 +2444,7 @@ func stoplockedm() {
// Schedules the locked m to run the locked gp.
// May run during STW, so write barriers are not allowed.
+//
//go:nowritebarrierrec
func startlockedm(gp *g) {
_g_ := getg()
@@ -3248,6 +3261,7 @@ func dropg() {
// If the time when the next timer should run is not 0,
// it is always larger than the returned time.
// We pass now in and out to avoid extra calls of nanotime.
+//
//go:yeswritebarrierrec
func checkTimers(pp *p, now int64) (rnow, pollUntil int64, ran bool) {
// If it's not yet time for the first timer, or the first adjusted
@@ -3680,6 +3694,7 @@ func entersyscall_gcwait() {
}
// The same as entersyscall(), but with a hint that the syscall is blocking.
+//
//go:nosplit
func entersyscallblock() {
_g_ := getg()
@@ -3939,6 +3954,7 @@ func exitsyscall0(gp *g) {
}
// Called from syscall package before fork.
+//
//go:linkname syscall_runtime_BeforeFork syscall.runtime_BeforeFork
//go:nosplit
func syscall_runtime_BeforeFork() {
@@ -3959,6 +3975,7 @@ func syscall_runtime_BeforeFork() {
}
// Called from syscall package after fork in parent.
+//
//go:linkname syscall_runtime_AfterFork syscall.runtime_AfterFork
//go:nosplit
func syscall_runtime_AfterFork() {
@@ -4009,6 +4026,7 @@ func syscall_runtime_AfterForkInChild() {
var pendingPreemptSignals uint32
// Called from syscall package before Exec.
+//
//go:linkname syscall_runtime_BeforeExec syscall.runtime_BeforeExec
func syscall_runtime_BeforeExec() {
// Prevent thread creation during exec.
@@ -4024,6 +4042,7 @@ func syscall_runtime_BeforeExec() {
}
// Called from syscall package after Exec.
+//
//go:linkname syscall_runtime_AfterExec syscall.runtime_AfterExec
func syscall_runtime_AfterExec() {
execLock.unlock()
@@ -4305,6 +4324,7 @@ func Breakpoint() {
// dolockOSThread is called by LockOSThread and lockOSThread below
// after they modify m.locked. Do not allow preemption during this call,
// or else the m might be different in this function than in the caller.
+//
//go:nosplit
func dolockOSThread() {
if GOARCH == "wasm" {
@@ -4356,6 +4376,7 @@ func lockOSThread() {
// dounlockOSThread is called by UnlockOSThread and unlockOSThread below
// after they update m->locked. Do not allow preemption during this call,
// or else the m might be in different in this function than in the caller.
+//
//go:nosplit
func dounlockOSThread() {
if GOARCH == "wasm" {
@@ -4438,6 +4459,7 @@ func _VDSO() { _VDSO() }
// Called if we receive a SIGPROF signal.
// Called by the signal handler, may run during STW.
+//
//go:nowritebarrierrec
func sigprof(pc, sp, lr uintptr, gp *g, mp *m) {
if prof.hz == 0 {
@@ -5446,6 +5468,7 @@ func schedEnabled(gp *g) bool {
// Put mp on midle list.
// sched.lock must be held.
// May run during STW, so write barriers are not allowed.
+//
//go:nowritebarrierrec
func mput(mp *m) {
assertLockHeld(&sched.lock)
@@ -5459,6 +5482,7 @@ func mput(mp *m) {
// Try to get an m from midle list.
// sched.lock must be held.
// May run during STW, so write barriers are not allowed.
+//
//go:nowritebarrierrec
func mget() *m {
assertLockHeld(&sched.lock)
@@ -5474,6 +5498,7 @@ func mget() *m {
// Put gp on the global runnable queue.
// sched.lock must be held.
// May run during STW, so write barriers are not allowed.
+//
//go:nowritebarrierrec
func globrunqput(gp *g) {
assertLockHeld(&sched.lock)
@@ -5485,6 +5510,7 @@ func globrunqput(gp *g) {
// Put gp at the head of the global runnable queue.
// sched.lock must be held.
// May run during STW, so write barriers are not allowed.
+//
//go:nowritebarrierrec
func globrunqputhead(gp *g) {
assertLockHeld(&sched.lock)
@@ -5497,6 +5523,7 @@ func globrunqputhead(gp *g) {
// This clears *batch.
// sched.lock must be held.
// May run during STW, so write barriers are not allowed.
+//
//go:nowritebarrierrec
func globrunqputbatch(batch *gQueue, n int32) {
assertLockHeld(&sched.lock)
@@ -5575,11 +5602,11 @@ func (p pMask) clear(id int32) {
//
// Thus, we get the following effects on timer-stealing in findrunnable:
//
-// * Idle Ps with no timers when they go idle are never checked in findrunnable
-// (for work- or timer-stealing; this is the ideal case).
-// * Running Ps must always be checked.
-// * Idle Ps whose timers are stolen must continue to be checked until they run
-// again, even after timer expiration.
+// - Idle Ps with no timers when they go idle are never checked in findrunnable
+// (for work- or timer-stealing; this is the ideal case).
+// - Running Ps must always be checked.
+// - Idle Ps whose timers are stolen must continue to be checked until they run
+// again, even after timer expiration.
//
// When the P starts running again, the mask should be set, as a timer may be
// added at any time.
@@ -5609,6 +5636,7 @@ func updateTimerPMask(pp *p) {
// sched.lock must be held.
//
// May run during STW, so write barriers are not allowed.
+//
//go:nowritebarrierrec
func pidleput(_p_ *p) {
assertLockHeld(&sched.lock)
@@ -5628,6 +5656,7 @@ func pidleput(_p_ *p) {
// sched.lock must be held.
//
// May run during STW, so write barriers are not allowed.
+//
//go:nowritebarrierrec
func pidleget() *p {
assertLockHeld(&sched.lock)
@@ -6083,6 +6112,7 @@ func sync_atomic_runtime_procUnpin() {
}
// Active spinning for sync.Mutex.
+//
//go:linkname sync_runtime_canSpin sync.runtime_canSpin
//go:nosplit
func sync_runtime_canSpin(i int) bool {
diff --git a/src/runtime/proc_test.go b/src/runtime/proc_test.go
index 719d0d1aee5..c49d6ae8a8f 100644
--- a/src/runtime/proc_test.go
+++ b/src/runtime/proc_test.go
@@ -1023,6 +1023,7 @@ func TestLockOSThreadTemplateThreadRace(t *testing.T) {
}
// fakeSyscall emulates a system call.
+//
//go:nosplit
func fakeSyscall(duration time.Duration) {
runtime.Entersyscall()
diff --git a/src/runtime/race.go b/src/runtime/race.go
index e019923bb5c..4694288082b 100644
--- a/src/runtime/race.go
+++ b/src/runtime/race.go
@@ -233,6 +233,7 @@ func raceSymbolizeData(ctx *symbolizeDataContext) {
}
// Race runtime functions called via runtime·racecall.
+//
//go:linkname __tsan_init __tsan_init
var __tsan_init byte
@@ -285,6 +286,7 @@ var __tsan_go_ignore_sync_end byte
var __tsan_report_count byte
// Mimic what cmd/cgo would do.
+//
//go:cgo_import_static __tsan_init
//go:cgo_import_static __tsan_fini
//go:cgo_import_static __tsan_proc_create
@@ -304,6 +306,7 @@ var __tsan_report_count byte
//go:cgo_import_static __tsan_report_count
// These are called from race_amd64.s.
+//
//go:cgo_import_static __tsan_read
//go:cgo_import_static __tsan_read_pc
//go:cgo_import_static __tsan_read_range
@@ -348,6 +351,7 @@ func racecallbackthunk(uintptr)
func racecall(fn *byte, arg0, arg1, arg2, arg3 uintptr)
// checks if the address has shadow (i.e. heap or data/bss)
+//
//go:nosplit
func isvalidaddr(addr unsafe.Pointer) bool {
return racearenastart <= uintptr(addr) && uintptr(addr) < racearenaend ||
diff --git a/src/runtime/runtime1.go b/src/runtime/runtime1.go
index 65e1e0eebca..5429aa2e5b6 100644
--- a/src/runtime/runtime1.go
+++ b/src/runtime/runtime1.go
@@ -53,6 +53,7 @@ var (
)
// nosplit for use in linux startup sysargs
+//
//go:nosplit
func argv_index(argv **byte, i int32) *byte {
return *(**byte)(add(unsafe.Pointer(argv), uintptr(i)*goarch.PtrSize))
@@ -438,6 +439,7 @@ func setTraceback(level string) {
// int64 division is lowered into _divv() call on 386, which does not fit into nosplit functions.
// Handles overflow in a time-specific manner.
// This keeps us within no-split stack limits on 32-bit processors.
+//
//go:nosplit
func timediv(v int64, div int32, rem *int32) int32 {
res := int32(0)
@@ -493,18 +495,21 @@ func reflect_typelinks() ([]unsafe.Pointer, [][]int32) {
}
// reflect_resolveNameOff resolves a name offset from a base pointer.
+//
//go:linkname reflect_resolveNameOff reflect.resolveNameOff
func reflect_resolveNameOff(ptrInModule unsafe.Pointer, off int32) unsafe.Pointer {
return unsafe.Pointer(resolveNameOff(ptrInModule, nameOff(off)).bytes)
}
// reflect_resolveTypeOff resolves an *rtype offset from a base type.
+//
//go:linkname reflect_resolveTypeOff reflect.resolveTypeOff
func reflect_resolveTypeOff(rtype unsafe.Pointer, off int32) unsafe.Pointer {
return unsafe.Pointer((*_type)(rtype).typeOff(typeOff(off)))
}
// reflect_resolveTextOff resolves a function pointer offset from a base type.
+//
//go:linkname reflect_resolveTextOff reflect.resolveTextOff
func reflect_resolveTextOff(rtype unsafe.Pointer, off int32) unsafe.Pointer {
return (*_type)(rtype).textOff(textOff(off))
@@ -512,18 +517,21 @@ func reflect_resolveTextOff(rtype unsafe.Pointer, off int32) unsafe.Pointer {
}
// reflectlite_resolveNameOff resolves a name offset from a base pointer.
+//
//go:linkname reflectlite_resolveNameOff internal/reflectlite.resolveNameOff
func reflectlite_resolveNameOff(ptrInModule unsafe.Pointer, off int32) unsafe.Pointer {
return unsafe.Pointer(resolveNameOff(ptrInModule, nameOff(off)).bytes)
}
// reflectlite_resolveTypeOff resolves an *rtype offset from a base type.
+//
//go:linkname reflectlite_resolveTypeOff internal/reflectlite.resolveTypeOff
func reflectlite_resolveTypeOff(rtype unsafe.Pointer, off int32) unsafe.Pointer {
return unsafe.Pointer((*_type)(rtype).typeOff(typeOff(off)))
}
// reflect_addReflectOff adds a pointer to the reflection offset lookup map.
+//
//go:linkname reflect_addReflectOff reflect.addReflectOff
func reflect_addReflectOff(ptr unsafe.Pointer) int32 {
reflectOffsLock()
diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go
index dc18bf927e4..b2c42d0e5c1 100644
--- a/src/runtime/runtime2.go
+++ b/src/runtime/runtime2.go
@@ -272,6 +272,7 @@ func (gp *guintptr) cas(old, new guintptr) bool {
// setGNoWB performs *gp = new without a write barrier.
// For times when it's impractical to use a guintptr.
+//
//go:nosplit
//go:nowritebarrier
func setGNoWB(gp **g, new *g) {
@@ -305,6 +306,7 @@ func (mp *muintptr) set(m *m) { *mp = muintptr(unsafe.Pointer(m)) }
// setMNoWB performs *mp = new without a write barrier.
// For times when it's impractical to use an muintptr.
+//
//go:nosplit
//go:nowritebarrier
func setMNoWB(mp **m, new *m) {
@@ -514,6 +516,7 @@ type m struct {
g0 *g // goroutine with scheduling stack
morebuf gobuf // gobuf arg to morestack
divmod uint32 // div/mod denominator for arm - known to liblink
+ _ uint32 // align next field to 8 bytes
// Fields not known to debuggers.
procid uint64 // for debuggers, but offset not hard-coded
diff --git a/src/runtime/runtime_test.go b/src/runtime/runtime_test.go
index 12f261bdd2c..1dc04ac55da 100644
--- a/src/runtime/runtime_test.go
+++ b/src/runtime/runtime_test.go
@@ -196,6 +196,7 @@ func TestSetPanicOnFault(t *testing.T) {
// testSetPanicOnFault tests one potentially faulting address.
// It deliberately constructs and uses an invalid pointer,
// so mark it as nocheckptr.
+//
//go:nocheckptr
func testSetPanicOnFault(t *testing.T, addr uintptr, nfault *int) {
if GOOS == "js" {
diff --git a/src/runtime/sema.go b/src/runtime/sema.go
index f94c1aa8910..e83deee083f 100644
--- a/src/runtime/sema.go
+++ b/src/runtime/sema.go
@@ -475,6 +475,7 @@ func less(a, b uint32) bool {
// notifyListAdd adds the caller to a notify list such that it can receive
// notifications. The caller must eventually call notifyListWait to wait for
// such a notification, passing the returned ticket number.
+//
//go:linkname notifyListAdd sync.runtime_notifyListAdd
func notifyListAdd(l *notifyList) uint32 {
// This may be called concurrently, for example, when called from
@@ -484,6 +485,7 @@ func notifyListAdd(l *notifyList) uint32 {
// notifyListWait waits for a notification. If one has been sent since
// notifyListAdd was called, it returns immediately. Otherwise, it blocks.
+//
//go:linkname notifyListWait sync.runtime_notifyListWait
func notifyListWait(l *notifyList, t uint32) {
lockWithRank(&l.lock, lockRankNotifyList)
@@ -518,6 +520,7 @@ func notifyListWait(l *notifyList, t uint32) {
}
// notifyListNotifyAll notifies all entries in the list.
+//
//go:linkname notifyListNotifyAll sync.runtime_notifyListNotifyAll
func notifyListNotifyAll(l *notifyList) {
// Fast-path: if there are no new waiters since the last notification
@@ -550,6 +553,7 @@ func notifyListNotifyAll(l *notifyList) {
}
// notifyListNotifyOne notifies one entry in the list.
+//
//go:linkname notifyListNotifyOne sync.runtime_notifyListNotifyOne
func notifyListNotifyOne(l *notifyList) {
// Fast-path: if there are no new waiters since the last notification
diff --git a/src/runtime/signal_unix.go b/src/runtime/signal_unix.go
index 0e11c576839..3db789396d6 100644
--- a/src/runtime/signal_unix.go
+++ b/src/runtime/signal_unix.go
@@ -108,6 +108,7 @@ var signalsOK bool
// Initialize signals.
// Called by libpreinit so runtime may not be initialized.
+//
//go:nosplit
//go:nowritebarrierrec
func initsig(preinit bool) {
@@ -260,6 +261,7 @@ func sigignore(sig uint32) {
// back to the default. This is called by the child after a fork, so that
// we can enable the signal mask for the exec without worrying about
// running a signal handler in the child.
+//
//go:nosplit
//go:nowritebarrierrec
func clearSignalHandlers() {
@@ -519,6 +521,7 @@ func sigprofNonGo(sig uint32, info *siginfo, ctx unsafe.Pointer) {
// sigprofNonGoPC is called when a profiling signal arrived on a
// non-Go thread and we have a single PC value, not a stack trace.
// g is nil, and what we can do is very limited.
+//
//go:nosplit
//go:nowritebarrierrec
func sigprofNonGoPC(pc uintptr) {
@@ -536,6 +539,7 @@ func sigprofNonGoPC(pc uintptr) {
// We do this in case some non-Go code called sigaltstack.
// This reports whether the stack was adjusted, and if so stores the old
// signal stack in *gsigstack.
+//
//go:nosplit
func adjustSignalStack(sig uint32, mp *m, gsigStack *gsignalStack) bool {
sp := uintptr(unsafe.Pointer(&sig))
@@ -795,6 +799,7 @@ func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
// getg().throwsplit, since sigpanic may need to grow the stack.
//
// This is exported via linkname to assembly in runtime/cgo.
+//
//go:linkname sigpanic
func sigpanic() {
g := getg()
@@ -843,6 +848,7 @@ func sigpanic() {
// dieFromSignal kills the program with a signal.
// This provides the expected exit status for the shell.
// This is only called with fatal signals expected to kill the process.
+//
//go:nosplit
//go:nowritebarrierrec
func dieFromSignal(sig uint32) {
@@ -1015,6 +1021,7 @@ func signalDuringFork(sig uint32) {
var badginsignalMsg = "fatal: bad g in signal handler\n"
// This runs on a foreign stack, without an m or a g. No stack split.
+//
//go:nosplit
//go:norace
//go:nowritebarrierrec
@@ -1044,6 +1051,7 @@ func sigfwd(fn uintptr, sig uint32, info *siginfo, ctx unsafe.Pointer)
// signal to the handler that was installed before Go's. Returns whether the
// signal was forwarded.
// This is called by the signal handler, and the world may be stopped.
+//
//go:nosplit
//go:nowritebarrierrec
func sigfwdgo(sig uint32, info *siginfo, ctx unsafe.Pointer) bool {
@@ -1113,6 +1121,7 @@ func sigfwdgo(sig uint32, info *siginfo, ctx unsafe.Pointer) bool {
// thread calls a Go function.
// This is nosplit and nowritebarrierrec because it is called by needm
// which may be called on a non-Go thread with no g available.
+//
//go:nosplit
//go:nowritebarrierrec
func sigsave(p *sigset) {
@@ -1124,6 +1133,7 @@ func sigsave(p *sigset) {
// calls a Go function.
// This is nosplit and nowritebarrierrec because it is called by dropm
// after g has been cleared.
+//
//go:nosplit
//go:nowritebarrierrec
func msigrestore(sigmask sigset) {
@@ -1143,6 +1153,7 @@ var sigsetAllExiting = sigset_all
// definition of sigset_all is used.
// This is nosplit and nowritebarrierrec because it is called by needm
// which may be called on a non-Go thread with no g available.
+//
//go:nosplit
//go:nowritebarrierrec
func sigblock(exiting bool) {
@@ -1157,6 +1168,7 @@ func sigblock(exiting bool) {
// This is nosplit and nowritebarrierrec because it is called from
// dieFromSignal, which can be called by sigfwdgo while running in the
// signal handler, on the signal stack, with no g available.
+//
//go:nosplit
//go:nowritebarrierrec
func unblocksig(sig uint32) {
@@ -1215,6 +1227,7 @@ func minitSignalMask() {
// unminitSignals is called from dropm, via unminit, to undo the
// effect of calling minit on a non-Go thread.
+//
//go:nosplit
func unminitSignals() {
if getg().m.newSigstack {
@@ -1234,6 +1247,7 @@ func unminitSignals() {
// blockableSig reports whether sig may be blocked by the signal mask.
// We never want to block the signals marked _SigUnblock;
// these are the synchronous signals that turn into a Go panic.
+// We never want to block the preemption signal if it is being used.
// In a Go program--not a c-archive/c-shared--we never want to block
// the signals marked _SigKill or _SigThrow, as otherwise it's possible
// for all running threads to block them and delay their delivery until
@@ -1244,6 +1258,9 @@ func blockableSig(sig uint32) bool {
if flags&_SigUnblock != 0 {
return false
}
+ if sig == sigPreempt && preemptMSupported && debug.asyncpreemptoff == 0 {
+ return false
+ }
if isarchive || islibrary {
return true
}
@@ -1264,6 +1281,7 @@ type gsignalStack struct {
// It saves the old values in *old for use by restoreGsignalStack.
// This is used when handling a signal if non-Go code has set the
// alternate signal stack.
+//
//go:nosplit
//go:nowritebarrierrec
func setGsignalStack(st *stackt, old *gsignalStack) {
@@ -1283,6 +1301,7 @@ func setGsignalStack(st *stackt, old *gsignalStack) {
// restoreGsignalStack restores the gsignal stack to the value it had
// before entering the signal handler.
+//
//go:nosplit
//go:nowritebarrierrec
func restoreGsignalStack(st *gsignalStack) {
@@ -1294,6 +1313,7 @@ func restoreGsignalStack(st *gsignalStack) {
}
// signalstack sets the current thread's alternate signal stack to s.
+//
//go:nosplit
func signalstack(s *stack) {
st := stackt{ss_size: s.hi - s.lo}
diff --git a/src/runtime/sigqueue.go b/src/runtime/sigqueue.go
index fdf99d94a23..49502cbed36 100644
--- a/src/runtime/sigqueue.go
+++ b/src/runtime/sigqueue.go
@@ -125,6 +125,7 @@ Send:
// Called to receive the next queued signal.
// Must only be called from a single goroutine at a time.
+//
//go:linkname signal_recv os/signal.signal_recv
func signal_recv() uint32 {
for {
@@ -173,6 +174,7 @@ func signal_recv() uint32 {
// the signal(s) in question, and here we are just waiting to make sure
// that all the signals have been delivered to the user channels
// by the os/signal package.
+//
//go:linkname signalWaitUntilIdle os/signal.signalWaitUntilIdle
func signalWaitUntilIdle() {
// Although the signals we care about have been removed from
@@ -193,6 +195,7 @@ func signalWaitUntilIdle() {
}
// Must only be called from a single goroutine at a time.
+//
//go:linkname signal_enable os/signal.signal_enable
func signal_enable(s uint32) {
if !sig.inuse {
@@ -221,6 +224,7 @@ func signal_enable(s uint32) {
}
// Must only be called from a single goroutine at a time.
+//
//go:linkname signal_disable os/signal.signal_disable
func signal_disable(s uint32) {
if s >= uint32(len(sig.wanted)*32) {
@@ -234,6 +238,7 @@ func signal_disable(s uint32) {
}
// Must only be called from a single goroutine at a time.
+//
//go:linkname signal_ignore os/signal.signal_ignore
func signal_ignore(s uint32) {
if s >= uint32(len(sig.wanted)*32) {
@@ -253,6 +258,7 @@ func signal_ignore(s uint32) {
// sigInitIgnored marks the signal as already ignored. This is called at
// program start by initsig. In a shared library initsig is called by
// libpreinit, so the runtime may not be initialized yet.
+//
//go:nosplit
func sigInitIgnored(s uint32) {
i := sig.ignored[s/32]
@@ -261,6 +267,7 @@ func sigInitIgnored(s uint32) {
}
// Checked by signal handlers.
+//
//go:linkname signal_ignored os/signal.signal_ignored
func signal_ignored(s uint32) bool {
i := atomic.Load(&sig.ignored[s/32])
diff --git a/src/runtime/sigqueue_plan9.go b/src/runtime/sigqueue_plan9.go
index d5fe8f8b35d..9ed6fb5886c 100644
--- a/src/runtime/sigqueue_plan9.go
+++ b/src/runtime/sigqueue_plan9.go
@@ -94,6 +94,7 @@ func sendNote(s *byte) bool {
// Called to receive the next queued signal.
// Must only be called from a single goroutine at a time.
+//
//go:linkname signal_recv os/signal.signal_recv
func signal_recv() string {
for {
@@ -117,6 +118,7 @@ func signal_recv() string {
// the signal(s) in question, and here we are just waiting to make sure
// that all the signals have been delivered to the user channels
// by the os/signal package.
+//
//go:linkname signalWaitUntilIdle os/signal.signalWaitUntilIdle
func signalWaitUntilIdle() {
for {
@@ -131,6 +133,7 @@ func signalWaitUntilIdle() {
}
// Must only be called from a single goroutine at a time.
+//
//go:linkname signal_enable os/signal.signal_enable
func signal_enable(s uint32) {
if !sig.inuse {
@@ -141,11 +144,13 @@ func signal_enable(s uint32) {
}
// Must only be called from a single goroutine at a time.
+//
//go:linkname signal_disable os/signal.signal_disable
func signal_disable(s uint32) {
}
// Must only be called from a single goroutine at a time.
+//
//go:linkname signal_ignore os/signal.signal_ignore
func signal_ignore(s uint32) {
}
diff --git a/src/runtime/stack.go b/src/runtime/stack.go
index 54a02173c38..b7df2317223 100644
--- a/src/runtime/stack.go
+++ b/src/runtime/stack.go
@@ -151,7 +151,9 @@ const (
// Global pool of spans that have free stacks.
// Stacks are assigned an order according to size.
-// order = log_2(size/FixedStack)
+//
+// order = log_2(size/FixedStack)
+//
// There is a free list for each order.
var stackpool [_NumStackOrders]struct {
item stackpoolItem
diff --git a/src/runtime/string.go b/src/runtime/string.go
index eec29075b9a..8b20c93fd78 100644
--- a/src/runtime/string.go
+++ b/src/runtime/string.go
@@ -147,10 +147,10 @@ func rawstringtmp(buf *tmpBuf, l int) (s string, b []byte) {
// and otherwise intrinsified by the compiler.
//
// Some internal compiler optimizations use this function.
-// - Used for m[T1{... Tn{..., string(k), ...} ...}] and m[string(k)]
-// where k is []byte, T1 to Tn is a nesting of struct and array literals.
-// - Used for "<"+string(b)+">" concatenation where b is []byte.
-// - Used for string(b)=="foo" comparison where b is []byte.
+// - Used for m[T1{... Tn{..., string(k), ...} ...}] and m[string(k)]
+// where k is []byte, T1 to Tn is a nesting of struct and array literals.
+// - Used for "<"+string(b)+">" concatenation where b is []byte.
+// - Used for string(b)=="foo" comparison where b is []byte.
func slicebytetostringtmp(ptr *byte, n int) (str string) {
if raceenabled && n > 0 {
racereadrangepc(unsafe.Pointer(ptr),
@@ -325,6 +325,7 @@ func gobytes(p *byte, n int) (b []byte) {
}
// This is exported via linkname to assembly in syscall (for Plan9).
+//
//go:linkname gostring
func gostring(p *byte) string {
l := findnull(p)
diff --git a/src/runtime/stubs.go b/src/runtime/stubs.go
index cd7c91029b0..8c4ab3ed4e6 100644
--- a/src/runtime/stubs.go
+++ b/src/runtime/stubs.go
@@ -12,6 +12,7 @@ import (
)
// 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)
@@ -111,6 +112,7 @@ func reflect_memclrNoHeapPointers(ptr unsafe.Pointer, n uintptr) {
func memmove(to, from unsafe.Pointer, n uintptr)
// Outside assembly calls memmove. Make sure it has ABI wrappers.
+//
//go:linkname memmove
//go:linkname reflect_memmove reflect.memmove
@@ -165,6 +167,7 @@ func net_fastrand() uint32 { return fastrand() }
func os_fastrand() uint32 { return fastrand() }
// in internal/bytealg/equal_*.s
+//
//go:noescape
func memequal(a, b unsafe.Pointer, size uintptr) bool
@@ -173,6 +176,7 @@ func memequal(a, b unsafe.Pointer, size uintptr) bool
// output depends on the input. noescape is inlined and currently
// compiles down to zero instructions.
// USE CAREFULLY!
+//
//go:nosplit
func noescape(p unsafe.Pointer) unsafe.Pointer {
x := uintptr(p)
@@ -235,6 +239,7 @@ func breakpoint()
// Arguments passed through to reflectcall do not escape. The type is used
// only in a very limited callee of reflectcall, the stackArgs are copied, and
// regArgs is only used in the reflectcall frame.
+//
//go:noescape
func reflectcall(stackArgsType *_type, fn, stackArgs unsafe.Pointer, stackArgsSize, stackRetOffset, frameSize uint32, regArgs *abi.RegArgs)
diff --git a/src/runtime/stubs2.go b/src/runtime/stubs2.go
index 9aa965454d9..94a888dec62 100644
--- a/src/runtime/stubs2.go
+++ b/src/runtime/stubs2.go
@@ -24,6 +24,7 @@ func usleep_no_g(usec uint32) {
// write calls the write system call.
// It returns a non-negative number of bytes written or a negative errno value.
+//
//go:noescape
func write1(fd uintptr, p unsafe.Pointer, n int32) int32
diff --git a/src/runtime/stubs_linux.go b/src/runtime/stubs_linux.go
index 06c14e21601..2367dc2bd03 100644
--- a/src/runtime/stubs_linux.go
+++ b/src/runtime/stubs_linux.go
@@ -13,6 +13,7 @@ func sbrk0() uintptr
// Called from write_err_android.go only, but defined in sys_linux_*.s;
// declared here (instead of in write_err_android.go) for go vet on non-android builds.
// The return value is the raw syscall result, which may encode an error number.
+//
//go:noescape
func access(name *byte, mode int32) int32
func connect(fd int32, addr unsafe.Pointer, len int32) int32
diff --git a/src/runtime/stubs_ppc64.go b/src/runtime/stubs_ppc64.go
index 07127629d19..6919b748f0c 100644
--- a/src/runtime/stubs_ppc64.go
+++ b/src/runtime/stubs_ppc64.go
@@ -7,5 +7,6 @@
package runtime
// This is needed for vet
+//
//go:noescape
func callCgoSigaction(sig uintptr, new, old *sigactiont) int32
diff --git a/src/runtime/symtab.go b/src/runtime/symtab.go
index ee4db47314b..ad34b68c7d5 100644
--- a/src/runtime/symtab.go
+++ b/src/runtime/symtab.go
@@ -494,6 +494,7 @@ var modulesSlice *[]*moduledata // see activeModules
//
// This is nosplit/nowritebarrier because it is called by the
// cgo pointer checking code.
+//
//go:nosplit
//go:nowritebarrier
func activeModules() []*moduledata {
@@ -659,6 +660,7 @@ func moduledataverify1(datap *moduledata) {
// relocated baseaddr to compute the function address.
//
// It is nosplit because it is part of the findfunc implementation.
+//
//go:nosplit
func (md *moduledata) textAddr(off32 uint32) uintptr {
off := uintptr(off32)
@@ -683,6 +685,7 @@ func (md *moduledata) textAddr(off32 uint32) uintptr {
// to md.text, and returns if the PC is in any Go text section.
//
// It is nosplit because it is part of the findfunc implementation.
+//
//go:nosplit
func (md *moduledata) textOff(pc uintptr) (uint32, bool) {
res := uint32(pc - md.text)
diff --git a/src/runtime/symtab_test.go b/src/runtime/symtab_test.go
index a83afc3385d..1a0c55af976 100644
--- a/src/runtime/symtab_test.go
+++ b/src/runtime/symtab_test.go
@@ -29,6 +29,7 @@ func TestCaller(t *testing.T) {
// These are marked noinline so that we can use FuncForPC
// in testCallerBar.
+//
//go:noinline
func testCallerFoo(t *testing.T) {
testCallerBar(t)
@@ -205,15 +206,15 @@ func tracebackFunc(t *testing.T) uintptr {
// Go obviously doesn't easily expose the problematic PCs to running programs,
// so this test is a bit fragile. Some details:
//
-// * tracebackFunc is our target function. We want to get a PC in the
-// alignment region following this function. This function also has other
-// functions inlined into it to ensure it has an InlTree (this was the source
-// of the bug in issue 44971).
+// - tracebackFunc is our target function. We want to get a PC in the
+// alignment region following this function. This function also has other
+// functions inlined into it to ensure it has an InlTree (this was the source
+// of the bug in issue 44971).
//
-// * We acquire a PC in tracebackFunc, walking forwards until FuncForPC says
-// we're in a new function. The last PC of the function according to FuncForPC
-// should be in the alignment region (assuming the function isn't already
-// perfectly aligned).
+// - We acquire a PC in tracebackFunc, walking forwards until FuncForPC says
+// we're in a new function. The last PC of the function according to FuncForPC
+// should be in the alignment region (assuming the function isn't already
+// perfectly aligned).
//
// This is a regression test for issue 44971.
func TestFunctionAlignmentTraceback(t *testing.T) {
diff --git a/src/runtime/sys_darwin.go b/src/runtime/sys_darwin.go
index 58b3a9171c7..1547fdceb0c 100644
--- a/src/runtime/sys_darwin.go
+++ b/src/runtime/sys_darwin.go
@@ -170,6 +170,7 @@ func pthread_kill_trampoline()
// mmap is used to do low-level memory allocation via mmap. Don't allow stack
// splits, since this function (used by sysAlloc) is called in a lot of low-level
// parts of the runtime and callers often assume it won't acquire any locks.
+//
//go:nosplit
func mmap(addr unsafe.Pointer, n uintptr, prot, flags, fd int32, off uint32) (unsafe.Pointer, int) {
args := struct {
@@ -232,10 +233,10 @@ func closefd(fd int32) int32 {
}
func close_trampoline()
+// This is exported via linkname to assembly in runtime/cgo.
+//
//go:nosplit
//go:cgo_unsafe_args
-//
-// This is exported via linkname to assembly in runtime/cgo.
//go:linkname exit
func exit(code int32) {
libcCall(unsafe.Pointer(abi.FuncPCABI0(exit_trampoline)), unsafe.Pointer(&code))
diff --git a/src/runtime/sys_libc.go b/src/runtime/sys_libc.go
index 7012b4167e9..0c6f13ca9f6 100644
--- a/src/runtime/sys_libc.go
+++ b/src/runtime/sys_libc.go
@@ -12,6 +12,7 @@ import "unsafe"
// fn is the raw pc value of the entry point of the desired function.
// Switches to the system stack, if not already there.
// Preserves the calling point as the location where a profiler traceback will begin.
+//
//go:nosplit
func libcCall(fn, arg unsafe.Pointer) int32 {
// Leave caller's PC/SP/G around for traceback.
diff --git a/src/runtime/sys_openbsd2.go b/src/runtime/sys_openbsd2.go
index d174d87a49c..f936e0cfc3b 100644
--- a/src/runtime/sys_openbsd2.go
+++ b/src/runtime/sys_openbsd2.go
@@ -12,6 +12,7 @@ import (
)
// This is exported via linkname to assembly in runtime/cgo.
+//
//go:linkname exit
//go:nosplit
//go:cgo_unsafe_args
@@ -45,6 +46,7 @@ func thrkill_trampoline()
// mmap is used to do low-level memory allocation via mmap. Don't allow stack
// splits, since this function (used by sysAlloc) is called in a lot of low-level
// parts of the runtime and callers often assume it won't acquire any locks.
+//
//go:nosplit
func mmap(addr unsafe.Pointer, n uintptr, prot, flags, fd int32, off uint32) (unsafe.Pointer, int) {
args := struct {
diff --git a/src/runtime/syscall_aix.go b/src/runtime/syscall_aix.go
index 79b51240e90..f294922e7dd 100644
--- a/src/runtime/syscall_aix.go
+++ b/src/runtime/syscall_aix.go
@@ -126,6 +126,7 @@ func syscall_chroot1(path uintptr) (err uintptr) {
}
// like close, but must not split stack, for fork.
+//
//go:linkname syscall_close syscall.close
//go:nosplit
func syscall_close(fd int32) int32 {
@@ -148,6 +149,7 @@ func syscall_execve(path, argv, envp uintptr) (err uintptr) {
}
// like exit, but must not split stack, for fork.
+//
//go:linkname syscall_exit syscall.exit
//go:nosplit
func syscall_exit(code uintptr) {
diff --git a/src/runtime/syscall_solaris.go b/src/runtime/syscall_solaris.go
index 79775711aea..e7bab3b23fb 100644
--- a/src/runtime/syscall_solaris.go
+++ b/src/runtime/syscall_solaris.go
@@ -85,6 +85,7 @@ func syscall_chroot(path uintptr) (err uintptr) {
}
// like close, but must not split stack, for forkx.
+//
//go:nosplit
//go:linkname syscall_close
func syscall_close(fd int32) int32 {
@@ -113,6 +114,7 @@ func syscall_execve(path, argv, envp uintptr) (err uintptr) {
}
// like exit, but must not split stack, for forkx.
+//
//go:nosplit
//go:linkname syscall_exit
func syscall_exit(code uintptr) {
diff --git a/src/runtime/syscall_windows.go b/src/runtime/syscall_windows.go
index 9c38facf081..a841a31a27e 100644
--- a/src/runtime/syscall_windows.go
+++ b/src/runtime/syscall_windows.go
@@ -399,6 +399,7 @@ const _LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800
// parameter and the important SEARCH_SYSTEM32 argument. But on systems that
// do not have that option, absoluteFilepath should contain a fallback
// to the full path inside of system32 for use with vanilla LoadLibrary.
+//
//go:linkname syscall_loadsystemlibrary syscall.loadsystemlibrary
//go:nosplit
//go:cgo_unsafe_args
diff --git a/src/runtime/syscall_windows_test.go b/src/runtime/syscall_windows_test.go
index 034a1d84db3..37f8f40cfba 100644
--- a/src/runtime/syscall_windows_test.go
+++ b/src/runtime/syscall_windows_test.go
@@ -469,6 +469,7 @@ func sum5andPair(i1, i2, i3, i4, i5 uint8Pair) uintptr {
// that insufficient spill slots allocated (according to the ABI)
// may cause compiler-generated spills to clobber the return PC.
// Then, the GC stack scanning will catch that.
+//
//go:registerparams
func sum9andGC(i1, i2, i3, i4, i5, i6, i7, i8, i9 uint32) uintptr {
runtime.GC()
diff --git a/src/runtime/testdata/testprog/crash.go b/src/runtime/testdata/testprog/crash.go
index c4990cdda9a..a2294ba1495 100644
--- a/src/runtime/testdata/testprog/crash.go
+++ b/src/runtime/testdata/testprog/crash.go
@@ -12,6 +12,13 @@ import (
func init() {
register("Crash", Crash)
register("DoublePanic", DoublePanic)
+ register("ErrorPanic", ErrorPanic)
+ register("StringerPanic", StringerPanic)
+ register("DoubleErrorPanic", DoubleErrorPanic)
+ register("DoubleStringerPanic", DoubleStringerPanic)
+ register("StringPanic", StringPanic)
+ register("NilPanic", NilPanic)
+ register("CircularPanic", CircularPanic)
}
func test(name string) {
@@ -64,3 +71,69 @@ func DoublePanic() {
}()
panic(P("XXX"))
}
+
+// Test that panic while panicking discards error message
+// See issue 52257
+type exampleError struct{}
+
+func (e exampleError) Error() string {
+ panic("important error message")
+}
+
+func ErrorPanic() {
+ panic(exampleError{})
+}
+
+type examplePanicError struct{}
+
+func (e examplePanicError) Error() string {
+ panic(exampleError{})
+}
+
+func DoubleErrorPanic() {
+ panic(examplePanicError{})
+}
+
+type exampleStringer struct{}
+
+func (s exampleStringer) String() string {
+ panic("important stringer message")
+}
+
+func StringerPanic() {
+ panic(exampleStringer{})
+}
+
+type examplePanicStringer struct{}
+
+func (s examplePanicStringer) String() string {
+ panic(exampleStringer{})
+}
+
+func DoubleStringerPanic() {
+ panic(examplePanicStringer{})
+}
+
+func StringPanic() {
+ panic("important string message")
+}
+
+func NilPanic() {
+ panic(nil)
+}
+
+type exampleCircleStartError struct {}
+
+func (e exampleCircleStartError) Error() string {
+ panic(exampleCircleEndError{})
+}
+
+type exampleCircleEndError struct {}
+
+func (e exampleCircleEndError) Error() string {
+ panic(exampleCircleStartError{})
+}
+
+func CircularPanic() {
+ panic(exampleCircleStartError{})
+} \ No newline at end of file
diff --git a/src/runtime/testdata/testprogcgo/dropm_stub.go b/src/runtime/testdata/testprogcgo/dropm_stub.go
index f7f142c1fdd..6997cfd3fa8 100644
--- a/src/runtime/testdata/testprogcgo/dropm_stub.go
+++ b/src/runtime/testdata/testprogcgo/dropm_stub.go
@@ -7,5 +7,6 @@ package main
import _ "unsafe" // for go:linkname
// Defined in the runtime package.
+//
//go:linkname runtime_getm_for_test runtime.getm
func runtime_getm_for_test() uintptr
diff --git a/src/runtime/testdata/testprogcgo/eintr.go b/src/runtime/testdata/testprogcgo/eintr.go
index b35b280a76e..6e9677f988b 100644
--- a/src/runtime/testdata/testprogcgo/eintr.go
+++ b/src/runtime/testdata/testprogcgo/eintr.go
@@ -70,6 +70,7 @@ func EINTR() {
// spin does CPU bound spinning and allocating for a millisecond,
// to get a SIGURG.
+//
//go:noinline
func spin() (float64, []byte) {
stop := time.Now().Add(time.Millisecond)
diff --git a/src/runtime/time.go b/src/runtime/time.go
index a9ad6207764..e4d82699871 100644
--- a/src/runtime/time.go
+++ b/src/runtime/time.go
@@ -173,6 +173,7 @@ const verifyTimers = false
// time.now is implemented in assembly.
// timeSleep puts the current goroutine to sleep for at least ns nanoseconds.
+//
//go:linkname timeSleep time.Sleep
func timeSleep(ns int64) {
if ns <= 0 {
@@ -205,6 +206,7 @@ func resetForSleep(gp *g, ut unsafe.Pointer) bool {
}
// startTimer adds t to the timer heap.
+//
//go:linkname startTimer time.startTimer
func startTimer(t *timer) {
if raceenabled {
@@ -215,14 +217,17 @@ func startTimer(t *timer) {
// stopTimer stops a timer.
// It reports whether t was stopped before being run.
+//
//go:linkname stopTimer time.stopTimer
func stopTimer(t *timer) bool {
return deltimer(t)
}
// resetTimer resets an inactive timer, adding it to the heap.
-//go:linkname resetTimer time.resetTimer
+//
// Reports whether the timer was modified before it was run.
+//
+//go:linkname resetTimer time.resetTimer
func resetTimer(t *timer, when int64) bool {
if raceenabled {
racerelease(unsafe.Pointer(t))
@@ -231,6 +236,7 @@ func resetTimer(t *timer, when int64) bool {
}
// modTimer modifies an existing timer.
+//
//go:linkname modTimer time.modTimer
func modTimer(t *timer, when, period int64, f func(any, uintptr), arg any, seq uintptr) {
modtimer(t, when, period, f, arg, seq)
@@ -737,6 +743,7 @@ func addAdjustedTimers(pp *p, moved []*timer) {
// should wake up the netpoller. It returns 0 if there are no timers.
// This function is invoked when dropping a P, and must run without
// any write barriers.
+//
//go:nowritebarrierrec
func nobarrierWakeTime(pp *p) int64 {
next := int64(atomic.Load64(&pp.timer0When))
@@ -753,6 +760,7 @@ func nobarrierWakeTime(pp *p) int64 {
// when the first timer should run.
// The caller must have locked the timers for pp.
// If a timer is run, this will temporarily unlock the timers.
+//
//go:systemstack
func runtimer(pp *p, now int64) int64 {
for {
@@ -819,6 +827,7 @@ func runtimer(pp *p, now int64) int64 {
// runOneTimer runs a single timer.
// The caller must have locked the timers for pp.
// This will temporarily unlock the timers while running the timer function.
+//
//go:systemstack
func runOneTimer(pp *p, t *timer, now int64) {
if raceenabled {
diff --git a/src/runtime/time_fake.go b/src/runtime/time_fake.go
index b5e04635883..9e24f709310 100644
--- a/src/runtime/time_fake.go
+++ b/src/runtime/time_fake.go
@@ -44,6 +44,7 @@ func time_now() (sec int64, nsec int32, mono int64) {
// write is like the Unix write system call.
// We have to avoid write barriers to avoid potential deadlock
// on write calls.
+//
//go:nowritebarrierrec
func write(fd uintptr, p unsafe.Pointer, n int32) int32 {
if !(fd == 1 || fd == 2) {
diff --git a/src/runtime/trace/annotation.go b/src/runtime/trace/annotation.go
index bf3dbc3d797..9171633b07e 100644
--- a/src/runtime/trace/annotation.go
+++ b/src/runtime/trace/annotation.go
@@ -28,13 +28,13 @@ type traceContextKey struct{}
// If the end function is called multiple times, only the first
// call is used in the latency measurement.
//
-// ctx, task := trace.NewTask(ctx, "awesomeTask")
-// trace.WithRegion(ctx, "preparation", prepWork)
-// // preparation of the task
-// go func() { // continue processing the task in a separate goroutine.
-// defer task.End()
-// trace.WithRegion(ctx, "remainingWork", remainingWork)
-// }()
+// ctx, task := trace.NewTask(ctx, "awesomeTask")
+// trace.WithRegion(ctx, "preparation", prepWork)
+// // preparation of the task
+// go func() { // continue processing the task in a separate goroutine.
+// defer task.End()
+// trace.WithRegion(ctx, "remainingWork", remainingWork)
+// }()
func NewTask(pctx context.Context, taskType string) (ctx context.Context, task *Task) {
pid := fromContext(pctx).id
id := newID()
@@ -148,7 +148,7 @@ func WithRegion(ctx context.Context, regionType string, fn func()) {
// after this region must be ended before this region can be ended.
// Recommended usage is
//
-// defer trace.StartRegion(ctx, "myTracedRegion").End()
+// defer trace.StartRegion(ctx, "myTracedRegion").End()
func StartRegion(ctx context.Context, regionType string) *Region {
if !IsEnabled() {
return noopRegion
diff --git a/src/runtime/trace/trace.go b/src/runtime/trace/trace.go
index b34aef03c51..e0c3ca7a1e7 100644
--- a/src/runtime/trace/trace.go
+++ b/src/runtime/trace/trace.go
@@ -5,7 +5,7 @@
// Package trace contains facilities for programs to generate traces
// for the Go execution tracer.
//
-// Tracing runtime activities
+// # Tracing runtime activities
//
// The execution trace captures a wide range of execution events such as
// goroutine creation/blocking/unblocking, syscall enter/exit/block,
@@ -19,7 +19,7 @@
// command runs the test in the current directory and writes the trace
// file (trace.out).
//
-// go test -trace=trace.out
+// go test -trace=trace.out
//
// This runtime/trace package provides APIs to add equivalent tracing
// support to a standalone program. See the Example that demonstrates
@@ -29,12 +29,12 @@
// following line will install a handler under the /debug/pprof/trace URL
// to download a live trace:
//
-// import _ "net/http/pprof"
+// import _ "net/http/pprof"
//
// See the net/http/pprof package for more details about all of the
// debug endpoints installed by this import.
//
-// User annotation
+// # User annotation
//
// Package trace provides user annotation APIs that can be used to
// log interesting events during execution.
@@ -55,16 +55,16 @@
// trace to trace the durations of sequential steps in a cappuccino making
// operation.
//
-// trace.WithRegion(ctx, "makeCappuccino", func() {
+// trace.WithRegion(ctx, "makeCappuccino", func() {
//
-// // orderID allows to identify a specific order
-// // among many cappuccino order region records.
-// trace.Log(ctx, "orderID", orderID)
+// // orderID allows to identify a specific order
+// // among many cappuccino order region records.
+// trace.Log(ctx, "orderID", orderID)
//
-// trace.WithRegion(ctx, "steamMilk", steamMilk)
-// trace.WithRegion(ctx, "extractCoffee", extractCoffee)
-// trace.WithRegion(ctx, "mixMilkCoffee", mixMilkCoffee)
-// })
+// trace.WithRegion(ctx, "steamMilk", steamMilk)
+// trace.WithRegion(ctx, "extractCoffee", extractCoffee)
+// trace.WithRegion(ctx, "mixMilkCoffee", mixMilkCoffee)
+// })
//
// A task is a higher-level component that aids tracing of logical
// operations such as an RPC request, an HTTP request, or an
@@ -80,27 +80,26 @@
// the trace tool can identify the goroutines involved in a specific
// cappuccino order.
//
-// ctx, task := trace.NewTask(ctx, "makeCappuccino")
-// trace.Log(ctx, "orderID", orderID)
-//
-// milk := make(chan bool)
-// espresso := make(chan bool)
-//
-// go func() {
-// trace.WithRegion(ctx, "steamMilk", steamMilk)
-// milk <- true
-// }()
-// go func() {
-// trace.WithRegion(ctx, "extractCoffee", extractCoffee)
-// espresso <- true
-// }()
-// go func() {
-// defer task.End() // When assemble is done, the order is complete.
-// <-espresso
-// <-milk
-// trace.WithRegion(ctx, "mixMilkCoffee", mixMilkCoffee)
-// }()
-//
+// ctx, task := trace.NewTask(ctx, "makeCappuccino")
+// trace.Log(ctx, "orderID", orderID)
+//
+// milk := make(chan bool)
+// espresso := make(chan bool)
+//
+// go func() {
+// trace.WithRegion(ctx, "steamMilk", steamMilk)
+// milk <- true
+// }()
+// go func() {
+// trace.WithRegion(ctx, "extractCoffee", extractCoffee)
+// espresso <- true
+// }()
+// go func() {
+// defer task.End() // When assemble is done, the order is complete.
+// <-espresso
+// <-milk
+// trace.WithRegion(ctx, "mixMilkCoffee", mixMilkCoffee)
+// }()
//
// The trace tool computes the latency of a task by measuring the
// time between the task creation and the task end and provides
diff --git a/src/runtime/type.go b/src/runtime/type.go
index a00394f3b39..b650d6d7959 100644
--- a/src/runtime/type.go
+++ b/src/runtime/type.go
@@ -14,6 +14,7 @@ import (
// tflag is documented in reflect/type.go.
//
// tflag values must be kept in sync with copies in:
+//
// cmd/compile/internal/reflectdata/reflect.go
// cmd/link/internal/ld/decodesym.go
// reflect/type.go
@@ -126,7 +127,14 @@ func (t *_type) name() string {
}
s := t.string()
i := len(s) - 1
- for i >= 0 && s[i] != '.' {
+ sqBrackets := 0
+ for i >= 0 && (s[i] != '.' || sqBrackets != 0) {
+ switch s[i] {
+ case ']':
+ sqBrackets++
+ case '[':
+ sqBrackets--
+ }
i--
}
return s[i+1:]
diff --git a/src/runtime/vdso_linux.go b/src/runtime/vdso_linux.go
index cff2000767f..2ebdd44e94d 100644
--- a/src/runtime/vdso_linux.go
+++ b/src/runtime/vdso_linux.go
@@ -280,6 +280,7 @@ func vdsoauxv(tag, val uintptr) {
}
// vdsoMarker reports whether PC is on the VDSO page.
+//
//go:nosplit
func inVDSOPage(pc uintptr) bool {
for _, k := range vdsoSymbolKeys {
diff --git a/src/runtime/vlrt.go b/src/runtime/vlrt.go
index 1dcb125aef5..4b12f593c8a 100644
--- a/src/runtime/vlrt.go
+++ b/src/runtime/vlrt.go
@@ -296,11 +296,14 @@ func slowdodiv(n, d uint64) (q, r uint64) {
// Floating point control word values.
// Bits 0-5 are bits to disable floating-point exceptions.
// Bits 8-9 are the precision control:
-// 0 = single precision a.k.a. float32
-// 2 = double precision a.k.a. float64
+//
+// 0 = single precision a.k.a. float32
+// 2 = double precision a.k.a. float64
+//
// Bits 10-11 are the rounding mode:
-// 0 = round to nearest (even on a tie)
-// 3 = round toward zero
+//
+// 0 = round to nearest (even on a tie)
+// 3 = round toward zero
var (
controlWord64 uint16 = 0x3f + 2<<8 + 0<<10
controlWord64trunc uint16 = 0x3f + 2<<8 + 3<<10
diff --git a/src/sort/example_multi_test.go b/src/sort/example_multi_test.go
index de6ec142d1c..93f2d3ec575 100644
--- a/src/sort/example_multi_test.go
+++ b/src/sort/example_multi_test.go
@@ -126,7 +126,7 @@ func Example_sortMultiKeys() {
// By user: [{dmr C 100} {glenda Go 200} {gri Go 100} {gri Smalltalk 80} {ken C 150} {ken Go 200} {r Go 100} {r C 150} {rsc Go 200}]
// By user,<lines: [{dmr C 100} {glenda Go 200} {gri Smalltalk 80} {gri Go 100} {ken C 150} {ken Go 200} {r Go 100} {r C 150} {rsc Go 200}]
// By user,>lines: [{dmr C 100} {glenda Go 200} {gri Go 100} {gri Smalltalk 80} {ken Go 200} {ken C 150} {r C 150} {r Go 100} {rsc Go 200}]
- // By language,<lines: [{dmr C 100} {ken C 150} {r C 150} {r Go 100} {gri Go 100} {ken Go 200} {glenda Go 200} {rsc Go 200} {gri Smalltalk 80}]
+ // By language,<lines: [{dmr C 100} {ken C 150} {r C 150} {gri Go 100} {r Go 100} {glenda Go 200} {ken Go 200} {rsc Go 200} {gri Smalltalk 80}]
// By language,<lines,user: [{dmr C 100} {ken C 150} {r C 150} {gri Go 100} {r Go 100} {glenda Go 200} {ken Go 200} {rsc Go 200} {gri Smalltalk 80}]
}
diff --git a/src/sort/export_test.go b/src/sort/export_test.go
index b6e30ceb570..2a3c21fe158 100644
--- a/src/sort/export_test.go
+++ b/src/sort/export_test.go
@@ -7,3 +7,7 @@ package sort
func Heapsort(data Interface) {
heapSort(data, 0, data.Len())
}
+
+func ReverseRange(data Interface, a, b int) {
+ reverseRange(data, a, b)
+}
diff --git a/src/sort/gen_sort_variants.go b/src/sort/gen_sort_variants.go
index 0ff18695446..2b671ceb02b 100644
--- a/src/sort/gen_sort_variants.go
+++ b/src/sort/gen_sort_variants.go
@@ -84,6 +84,9 @@ func main() {
ExtraArg: "",
DataType: "[]E",
Funcs: template.FuncMap{
+ "GreaterOrEqual": func(name, i, j string) string {
+ return fmt.Sprintf("(%s[%s] >= %s[%s])", name, i, name, j)
+ },
"Less": func(name, i, j string) string {
return fmt.Sprintf("(%s[%s] < %s[%s])", name, i, name, j)
},
@@ -103,6 +106,9 @@ func main() {
ExtraArg: ", less",
DataType: "[]E",
Funcs: template.FuncMap{
+ "GreaterOrEqual": func(name, i, j string) string {
+ return fmt.Sprintf("!less(%s[%s], %s[%s])", name, i, name, j)
+ },
"Less": func(name, i, j string) string {
return fmt.Sprintf("less(%s[%s], %s[%s])", name, i, name, j)
},
@@ -123,6 +129,9 @@ func main() {
ExtraArg: "",
DataType: "Interface",
Funcs: template.FuncMap{
+ "GreaterOrEqual": func(name, i, j string) string {
+ return fmt.Sprintf("!%s.Less(%s, %s)", name, i, j)
+ },
"Less": func(name, i, j string) string {
return fmt.Sprintf("%s.Less(%s, %s)", name, i, j)
},
@@ -143,6 +152,9 @@ func main() {
ExtraArg: "",
DataType: "lessSwap",
Funcs: template.FuncMap{
+ "GreaterOrEqual": func(name, i, j string) string {
+ return fmt.Sprintf("!%s.Less(%s, %s)", name, i, j)
+ },
"Less": func(name, i, j string) string {
return fmt.Sprintf("%s.Less(%s, %s)", name, i, j)
},
@@ -210,7 +222,7 @@ func siftDown{{.FuncSuffix}}{{.TypeParam}}(data {{.DataType}}, lo, hi, first int
if child+1 < hi && {{Less "data" "first+child" "first+child+1"}} {
child++
}
- if !{{Less "data" "first+root" "first+child"}} {
+ if {{GreaterOrEqual "data" "first+root" "first+child"}} {
return
}
{{Swap "data" "first+root" "first+child"}}
@@ -235,146 +247,283 @@ func heapSort{{.FuncSuffix}}{{.TypeParam}}(data {{.DataType}}, a, b int {{.Extra
}
}
-// Quicksort, loosely following Bentley and McIlroy,
-// "Engineering a Sort Function" SP&E November 1993.
+// pdqsort{{.FuncSuffix}} sorts data[a:b].
+// The algorithm based on pattern-defeating quicksort(pdqsort), but without the optimizations from BlockQuicksort.
+// pdqsort paper: https://arxiv.org/pdf/2106.05123.pdf
+// C++ implementation: https://github.com/orlp/pdqsort
+// Rust implementation: https://docs.rs/pdqsort/latest/pdqsort/
+// limit is the number of allowed bad (very unbalanced) pivots before falling back to heapsort.
+func pdqsort{{.FuncSuffix}}{{.TypeParam}}(data {{.DataType}}, a, b, limit int {{.ExtraParam}}) {
+ const maxInsertion = 12
-// medianOfThree{{.FuncSuffix}} moves the median of the three values data[m0], data[m1], data[m2] into data[m1].
-func medianOfThree{{.FuncSuffix}}{{.TypeParam}}(data {{.DataType}}, m1, m0, m2 int {{.ExtraParam}}) {
- // sort 3 elements
- if {{Less "data" "m1" "m0"}} {
- {{Swap "data" "m1" "m0"}}
- }
- // data[m0] <= data[m1]
- if {{Less "data" "m2" "m1"}} {
- {{Swap "data" "m2" "m1"}}
- // data[m0] <= data[m2] && data[m1] < data[m2]
- if {{Less "data" "m1" "m0"}} {
- {{Swap "data" "m1" "m0"}}
+ var (
+ wasBalanced = true // whether the last partitioning was reasonably balanced
+ wasPartitioned = true // whether the slice was already partitioned
+ )
+
+ for {
+ length := b - a
+
+ if length <= maxInsertion {
+ insertionSort{{.FuncSuffix}}(data, a, b {{.ExtraArg}})
+ return
+ }
+
+ // Fall back to heapsort if too many bad choices were made.
+ if limit == 0 {
+ heapSort{{.FuncSuffix}}(data, a, b {{.ExtraArg}})
+ return
+ }
+
+ // If the last partitioning was imbalanced, we need to breaking patterns.
+ if !wasBalanced {
+ breakPatterns{{.FuncSuffix}}(data, a, b {{.ExtraArg}})
+ limit--
+ }
+
+ pivot, hint := choosePivot{{.FuncSuffix}}(data, a, b {{.ExtraArg}})
+ if hint == decreasingHint {
+ reverseRange{{.FuncSuffix}}(data, a, b {{.ExtraArg}})
+ // The chosen pivot was pivot-a elements after the start of the array.
+ // After reversing it is pivot-a elements before the end of the array.
+ // The idea came from Rust's implementation.
+ pivot = (b - 1) - (pivot - a)
+ hint = increasingHint
+ }
+
+ // The slice is likely already sorted.
+ if wasBalanced && wasPartitioned && hint == increasingHint {
+ if partialInsertionSort{{.FuncSuffix}}(data, a, b {{.ExtraArg}}) {
+ return
+ }
+ }
+
+ // Probably the slice contains many duplicate elements, partition the slice into
+ // elements equal to and elements greater than the pivot.
+ if a > 0 && {{GreaterOrEqual "data" "a-1" "pivot"}} {
+ mid := partitionEqual{{.FuncSuffix}}(data, a, b, pivot {{.ExtraArg}})
+ a = mid
+ continue
+ }
+
+ mid, alreadyPartitioned := partition{{.FuncSuffix}}(data, a, b, pivot {{.ExtraArg}})
+ wasPartitioned = alreadyPartitioned
+
+ leftLen, rightLen := mid-a, b-mid
+ balanceThreshold := length / 8
+ if leftLen < rightLen {
+ wasBalanced = leftLen >= balanceThreshold
+ pdqsort{{.FuncSuffix}}(data, a, mid, limit {{.ExtraArg}})
+ a = mid + 1
+ } else {
+ wasBalanced = rightLen >= balanceThreshold
+ pdqsort{{.FuncSuffix}}(data, mid+1, b, limit {{.ExtraArg}})
+ b = mid
}
}
- // now data[m0] <= data[m1] <= data[m2]
}
-func swapRange{{.FuncSuffix}}{{.TypeParam}}(data {{.DataType}}, a, b, n int {{.ExtraParam}}) {
- for i := 0; i < n; i++ {
- {{Swap "data" "a+i" "b+i"}}
+// partition{{.FuncSuffix}} does one quicksort partition.
+// Let p = data[pivot]
+// Moves elements in data[a:b] around, so that data[i]<p and data[j]>=p for i<newpivot and j>newpivot.
+// On return, data[newpivot] = p
+func partition{{.FuncSuffix}}{{.TypeParam}}(data {{.DataType}}, a, b, pivot int {{.ExtraParam}}) (newpivot int, alreadyPartitioned bool) {
+ {{Swap "data" "a" "pivot"}}
+ i, j := a+1, b-1 // i and j are inclusive of the elements remaining to be partitioned
+
+ for i <= j && {{Less "data" "i" "a"}} {
+ i++
+ }
+ for i <= j && {{GreaterOrEqual "data" "j" "a"}} {
+ j--
}
+ if i > j {
+ {{Swap "data" "j" "a"}}
+ return j, true
+ }
+ {{Swap "data" "i" "j"}}
+ i++
+ j--
+
+ for {
+ for i <= j && {{Less "data" "i" "a"}} {
+ i++
+ }
+ for i <= j && {{GreaterOrEqual "data" "j" "a"}} {
+ j--
+ }
+ if i > j {
+ break
+ }
+ {{Swap "data" "i" "j"}}
+ i++
+ j--
+ }
+ {{Swap "data" "j" "a"}}
+ return j, false
}
-func doPivot{{.FuncSuffix}}{{.TypeParam}}(data {{.DataType}}, lo, hi int {{.ExtraParam}}) (midlo, midhi int) {
- m := int(uint(lo+hi) >> 1) // Written like this to avoid integer overflow.
- if hi-lo > 40 {
- // Tukey's "Ninther" median of three medians of three.
- s := (hi - lo) / 8
- medianOfThree{{.FuncSuffix}}(data, lo, lo+s, lo+2*s {{.ExtraArg}})
- medianOfThree{{.FuncSuffix}}(data, m, m-s, m+s {{.ExtraArg}})
- medianOfThree{{.FuncSuffix}}(data, hi-1, hi-1-s, hi-1-2*s {{.ExtraArg}})
- }
- medianOfThree{{.FuncSuffix}}(data, lo, m, hi-1 {{.ExtraArg}})
-
- // Invariants are:
- // data[lo] = pivot (set up by ChoosePivot)
- // data[lo < i < a] < pivot
- // data[a <= i < b] <= pivot
- // data[b <= i < c] unexamined
- // data[c <= i < hi-1] > pivot
- // data[hi-1] >= pivot
- pivot := lo
- a, c := lo+1, hi-1
-
- for ; a < c && {{Less "data" "a" "pivot"}}; a++ {
- }
- b := a
+// partitionEqual{{.FuncSuffix}} partitions data[a:b] into elements equal to data[pivot] followed by elements greater than data[pivot].
+// It assumed that data[a:b] does not contain elements smaller than the data[pivot].
+func partitionEqual{{.FuncSuffix}}{{.TypeParam}}(data {{.DataType}}, a, b, pivot int {{.ExtraParam}}) (newpivot int) {
+ {{Swap "data" "a" "pivot"}}
+ i, j := a+1, b-1 // i and j are inclusive of the elements remaining to be partitioned
+
for {
- for ; b < c && !{{Less "data" "pivot" "b"}}; b++ { // data[b] <= pivot
+ for i <= j && {{GreaterOrEqual "data" "a" "i"}} {
+ i++
}
- for ; b < c && {{Less "data" "pivot" "c-1"}}; c-- { // data[c-1] > pivot
+ for i <= j && {{Less "data" "a" "j"}} {
+ j--
}
- if b >= c {
+ if i > j {
break
}
- // data[b] > pivot; data[c-1] <= pivot
- {{Swap "data" "b" "c-1"}}
- b++
- c--
- }
- // If hi-c<3 then there are duplicates (by property of median of nine).
- // Let's be a bit more conservative, and set border to 5.
- protect := hi-c < 5
- if !protect && hi-c < (hi-lo)/4 {
- // Lets test some points for equality to pivot
- dups := 0
- if !{{Less "data" "pivot" "hi-1"}} { // data[hi-1] = pivot
- {{Swap "data" "c" "hi-1"}}
- c++
- dups++
- }
- if !{{Less "data" "b-1" "pivot"}} { // data[b-1] = pivot
- b--
- dups++
- }
- // m-lo = (hi-lo)/2 > 6
- // b-lo > (hi-lo)*3/4-1 > 8
- // ==> m < b ==> data[m] <= pivot
- if !{{Less "data" "m" "pivot"}} { // data[m] = pivot
- {{Swap "data" "m" "b-1"}}
- b--
- dups++
- }
- // if at least 2 points are equal to pivot, assume skewed distribution
- protect = dups > 1
- }
- if protect {
- // Protect against a lot of duplicates
- // Add invariant:
- // data[a <= i < b] unexamined
- // data[b <= i < c] = pivot
- for {
- for ; a < b && !{{Less "data" "b-1" "pivot"}}; b-- { // data[b] == pivot
- }
- for ; a < b && {{Less "data" "a" "pivot"}}; a++ { // data[a] < pivot
+ {{Swap "data" "i" "j"}}
+ i++
+ j--
+ }
+ return i
+}
+
+// partialInsertionSort{{.FuncSuffix}} partially sorts a slice, returns true if the slice is sorted at the end.
+func partialInsertionSort{{.FuncSuffix}}{{.TypeParam}}(data {{.DataType}}, a, b int {{.ExtraParam}}) bool {
+ const (
+ maxSteps = 5 // maximum number of adjacent out-of-order pairs that will get shifted
+ shortestShifting = 50 // don't shift any elements on short arrays
+ )
+ i := a + 1
+ for j := 0; j < maxSteps; j++ {
+ for i < b && {{GreaterOrEqual "data" "i" "i-1"}} {
+ i++
+ }
+
+ if i == b {
+ return true
+ }
+
+ if b-a < shortestShifting {
+ return false
+ }
+
+ {{Swap "data" "i" "i-1"}}
+
+ // Shift the smaller one to the left.
+ if i-a >= 2 {
+ for j := i - 1; j >= 1; j-- {
+ if {{GreaterOrEqual "data" "j" "j-1"}} {
+ break
+ }
+ {{Swap "data" "j" "j-1"}}
}
- if a >= b {
- break
+ }
+ // Shift the greater one to the right.
+ if b-i >= 2 {
+ for j := i + 1; j < b; j++ {
+ if {{GreaterOrEqual "data" "j" "j-1"}} {
+ break
+ }
+ {{Swap "data" "j" "j-1"}}
}
- // data[a] == pivot; data[b-1] < pivot
- {{Swap "data" "a" "b-1"}}
- a++
- b--
}
}
- // Swap pivot into middle
- {{Swap "data" "pivot" "b-1"}}
- return b - 1, c
+ return false
}
-func quickSort{{.FuncSuffix}}{{.TypeParam}}(data {{.DataType}}, a, b, maxDepth int {{.ExtraParam}}) {
- for b-a > 12 { // Use ShellSort for slices <= 12 elements
- if maxDepth == 0 {
- heapSort{{.FuncSuffix}}(data, a, b {{.ExtraArg}})
- return
- }
- maxDepth--
- mlo, mhi := doPivot{{.FuncSuffix}}(data, a, b {{.ExtraArg}})
- // Avoiding recursion on the larger subproblem guarantees
- // a stack depth of at most lg(b-a).
- if mlo-a < b-mhi {
- quickSort{{.FuncSuffix}}(data, a, mlo, maxDepth {{.ExtraArg}})
- a = mhi // i.e., quickSort{{.FuncSuffix}}(data, mhi, b)
- } else {
- quickSort{{.FuncSuffix}}(data, mhi, b, maxDepth {{.ExtraArg}})
- b = mlo // i.e., quickSort{{.FuncSuffix}}(data, a, mlo)
+// breakPatterns{{.FuncSuffix}} scatters some elements around in an attempt to break some patterns
+// that might cause imbalanced partitions in quicksort.
+func breakPatterns{{.FuncSuffix}}{{.TypeParam}}(data {{.DataType}}, a, b int {{.ExtraParam}}) {
+ length := b - a
+ if length >= 8 {
+ random := xorshift(length)
+ modulus := nextPowerOfTwo(length)
+
+ for idx := a + (length/4)*2 - 1; idx <= a + (length/4)*2 + 1; idx++ {
+ other := int(uint(random.Next()) & (modulus - 1))
+ if other >= length {
+ other -= length
+ }
+ {{Swap "data" "idx" "a+other"}}
}
}
- if b-a > 1 {
- // Do ShellSort pass with gap 6
- // It could be written in this simplified form cause b-a <= 12
- for i := a + 6; i < b; i++ {
- if {{Less "data" "i" "i-6"}} {
- {{Swap "data" "i" "i-6"}}
- }
+}
+
+// choosePivot{{.FuncSuffix}} chooses a pivot in data[a:b].
+//
+// [0,8): chooses a static pivot.
+// [8,shortestNinther): uses the simple median-of-three method.
+// [shortestNinther,∞): uses the Tukey ninther method.
+func choosePivot{{.FuncSuffix}}{{.TypeParam}}(data {{.DataType}}, a, b int {{.ExtraParam}}) (pivot int, hint sortedHint) {
+ const (
+ shortestNinther = 50
+ maxSwaps = 4 * 3
+ )
+
+ l := b - a
+
+ var (
+ swaps int
+ i = a + l/4*1
+ j = a + l/4*2
+ k = a + l/4*3
+ )
+
+ if l >= 8 {
+ if l >= shortestNinther {
+ // Tukey ninther method, the idea came from Rust's implementation.
+ i = medianAdjacent{{.FuncSuffix}}(data, i, &swaps {{.ExtraArg}})
+ j = medianAdjacent{{.FuncSuffix}}(data, j, &swaps {{.ExtraArg}})
+ k = medianAdjacent{{.FuncSuffix}}(data, k, &swaps {{.ExtraArg}})
}
- insertionSort{{.FuncSuffix}}(data, a, b {{.ExtraArg}})
+ // Find the median among i, j, k and stores it into j.
+ j = median{{.FuncSuffix}}(data, i, j, k, &swaps {{.ExtraArg}})
+ }
+
+ switch swaps {
+ case 0:
+ return j, increasingHint
+ case maxSwaps:
+ return j, decreasingHint
+ default:
+ return j, unknownHint
+ }
+}
+
+// order2{{.FuncSuffix}} returns x,y where data[x] <= data[y], where x,y=a,b or x,y=b,a.
+func order2{{.FuncSuffix}}{{.TypeParam}}(data {{.DataType}}, a, b int, swaps *int {{.ExtraParam}}) (int, int) {
+ if {{Less "data" "b" "a"}} {
+ *swaps++
+ return b, a
+ }
+ return a, b
+}
+
+// median{{.FuncSuffix}} returns x where data[x] is the median of data[a],data[b],data[c], where x is a, b, or c.
+func median{{.FuncSuffix}}{{.TypeParam}}(data {{.DataType}}, a, b, c int, swaps *int {{.ExtraParam}}) int {
+ a, b = order2{{.FuncSuffix}}(data, a, b, swaps {{.ExtraArg}})
+ b, c = order2{{.FuncSuffix}}(data, b, c, swaps {{.ExtraArg}})
+ a, b = order2{{.FuncSuffix}}(data, a, b, swaps {{.ExtraArg}})
+ return b
+}
+
+// medianAdjacent{{.FuncSuffix}} finds the median of data[a - 1], data[a], data[a + 1] and stores the index into a.
+func medianAdjacent{{.FuncSuffix}}{{.TypeParam}}(data {{.DataType}}, a int, swaps *int {{.ExtraParam}}) int {
+ return median{{.FuncSuffix}}(data, a-1, a, a+1, swaps {{.ExtraArg}})
+}
+
+func reverseRange{{.FuncSuffix}}{{.TypeParam}}(data {{.DataType}}, a, b int {{.ExtraParam}}) {
+ i := a
+ j := b - 1
+ for i < j {
+ {{Swap "data" "i" "j"}}
+ i++
+ j--
+ }
+}
+
+func swapRange{{.FuncSuffix}}{{.TypeParam}}(data {{.DataType}}, a, b, n int {{.ExtraParam}}) {
+ for i := 0; i < n; i++ {
+ {{Swap "data" "a+i" "b+i"}}
}
}
@@ -457,7 +606,7 @@ func symMerge{{.FuncSuffix}}{{.TypeParam}}(data {{.DataType}}, a, m, b int {{.Ex
j := m
for i < j {
h := int(uint(i+j) >> 1)
- if !{{Less "data" "m" "h"}} {
+ if {{GreaterOrEqual "data" "m" "h"}} {
i = h + 1
} else {
j = h
@@ -484,7 +633,7 @@ func symMerge{{.FuncSuffix}}{{.TypeParam}}(data {{.DataType}}, a, m, b int {{.Ex
for start < r {
c := int(uint(start+r) >> 1)
- if !{{Less "data" "p-c" "c"}} {
+ if {{GreaterOrEqual "data" "p-c" "c"}} {
start = c + 1
} else {
r = c
diff --git a/src/sort/search.go b/src/sort/search.go
index 601557a94b3..434349416ee 100644
--- a/src/sort/search.go
+++ b/src/sort/search.go
@@ -72,6 +72,47 @@ func Search(n int, f func(int) bool) int {
return i
}
+// Find uses binary search to find and return the smallest index i in [0, n)
+// at which cmp(i) <= 0. If there is no such index i, Find returns i = n.
+// The found result is true if i < n and cmp(i) == 0.
+// Find calls cmp(i) only for i in the range [0, n).
+//
+// To permit binary search, Find requires that cmp(i) > 0 for a leading
+// prefix of the range, cmp(i) == 0 in the middle, and cmp(i) < 0 for
+// the final suffix of the range. (Each subrange could be empty.)
+// The usual way to establish this condition is to interpret cmp(i)
+// as a comparison of a desired target value t against entry i in an
+// underlying indexed data structure x, returning <0, 0, and >0
+// when t < x[i], t == x[i], and t > x[i], respectively.
+//
+// For example, to look for a particular string in a sorted, random-access
+// list of strings:
+// i, found := sort.Find(x.Len(), func(i int) int {
+// return strings.Compare(target, x.At(i))
+// })
+// if found {
+// fmt.Printf("found %s at entry %d\n", target, i)
+// } else {
+// fmt.Printf("%s not found, would insert at %d", target, i)
+// }
+func Find(n int, cmp func(int) int) (i int, found bool) {
+ // The invariants here are similar to the ones in Search.
+ // Define cmp(-1) > 0 and cmp(n) <= 0
+ // Invariant: cmp(i-1) > 0, cmp(j) <= 0
+ i, j := 0, n
+ for i < j {
+ h := int(uint(i+j) >> 1) // avoid overflow when computing h
+ // i ≤ h < j
+ if cmp(h) > 0 {
+ i = h + 1 // preserves cmp(i-1) > 0
+ } else {
+ j = h // preserves cmp(j) <= 0
+ }
+ }
+ // i == j, cmp(i-1) > 0 and cmp(j) <= 0
+ return i, i < n && cmp(i) == 0
+}
+
// Convenience wrappers for common cases.
// SearchInts searches for x in a sorted slice of ints and returns the index
diff --git a/src/sort/search_test.go b/src/sort/search_test.go
index f06897ee21e..49813eaecb0 100644
--- a/src/sort/search_test.go
+++ b/src/sort/search_test.go
@@ -7,6 +7,7 @@ package sort_test
import (
"runtime"
. "sort"
+ stringspkg "strings"
"testing"
)
@@ -57,6 +58,80 @@ func TestSearch(t *testing.T) {
}
}
+func TestFind(t *testing.T) {
+ str1 := []string{"foo"}
+ str2 := []string{"ab", "ca"}
+ str3 := []string{"mo", "qo", "vo"}
+ str4 := []string{"ab", "ad", "ca", "xy"}
+
+ // slice with repeating elements
+ strRepeats := []string{"ba", "ca", "da", "da", "da", "ka", "ma", "ma", "ta"}
+
+ // slice with all element equal
+ strSame := []string{"xx", "xx", "xx"}
+
+ tests := []struct {
+ data []string
+ target string
+ wantPos int
+ wantFound bool
+ }{
+ {[]string{}, "foo", 0, false},
+ {[]string{}, "", 0, false},
+
+ {str1, "foo", 0, true},
+ {str1, "bar", 0, false},
+ {str1, "zx", 1, false},
+
+ {str2, "aa", 0, false},
+ {str2, "ab", 0, true},
+ {str2, "ad", 1, false},
+ {str2, "ca", 1, true},
+ {str2, "ra", 2, false},
+
+ {str3, "bb", 0, false},
+ {str3, "mo", 0, true},
+ {str3, "nb", 1, false},
+ {str3, "qo", 1, true},
+ {str3, "tr", 2, false},
+ {str3, "vo", 2, true},
+ {str3, "xr", 3, false},
+
+ {str4, "aa", 0, false},
+ {str4, "ab", 0, true},
+ {str4, "ac", 1, false},
+ {str4, "ad", 1, true},
+ {str4, "ax", 2, false},
+ {str4, "ca", 2, true},
+ {str4, "cc", 3, false},
+ {str4, "dd", 3, false},
+ {str4, "xy", 3, true},
+ {str4, "zz", 4, false},
+
+ {strRepeats, "da", 2, true},
+ {strRepeats, "db", 5, false},
+ {strRepeats, "ma", 6, true},
+ {strRepeats, "mb", 8, false},
+
+ {strSame, "xx", 0, true},
+ {strSame, "ab", 0, false},
+ {strSame, "zz", 3, false},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.target, func(t *testing.T) {
+ cmp := func(i int) int {
+ return stringspkg.Compare(tt.target, tt.data[i])
+ }
+
+ pos, found := Find(len(tt.data), cmp)
+ if pos != tt.wantPos || found != tt.wantFound {
+ t.Errorf("Find got (%v, %v), want (%v, %v)", pos, found, tt.wantPos, tt.wantFound)
+ }
+ })
+ }
+}
+
// log2 computes the binary logarithm of x, rounded up to the next integer.
// (log2(0) == 0, log2(1) == 0, log2(2) == 1, log2(3) == 2, etc.)
func log2(x int) int {
@@ -158,3 +233,34 @@ func TestSearchExhaustive(t *testing.T) {
}
}
}
+
+// Abstract exhaustive test for Find.
+func TestFindExhaustive(t *testing.T) {
+ // Test Find for different sequence sizes and search targets.
+ // For each size, we have a (unmaterialized) sequence of integers:
+ // 2,4...size*2
+ // And we're looking for every possible integer between 1 and size*2 + 1.
+ for size := 0; size <= 100; size++ {
+ for x := 1; x <= size*2+1; x++ {
+ var wantFound bool
+ var wantPos int
+
+ cmp := func(i int) int {
+ // Encodes the unmaterialized sequence with elem[i] == (i+1)*2
+ return x - (i+1)*2
+ }
+ pos, found := Find(size, cmp)
+
+ if x%2 == 0 {
+ wantPos = x/2 - 1
+ wantFound = true
+ } else {
+ wantPos = x / 2
+ wantFound = false
+ }
+ if found != wantFound || pos != wantPos {
+ t.Errorf("Find(%d, %d): got (%v, %v), want (%v, %v)", size, x, pos, found, wantPos, wantFound)
+ }
+ }
+ }
+}
diff --git a/src/sort/slice.go b/src/sort/slice.go
index ba5c2e2f3d3..443182b42eb 100644
--- a/src/sort/slice.go
+++ b/src/sort/slice.go
@@ -4,6 +4,8 @@
package sort
+import "math/bits"
+
// Slice sorts the slice x given the provided less function.
// It panics if x is not a slice.
//
@@ -17,7 +19,8 @@ func Slice(x any, less func(i, j int) bool) {
rv := reflectValueOf(x)
swap := reflectSwapper(x)
length := rv.Len()
- quickSort_func(lessSwap{less, swap}, 0, length, maxDepth(length))
+ limit := bits.Len(uint(length))
+ pdqsort_func(lessSwap{less, swap}, 0, length, limit)
}
// SliceStable sorts the slice x using the provided less
diff --git a/src/sort/sort.go b/src/sort/sort.go
index aed0eaba303..68e2f0d082f 100644
--- a/src/sort/sort.go
+++ b/src/sort/sort.go
@@ -7,6 +7,8 @@
// Package sort provides primitives for sorting slices and user-defined collections.
package sort
+import "math/bits"
+
// An implementation of Interface can be sorted by the routines in this package.
// The methods refer to elements of the underlying collection by integer index.
type Interface interface {
@@ -39,17 +41,34 @@ type Interface interface {
// data.Less and data.Swap. The sort is not guaranteed to be stable.
func Sort(data Interface) {
n := data.Len()
- quickSort(data, 0, n, maxDepth(n))
+ if n <= 1 {
+ return
+ }
+ limit := bits.Len(uint(n))
+ pdqsort(data, 0, n, limit)
}
-// maxDepth returns a threshold at which quicksort should switch
-// to heapsort. It returns 2*ceil(lg(n+1)).
-func maxDepth(n int) int {
- var depth int
- for i := n; i > 0; i >>= 1 {
- depth++
- }
- return depth * 2
+type sortedHint int // hint for pdqsort when choosing the pivot
+
+const (
+ unknownHint sortedHint = iota
+ increasingHint
+ decreasingHint
+)
+
+// xorshift paper: https://www.jstatsoft.org/article/view/v008i14/xorshift.pdf
+type xorshift uint64
+
+func (r *xorshift) Next() uint64 {
+ *r ^= *r << 13
+ *r ^= *r >> 17
+ *r ^= *r << 5
+ return uint64(*r)
+}
+
+func nextPowerOfTwo(length int) uint {
+ shift := uint(bits.Len(uint(length)))
+ return uint(1 << shift)
}
// lessSwap is a pair of Less and Swap function for use with the
diff --git a/src/sort/sort_test.go b/src/sort/sort_test.go
index bfff3528d3f..862bba2d441 100644
--- a/src/sort/sort_test.go
+++ b/src/sort/sort_test.go
@@ -122,6 +122,37 @@ func TestReverseSortIntSlice(t *testing.T) {
}
}
+func TestBreakPatterns(t *testing.T) {
+ // Special slice used to trigger breakPatterns.
+ data := make([]int, 30)
+ for i := range data {
+ data[i] = 10
+ }
+ data[(len(data)/4)*1] = 0
+ data[(len(data)/4)*2] = 1
+ data[(len(data)/4)*3] = 2
+ Sort(IntSlice(data))
+}
+
+func TestReverseRange(t *testing.T) {
+ data := []int{1, 2, 3, 4, 5, 6, 7}
+ ReverseRange(IntSlice(data), 0, len(data))
+ for i := len(data) - 1; i > 0; i-- {
+ if data[i] > data[i-1] {
+ t.Fatalf("reverseRange didn't work")
+ }
+ }
+
+ data1 := []int{1, 2, 3, 4, 5, 6, 7}
+ data2 := []int{1, 2, 5, 4, 3, 6, 7}
+ ReverseRange(IntSlice(data1), 2, 5)
+ for i, v := range data1 {
+ if v != data2[i] {
+ t.Fatalf("reverseRange didn't work")
+ }
+ }
+}
+
type nonDeterministicTestingData struct {
r *rand.Rand
}
@@ -220,6 +251,45 @@ func BenchmarkSortInt1K(b *testing.B) {
}
}
+func BenchmarkSortInt1K_Sorted(b *testing.B) {
+ b.StopTimer()
+ for i := 0; i < b.N; i++ {
+ data := make([]int, 1<<10)
+ for i := 0; i < len(data); i++ {
+ data[i] = i
+ }
+ b.StartTimer()
+ Ints(data)
+ b.StopTimer()
+ }
+}
+
+func BenchmarkSortInt1K_Reversed(b *testing.B) {
+ b.StopTimer()
+ for i := 0; i < b.N; i++ {
+ data := make([]int, 1<<10)
+ for i := 0; i < len(data); i++ {
+ data[i] = len(data) - i
+ }
+ b.StartTimer()
+ Ints(data)
+ b.StopTimer()
+ }
+}
+
+func BenchmarkSortInt1K_Mod8(b *testing.B) {
+ b.StopTimer()
+ for i := 0; i < b.N; i++ {
+ data := make([]int, 1<<10)
+ for i := 0; i < len(data); i++ {
+ data[i] = i % 8
+ }
+ b.StartTimer()
+ Ints(data)
+ b.StopTimer()
+ }
+}
+
func BenchmarkStableInt1K(b *testing.B) {
b.StopTimer()
unsorted := make([]int, 1<<10)
diff --git a/src/sort/zsortfunc.go b/src/sort/zsortfunc.go
index 80c8a779959..49b6169b976 100644
--- a/src/sort/zsortfunc.go
+++ b/src/sort/zsortfunc.go
@@ -52,146 +52,283 @@ func heapSort_func(data lessSwap, a, b int) {
}
}
-// Quicksort, loosely following Bentley and McIlroy,
-// "Engineering a Sort Function" SP&E November 1993.
-
-// medianOfThree_func moves the median of the three values data[m0], data[m1], data[m2] into data[m1].
-func medianOfThree_func(data lessSwap, m1, m0, m2 int) {
- // sort 3 elements
- if data.Less(m1, m0) {
- data.Swap(m1, m0)
- }
- // data[m0] <= data[m1]
- if data.Less(m2, m1) {
- data.Swap(m2, m1)
- // data[m0] <= data[m2] && data[m1] < data[m2]
- if data.Less(m1, m0) {
- data.Swap(m1, m0)
+// pdqsort_func sorts data[a:b].
+// The algorithm based on pattern-defeating quicksort(pdqsort), but without the optimizations from BlockQuicksort.
+// pdqsort paper: https://arxiv.org/pdf/2106.05123.pdf
+// C++ implementation: https://github.com/orlp/pdqsort
+// Rust implementation: https://docs.rs/pdqsort/latest/pdqsort/
+// limit is the number of allowed bad (very unbalanced) pivots before falling back to heapsort.
+func pdqsort_func(data lessSwap, a, b, limit int) {
+ const maxInsertion = 12
+
+ var (
+ wasBalanced = true // whether the last partitioning was reasonably balanced
+ wasPartitioned = true // whether the slice was already partitioned
+ )
+
+ for {
+ length := b - a
+
+ if length <= maxInsertion {
+ insertionSort_func(data, a, b)
+ return
}
- }
- // now data[m0] <= data[m1] <= data[m2]
-}
-func swapRange_func(data lessSwap, a, b, n int) {
- for i := 0; i < n; i++ {
- data.Swap(a+i, b+i)
+ // Fall back to heapsort if too many bad choices were made.
+ if limit == 0 {
+ heapSort_func(data, a, b)
+ return
+ }
+
+ // If the last partitioning was imbalanced, we need to breaking patterns.
+ if !wasBalanced {
+ breakPatterns_func(data, a, b)
+ limit--
+ }
+
+ pivot, hint := choosePivot_func(data, a, b)
+ if hint == decreasingHint {
+ reverseRange_func(data, a, b)
+ // The chosen pivot was pivot-a elements after the start of the array.
+ // After reversing it is pivot-a elements before the end of the array.
+ // The idea came from Rust's implementation.
+ pivot = (b - 1) - (pivot - a)
+ hint = increasingHint
+ }
+
+ // The slice is likely already sorted.
+ if wasBalanced && wasPartitioned && hint == increasingHint {
+ if partialInsertionSort_func(data, a, b) {
+ return
+ }
+ }
+
+ // Probably the slice contains many duplicate elements, partition the slice into
+ // elements equal to and elements greater than the pivot.
+ if a > 0 && !data.Less(a-1, pivot) {
+ mid := partitionEqual_func(data, a, b, pivot)
+ a = mid
+ continue
+ }
+
+ mid, alreadyPartitioned := partition_func(data, a, b, pivot)
+ wasPartitioned = alreadyPartitioned
+
+ leftLen, rightLen := mid-a, b-mid
+ balanceThreshold := length / 8
+ if leftLen < rightLen {
+ wasBalanced = leftLen >= balanceThreshold
+ pdqsort_func(data, a, mid, limit)
+ a = mid + 1
+ } else {
+ wasBalanced = rightLen >= balanceThreshold
+ pdqsort_func(data, mid+1, b, limit)
+ b = mid
+ }
}
}
-func doPivot_func(data lessSwap, lo, hi int) (midlo, midhi int) {
- m := int(uint(lo+hi) >> 1) // Written like this to avoid integer overflow.
- if hi-lo > 40 {
- // Tukey's "Ninther" median of three medians of three.
- s := (hi - lo) / 8
- medianOfThree_func(data, lo, lo+s, lo+2*s)
- medianOfThree_func(data, m, m-s, m+s)
- medianOfThree_func(data, hi-1, hi-1-s, hi-1-2*s)
+// partition_func does one quicksort partition.
+// Let p = data[pivot]
+// Moves elements in data[a:b] around, so that data[i]<p and data[j]>=p for i<newpivot and j>newpivot.
+// On return, data[newpivot] = p
+func partition_func(data lessSwap, a, b, pivot int) (newpivot int, alreadyPartitioned bool) {
+ data.Swap(a, pivot)
+ i, j := a+1, b-1 // i and j are inclusive of the elements remaining to be partitioned
+
+ for i <= j && data.Less(i, a) {
+ i++
+ }
+ for i <= j && !data.Less(j, a) {
+ j--
}
- medianOfThree_func(data, lo, m, hi-1)
-
- // Invariants are:
- // data[lo] = pivot (set up by ChoosePivot)
- // data[lo < i < a] < pivot
- // data[a <= i < b] <= pivot
- // data[b <= i < c] unexamined
- // data[c <= i < hi-1] > pivot
- // data[hi-1] >= pivot
- pivot := lo
- a, c := lo+1, hi-1
-
- for ; a < c && data.Less(a, pivot); a++ {
+ if i > j {
+ data.Swap(j, a)
+ return j, true
}
- b := a
+ data.Swap(i, j)
+ i++
+ j--
+
for {
- for ; b < c && !data.Less(pivot, b); b++ { // data[b] <= pivot
+ for i <= j && data.Less(i, a) {
+ i++
}
- for ; b < c && data.Less(pivot, c-1); c-- { // data[c-1] > pivot
+ for i <= j && !data.Less(j, a) {
+ j--
}
- if b >= c {
+ if i > j {
break
}
- // data[b] > pivot; data[c-1] <= pivot
- data.Swap(b, c-1)
- b++
- c--
+ data.Swap(i, j)
+ i++
+ j--
}
- // If hi-c<3 then there are duplicates (by property of median of nine).
- // Let's be a bit more conservative, and set border to 5.
- protect := hi-c < 5
- if !protect && hi-c < (hi-lo)/4 {
- // Lets test some points for equality to pivot
- dups := 0
- if !data.Less(pivot, hi-1) { // data[hi-1] = pivot
- data.Swap(c, hi-1)
- c++
- dups++
- }
- if !data.Less(b-1, pivot) { // data[b-1] = pivot
- b--
- dups++
- }
- // m-lo = (hi-lo)/2 > 6
- // b-lo > (hi-lo)*3/4-1 > 8
- // ==> m < b ==> data[m] <= pivot
- if !data.Less(m, pivot) { // data[m] = pivot
- data.Swap(m, b-1)
- b--
- dups++
- }
- // if at least 2 points are equal to pivot, assume skewed distribution
- protect = dups > 1
+ data.Swap(j, a)
+ return j, false
+}
+
+// partitionEqual_func partitions data[a:b] into elements equal to data[pivot] followed by elements greater than data[pivot].
+// It assumed that data[a:b] does not contain elements smaller than the data[pivot].
+func partitionEqual_func(data lessSwap, a, b, pivot int) (newpivot int) {
+ data.Swap(a, pivot)
+ i, j := a+1, b-1 // i and j are inclusive of the elements remaining to be partitioned
+
+ for {
+ for i <= j && !data.Less(a, i) {
+ i++
+ }
+ for i <= j && data.Less(a, j) {
+ j--
+ }
+ if i > j {
+ break
+ }
+ data.Swap(i, j)
+ i++
+ j--
}
- if protect {
- // Protect against a lot of duplicates
- // Add invariant:
- // data[a <= i < b] unexamined
- // data[b <= i < c] = pivot
- for {
- for ; a < b && !data.Less(b-1, pivot); b-- { // data[b] == pivot
- }
- for ; a < b && data.Less(a, pivot); a++ { // data[a] < pivot
+ return i
+}
+
+// partialInsertionSort_func partially sorts a slice, returns true if the slice is sorted at the end.
+func partialInsertionSort_func(data lessSwap, a, b int) bool {
+ const (
+ maxSteps = 5 // maximum number of adjacent out-of-order pairs that will get shifted
+ shortestShifting = 50 // don't shift any elements on short arrays
+ )
+ i := a + 1
+ for j := 0; j < maxSteps; j++ {
+ for i < b && !data.Less(i, i-1) {
+ i++
+ }
+
+ if i == b {
+ return true
+ }
+
+ if b-a < shortestShifting {
+ return false
+ }
+
+ data.Swap(i, i-1)
+
+ // Shift the smaller one to the left.
+ if i-a >= 2 {
+ for j := i - 1; j >= 1; j-- {
+ if !data.Less(j, j-1) {
+ break
+ }
+ data.Swap(j, j-1)
}
- if a >= b {
- break
+ }
+ // Shift the greater one to the right.
+ if b-i >= 2 {
+ for j := i + 1; j < b; j++ {
+ if !data.Less(j, j-1) {
+ break
+ }
+ data.Swap(j, j-1)
}
- // data[a] == pivot; data[b-1] < pivot
- data.Swap(a, b-1)
- a++
- b--
}
}
- // Swap pivot into middle
- data.Swap(pivot, b-1)
- return b - 1, c
+ return false
}
-func quickSort_func(data lessSwap, a, b, maxDepth int) {
- for b-a > 12 { // Use ShellSort for slices <= 12 elements
- if maxDepth == 0 {
- heapSort_func(data, a, b)
- return
- }
- maxDepth--
- mlo, mhi := doPivot_func(data, a, b)
- // Avoiding recursion on the larger subproblem guarantees
- // a stack depth of at most lg(b-a).
- if mlo-a < b-mhi {
- quickSort_func(data, a, mlo, maxDepth)
- a = mhi // i.e., quickSort_func(data, mhi, b)
- } else {
- quickSort_func(data, mhi, b, maxDepth)
- b = mlo // i.e., quickSort_func(data, a, mlo)
+// breakPatterns_func scatters some elements around in an attempt to break some patterns
+// that might cause imbalanced partitions in quicksort.
+func breakPatterns_func(data lessSwap, a, b int) {
+ length := b - a
+ if length >= 8 {
+ random := xorshift(length)
+ modulus := nextPowerOfTwo(length)
+
+ for idx := a + (length/4)*2 - 1; idx <= a+(length/4)*2+1; idx++ {
+ other := int(uint(random.Next()) & (modulus - 1))
+ if other >= length {
+ other -= length
+ }
+ data.Swap(idx, a+other)
}
}
- if b-a > 1 {
- // Do ShellSort pass with gap 6
- // It could be written in this simplified form cause b-a <= 12
- for i := a + 6; i < b; i++ {
- if data.Less(i, i-6) {
- data.Swap(i, i-6)
- }
+}
+
+// choosePivot_func chooses a pivot in data[a:b].
+//
+// [0,8): chooses a static pivot.
+// [8,shortestNinther): uses the simple median-of-three method.
+// [shortestNinther,∞): uses the Tukey ninther method.
+func choosePivot_func(data lessSwap, a, b int) (pivot int, hint sortedHint) {
+ const (
+ shortestNinther = 50
+ maxSwaps = 4 * 3
+ )
+
+ l := b - a
+
+ var (
+ swaps int
+ i = a + l/4*1
+ j = a + l/4*2
+ k = a + l/4*3
+ )
+
+ if l >= 8 {
+ if l >= shortestNinther {
+ // Tukey ninther method, the idea came from Rust's implementation.
+ i = medianAdjacent_func(data, i, &swaps)
+ j = medianAdjacent_func(data, j, &swaps)
+ k = medianAdjacent_func(data, k, &swaps)
}
- insertionSort_func(data, a, b)
+ // Find the median among i, j, k and stores it into j.
+ j = median_func(data, i, j, k, &swaps)
+ }
+
+ switch swaps {
+ case 0:
+ return j, increasingHint
+ case maxSwaps:
+ return j, decreasingHint
+ default:
+ return j, unknownHint
+ }
+}
+
+// order2_func returns x,y where data[x] <= data[y], where x,y=a,b or x,y=b,a.
+func order2_func(data lessSwap, a, b int, swaps *int) (int, int) {
+ if data.Less(b, a) {
+ *swaps++
+ return b, a
+ }
+ return a, b
+}
+
+// median_func returns x where data[x] is the median of data[a],data[b],data[c], where x is a, b, or c.
+func median_func(data lessSwap, a, b, c int, swaps *int) int {
+ a, b = order2_func(data, a, b, swaps)
+ b, c = order2_func(data, b, c, swaps)
+ a, b = order2_func(data, a, b, swaps)
+ return b
+}
+
+// medianAdjacent_func finds the median of data[a - 1], data[a], data[a + 1] and stores the index into a.
+func medianAdjacent_func(data lessSwap, a int, swaps *int) int {
+ return median_func(data, a-1, a, a+1, swaps)
+}
+
+func reverseRange_func(data lessSwap, a, b int) {
+ i := a
+ j := b - 1
+ for i < j {
+ data.Swap(i, j)
+ i++
+ j--
+ }
+}
+
+func swapRange_func(data lessSwap, a, b, n int) {
+ for i := 0; i < n; i++ {
+ data.Swap(a+i, b+i)
}
}
diff --git a/src/sort/zsortinterface.go b/src/sort/zsortinterface.go
index e0d70936785..51fa5032e99 100644
--- a/src/sort/zsortinterface.go
+++ b/src/sort/zsortinterface.go
@@ -52,146 +52,283 @@ func heapSort(data Interface, a, b int) {
}
}
-// Quicksort, loosely following Bentley and McIlroy,
-// "Engineering a Sort Function" SP&E November 1993.
-
-// medianOfThree moves the median of the three values data[m0], data[m1], data[m2] into data[m1].
-func medianOfThree(data Interface, m1, m0, m2 int) {
- // sort 3 elements
- if data.Less(m1, m0) {
- data.Swap(m1, m0)
- }
- // data[m0] <= data[m1]
- if data.Less(m2, m1) {
- data.Swap(m2, m1)
- // data[m0] <= data[m2] && data[m1] < data[m2]
- if data.Less(m1, m0) {
- data.Swap(m1, m0)
+// pdqsort sorts data[a:b].
+// The algorithm based on pattern-defeating quicksort(pdqsort), but without the optimizations from BlockQuicksort.
+// pdqsort paper: https://arxiv.org/pdf/2106.05123.pdf
+// C++ implementation: https://github.com/orlp/pdqsort
+// Rust implementation: https://docs.rs/pdqsort/latest/pdqsort/
+// limit is the number of allowed bad (very unbalanced) pivots before falling back to heapsort.
+func pdqsort(data Interface, a, b, limit int) {
+ const maxInsertion = 12
+
+ var (
+ wasBalanced = true // whether the last partitioning was reasonably balanced
+ wasPartitioned = true // whether the slice was already partitioned
+ )
+
+ for {
+ length := b - a
+
+ if length <= maxInsertion {
+ insertionSort(data, a, b)
+ return
}
- }
- // now data[m0] <= data[m1] <= data[m2]
-}
-func swapRange(data Interface, a, b, n int) {
- for i := 0; i < n; i++ {
- data.Swap(a+i, b+i)
+ // Fall back to heapsort if too many bad choices were made.
+ if limit == 0 {
+ heapSort(data, a, b)
+ return
+ }
+
+ // If the last partitioning was imbalanced, we need to breaking patterns.
+ if !wasBalanced {
+ breakPatterns(data, a, b)
+ limit--
+ }
+
+ pivot, hint := choosePivot(data, a, b)
+ if hint == decreasingHint {
+ reverseRange(data, a, b)
+ // The chosen pivot was pivot-a elements after the start of the array.
+ // After reversing it is pivot-a elements before the end of the array.
+ // The idea came from Rust's implementation.
+ pivot = (b - 1) - (pivot - a)
+ hint = increasingHint
+ }
+
+ // The slice is likely already sorted.
+ if wasBalanced && wasPartitioned && hint == increasingHint {
+ if partialInsertionSort(data, a, b) {
+ return
+ }
+ }
+
+ // Probably the slice contains many duplicate elements, partition the slice into
+ // elements equal to and elements greater than the pivot.
+ if a > 0 && !data.Less(a-1, pivot) {
+ mid := partitionEqual(data, a, b, pivot)
+ a = mid
+ continue
+ }
+
+ mid, alreadyPartitioned := partition(data, a, b, pivot)
+ wasPartitioned = alreadyPartitioned
+
+ leftLen, rightLen := mid-a, b-mid
+ balanceThreshold := length / 8
+ if leftLen < rightLen {
+ wasBalanced = leftLen >= balanceThreshold
+ pdqsort(data, a, mid, limit)
+ a = mid + 1
+ } else {
+ wasBalanced = rightLen >= balanceThreshold
+ pdqsort(data, mid+1, b, limit)
+ b = mid
+ }
}
}
-func doPivot(data Interface, lo, hi int) (midlo, midhi int) {
- m := int(uint(lo+hi) >> 1) // Written like this to avoid integer overflow.
- if hi-lo > 40 {
- // Tukey's "Ninther" median of three medians of three.
- s := (hi - lo) / 8
- medianOfThree(data, lo, lo+s, lo+2*s)
- medianOfThree(data, m, m-s, m+s)
- medianOfThree(data, hi-1, hi-1-s, hi-1-2*s)
+// partition does one quicksort partition.
+// Let p = data[pivot]
+// Moves elements in data[a:b] around, so that data[i]<p and data[j]>=p for i<newpivot and j>newpivot.
+// On return, data[newpivot] = p
+func partition(data Interface, a, b, pivot int) (newpivot int, alreadyPartitioned bool) {
+ data.Swap(a, pivot)
+ i, j := a+1, b-1 // i and j are inclusive of the elements remaining to be partitioned
+
+ for i <= j && data.Less(i, a) {
+ i++
+ }
+ for i <= j && !data.Less(j, a) {
+ j--
}
- medianOfThree(data, lo, m, hi-1)
-
- // Invariants are:
- // data[lo] = pivot (set up by ChoosePivot)
- // data[lo < i < a] < pivot
- // data[a <= i < b] <= pivot
- // data[b <= i < c] unexamined
- // data[c <= i < hi-1] > pivot
- // data[hi-1] >= pivot
- pivot := lo
- a, c := lo+1, hi-1
-
- for ; a < c && data.Less(a, pivot); a++ {
+ if i > j {
+ data.Swap(j, a)
+ return j, true
}
- b := a
+ data.Swap(i, j)
+ i++
+ j--
+
for {
- for ; b < c && !data.Less(pivot, b); b++ { // data[b] <= pivot
+ for i <= j && data.Less(i, a) {
+ i++
}
- for ; b < c && data.Less(pivot, c-1); c-- { // data[c-1] > pivot
+ for i <= j && !data.Less(j, a) {
+ j--
}
- if b >= c {
+ if i > j {
break
}
- // data[b] > pivot; data[c-1] <= pivot
- data.Swap(b, c-1)
- b++
- c--
+ data.Swap(i, j)
+ i++
+ j--
}
- // If hi-c<3 then there are duplicates (by property of median of nine).
- // Let's be a bit more conservative, and set border to 5.
- protect := hi-c < 5
- if !protect && hi-c < (hi-lo)/4 {
- // Lets test some points for equality to pivot
- dups := 0
- if !data.Less(pivot, hi-1) { // data[hi-1] = pivot
- data.Swap(c, hi-1)
- c++
- dups++
- }
- if !data.Less(b-1, pivot) { // data[b-1] = pivot
- b--
- dups++
- }
- // m-lo = (hi-lo)/2 > 6
- // b-lo > (hi-lo)*3/4-1 > 8
- // ==> m < b ==> data[m] <= pivot
- if !data.Less(m, pivot) { // data[m] = pivot
- data.Swap(m, b-1)
- b--
- dups++
- }
- // if at least 2 points are equal to pivot, assume skewed distribution
- protect = dups > 1
+ data.Swap(j, a)
+ return j, false
+}
+
+// partitionEqual partitions data[a:b] into elements equal to data[pivot] followed by elements greater than data[pivot].
+// It assumed that data[a:b] does not contain elements smaller than the data[pivot].
+func partitionEqual(data Interface, a, b, pivot int) (newpivot int) {
+ data.Swap(a, pivot)
+ i, j := a+1, b-1 // i and j are inclusive of the elements remaining to be partitioned
+
+ for {
+ for i <= j && !data.Less(a, i) {
+ i++
+ }
+ for i <= j && data.Less(a, j) {
+ j--
+ }
+ if i > j {
+ break
+ }
+ data.Swap(i, j)
+ i++
+ j--
}
- if protect {
- // Protect against a lot of duplicates
- // Add invariant:
- // data[a <= i < b] unexamined
- // data[b <= i < c] = pivot
- for {
- for ; a < b && !data.Less(b-1, pivot); b-- { // data[b] == pivot
- }
- for ; a < b && data.Less(a, pivot); a++ { // data[a] < pivot
+ return i
+}
+
+// partialInsertionSort partially sorts a slice, returns true if the slice is sorted at the end.
+func partialInsertionSort(data Interface, a, b int) bool {
+ const (
+ maxSteps = 5 // maximum number of adjacent out-of-order pairs that will get shifted
+ shortestShifting = 50 // don't shift any elements on short arrays
+ )
+ i := a + 1
+ for j := 0; j < maxSteps; j++ {
+ for i < b && !data.Less(i, i-1) {
+ i++
+ }
+
+ if i == b {
+ return true
+ }
+
+ if b-a < shortestShifting {
+ return false
+ }
+
+ data.Swap(i, i-1)
+
+ // Shift the smaller one to the left.
+ if i-a >= 2 {
+ for j := i - 1; j >= 1; j-- {
+ if !data.Less(j, j-1) {
+ break
+ }
+ data.Swap(j, j-1)
}
- if a >= b {
- break
+ }
+ // Shift the greater one to the right.
+ if b-i >= 2 {
+ for j := i + 1; j < b; j++ {
+ if !data.Less(j, j-1) {
+ break
+ }
+ data.Swap(j, j-1)
}
- // data[a] == pivot; data[b-1] < pivot
- data.Swap(a, b-1)
- a++
- b--
}
}
- // Swap pivot into middle
- data.Swap(pivot, b-1)
- return b - 1, c
+ return false
}
-func quickSort(data Interface, a, b, maxDepth int) {
- for b-a > 12 { // Use ShellSort for slices <= 12 elements
- if maxDepth == 0 {
- heapSort(data, a, b)
- return
- }
- maxDepth--
- mlo, mhi := doPivot(data, a, b)
- // Avoiding recursion on the larger subproblem guarantees
- // a stack depth of at most lg(b-a).
- if mlo-a < b-mhi {
- quickSort(data, a, mlo, maxDepth)
- a = mhi // i.e., quickSort(data, mhi, b)
- } else {
- quickSort(data, mhi, b, maxDepth)
- b = mlo // i.e., quickSort(data, a, mlo)
+// breakPatterns scatters some elements around in an attempt to break some patterns
+// that might cause imbalanced partitions in quicksort.
+func breakPatterns(data Interface, a, b int) {
+ length := b - a
+ if length >= 8 {
+ random := xorshift(length)
+ modulus := nextPowerOfTwo(length)
+
+ for idx := a + (length/4)*2 - 1; idx <= a+(length/4)*2+1; idx++ {
+ other := int(uint(random.Next()) & (modulus - 1))
+ if other >= length {
+ other -= length
+ }
+ data.Swap(idx, a+other)
}
}
- if b-a > 1 {
- // Do ShellSort pass with gap 6
- // It could be written in this simplified form cause b-a <= 12
- for i := a + 6; i < b; i++ {
- if data.Less(i, i-6) {
- data.Swap(i, i-6)
- }
+}
+
+// choosePivot chooses a pivot in data[a:b].
+//
+// [0,8): chooses a static pivot.
+// [8,shortestNinther): uses the simple median-of-three method.
+// [shortestNinther,∞): uses the Tukey ninther method.
+func choosePivot(data Interface, a, b int) (pivot int, hint sortedHint) {
+ const (
+ shortestNinther = 50
+ maxSwaps = 4 * 3
+ )
+
+ l := b - a
+
+ var (
+ swaps int
+ i = a + l/4*1
+ j = a + l/4*2
+ k = a + l/4*3
+ )
+
+ if l >= 8 {
+ if l >= shortestNinther {
+ // Tukey ninther method, the idea came from Rust's implementation.
+ i = medianAdjacent(data, i, &swaps)
+ j = medianAdjacent(data, j, &swaps)
+ k = medianAdjacent(data, k, &swaps)
}
- insertionSort(data, a, b)
+ // Find the median among i, j, k and stores it into j.
+ j = median(data, i, j, k, &swaps)
+ }
+
+ switch swaps {
+ case 0:
+ return j, increasingHint
+ case maxSwaps:
+ return j, decreasingHint
+ default:
+ return j, unknownHint
+ }
+}
+
+// order2 returns x,y where data[x] <= data[y], where x,y=a,b or x,y=b,a.
+func order2(data Interface, a, b int, swaps *int) (int, int) {
+ if data.Less(b, a) {
+ *swaps++
+ return b, a
+ }
+ return a, b
+}
+
+// median returns x where data[x] is the median of data[a],data[b],data[c], where x is a, b, or c.
+func median(data Interface, a, b, c int, swaps *int) int {
+ a, b = order2(data, a, b, swaps)
+ b, c = order2(data, b, c, swaps)
+ a, b = order2(data, a, b, swaps)
+ return b
+}
+
+// medianAdjacent finds the median of data[a - 1], data[a], data[a + 1] and stores the index into a.
+func medianAdjacent(data Interface, a int, swaps *int) int {
+ return median(data, a-1, a, a+1, swaps)
+}
+
+func reverseRange(data Interface, a, b int) {
+ i := a
+ j := b - 1
+ for i < j {
+ data.Swap(i, j)
+ i++
+ j--
+ }
+}
+
+func swapRange(data Interface, a, b, n int) {
+ for i := 0; i < n; i++ {
+ data.Swap(a+i, b+i)
}
}
diff --git a/src/strconv/atof.go b/src/strconv/atof.go
index 57556c70475..60098efed0a 100644
--- a/src/strconv/atof.go
+++ b/src/strconv/atof.go
@@ -420,9 +420,11 @@ var float32pow10 = []float32{1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1
// If possible to convert decimal representation to 64-bit float f exactly,
// entirely in floating-point math, do so, avoiding the expense of decimalToFloatBits.
// Three common cases:
+//
// value is exact integer
// value is exact integer * exact power of ten
// value is exact integer / exact power of ten
+//
// These all produce potentially inexact but correctly rounded answers.
func atof64exact(mantissa uint64, exp int, neg bool) (f float64, ok bool) {
if mantissa>>float64info.mantbits != 0 {
diff --git a/src/strconv/doc.go b/src/strconv/doc.go
index 8db725f96ae..769ecd9a21c 100644
--- a/src/strconv/doc.go
+++ b/src/strconv/doc.go
@@ -5,7 +5,7 @@
// Package strconv implements conversions to and from string representations
// of basic data types.
//
-// Numeric Conversions
+// # Numeric Conversions
//
// The most common numeric conversions are Atoi (string to int) and Itoa (int to string).
//
@@ -40,7 +40,7 @@
// AppendBool, AppendFloat, AppendInt, and AppendUint are similar but
// append the formatted value to a destination slice.
//
-// String Conversions
+// # String Conversions
//
// Quote and QuoteToASCII convert strings to quoted Go string literals.
// The latter guarantees that the result is an ASCII string, by escaping
@@ -53,5 +53,4 @@
// return quoted Go rune literals.
//
// Unquote and UnquoteChar unquote Go string and rune literals.
-//
package strconv
diff --git a/src/strconv/eisel_lemire.go b/src/strconv/eisel_lemire.go
index fecd1b93451..03842e50797 100644
--- a/src/strconv/eisel_lemire.go
+++ b/src/strconv/eisel_lemire.go
@@ -176,8 +176,8 @@ const (
// detailedPowersOfTen contains 128-bit mantissa approximations (rounded down)
// to the powers of 10. For example:
//
-// - 1e43 ≈ (0xE596B7B0_C643C719 * (2 ** 79))
-// - 1e43 = (0xE596B7B0_C643C719_6D9CCD05_D0000000 * (2 ** 15))
+// - 1e43 ≈ (0xE596B7B0_C643C719 * (2 ** 79))
+// - 1e43 = (0xE596B7B0_C643C719_6D9CCD05_D0000000 * (2 ** 15))
//
// The mantissas are explicitly listed. The exponents are implied by a linear
// expression with slope 217706.0/65536.0 ≈ log(10)/log(2).
diff --git a/src/strconv/ftoaryu.go b/src/strconv/ftoaryu.go
index f2e74bed177..b975cdc9b92 100644
--- a/src/strconv/ftoaryu.go
+++ b/src/strconv/ftoaryu.go
@@ -487,8 +487,9 @@ func ryuDigits32(d *decimalSlice, lower, central, upper uint32,
// The returned boolean is true if all trimmed bits were zero.
//
// That is:
-// m*2^e2 * round(10^q) = resM * 2^resE + ε
-// exact = ε == 0
+//
+// m*2^e2 * round(10^q) = resM * 2^resE + ε
+// exact = ε == 0
func mult64bitPow10(m uint32, e2, q int) (resM uint32, resE int, exact bool) {
if q == 0 {
// P == 1<<63
@@ -515,8 +516,9 @@ func mult64bitPow10(m uint32, e2, q int) (resM uint32, resE int, exact bool) {
// The returned boolean is true is all trimmed bits were zero.
//
// That is:
-// m*2^e2 * round(10^q) = resM * 2^resE + ε
-// exact = ε == 0
+//
+// m*2^e2 * round(10^q) = resM * 2^resE + ε
+// exact = ε == 0
func mult128bitPow10(m uint64, e2, q int) (resM uint64, resE int, exact bool) {
if q == 0 {
// P == 1<<127
diff --git a/src/strconv/quote.go b/src/strconv/quote.go
index 6c022846c08..1b5bddfeaea 100644
--- a/src/strconv/quote.go
+++ b/src/strconv/quote.go
@@ -249,10 +249,10 @@ func unhex(b byte) (v rune, ok bool) {
// or character literal represented by the string s.
// It returns four values:
//
-// 1) value, the decoded Unicode code point or byte value;
-// 2) multibyte, a boolean indicating whether the decoded character requires a multibyte UTF-8 representation;
-// 3) tail, the remainder of the string after the character; and
-// 4) an error that will be nil if the character is syntactically valid.
+// 1. value, the decoded Unicode code point or byte value;
+// 2. multibyte, a boolean indicating whether the decoded character requires a multibyte UTF-8 representation;
+// 3. tail, the remainder of the string after the character; and
+// 4. an error that will be nil if the character is syntactically valid.
//
// The second argument, quote, specifies the type of literal being parsed
// and therefore which escaped quote character is permitted.
diff --git a/src/strings/builder.go b/src/strings/builder.go
index ba4df618bfa..3caddabd4ec 100644
--- a/src/strings/builder.go
+++ b/src/strings/builder.go
@@ -22,6 +22,7 @@ type Builder struct {
// noescape is inlined and currently compiles down to zero instructions.
// USE CAREFULLY!
// This was copied from the runtime; see issues 23382 and 7921.
+//
//go:nosplit
//go:nocheckptr
func noescape(p unsafe.Pointer) unsafe.Pointer {
diff --git a/src/strings/replace.go b/src/strings/replace.go
index ee728bb22b6..73bc78a07ec 100644
--- a/src/strings/replace.go
+++ b/src/strings/replace.go
@@ -107,14 +107,14 @@ func (r *Replacer) WriteString(w io.Writer, s string) (n int, err error) {
// and values may be empty. For example, the trie containing keys "ax", "ay",
// "bcbc", "x" and "xy" could have eight nodes:
//
-// n0 -
-// n1 a-
-// n2 .x+
-// n3 .y+
-// n4 b-
-// n5 .cbc+
-// n6 x+
-// n7 .y+
+// n0 -
+// n1 a-
+// n2 .x+
+// n3 .y+
+// n4 b-
+// n5 .cbc+
+// n6 x+
+// n7 .y+
//
// n0 is the root node, and its children are n1, n4 and n6; n1's children are
// n2 and n3; n4's child is n5; n6's child is n7. Nodes n0, n1 and n4 (marked
diff --git a/src/strings/strings.go b/src/strings/strings.go
index 74e505338e1..a563f37cf59 100644
--- a/src/strings/strings.go
+++ b/src/strings/strings.go
@@ -267,9 +267,10 @@ func genSplit(s, sep string, sepSave, n int) []string {
// the substrings between those separators.
//
// The count determines the number of substrings to return:
-// n > 0: at most n substrings; the last substring will be the unsplit remainder.
-// n == 0: the result is nil (zero substrings)
-// n < 0: all substrings
+//
+// n > 0: at most n substrings; the last substring will be the unsplit remainder.
+// n == 0: the result is nil (zero substrings)
+// n < 0: all substrings
//
// Edge cases for s and sep (for example, empty strings) are handled
// as described in the documentation for Split.
@@ -281,9 +282,10 @@ func SplitN(s, sep string, n int) []string { return genSplit(s, sep, 0, n) }
// returns a slice of those substrings.
//
// The count determines the number of substrings to return:
-// n > 0: at most n substrings; the last substring will be the unsplit remainder.
-// n == 0: the result is nil (zero substrings)
-// n < 0: all substrings
+//
+// n > 0: at most n substrings; the last substring will be the unsplit remainder.
+// n == 0: the result is nil (zero substrings)
+// n < 0: all substrings
//
// Edge cases for s and sep (for example, empty strings) are handled
// as described in the documentation for SplitAfter.
@@ -1043,8 +1045,6 @@ func ReplaceAll(s, old, new string) string {
// EqualFold reports whether s and t, interpreted as UTF-8 strings,
// are equal under simple Unicode case-folding, which is a more general
// form of case-insensitivity.
-//
-// EqualFold(s, t) is equivalent to Tolower(s) == Tolower(t).
func EqualFold(s, t string) bool {
for s != "" && t != "" {
// Extract first rune from each string.
diff --git a/src/sync/atomic/atomic_test.go b/src/sync/atomic/atomic_test.go
index 8a53094cb78..09f93a4fe32 100644
--- a/src/sync/atomic/atomic_test.go
+++ b/src/sync/atomic/atomic_test.go
@@ -1155,9 +1155,10 @@ func hammerStoreLoadUintptr(t *testing.T, paddr unsafe.Pointer) {
StoreUintptr(addr, new)
}
-//go:nocheckptr
// This code is just testing that LoadPointer/StorePointer operate
// atomically; it's not actually calculating pointers.
+//
+//go:nocheckptr
func hammerStoreLoadPointer(t *testing.T, paddr unsafe.Pointer) {
addr := (*unsafe.Pointer)(paddr)
v := uintptr(LoadPointer(addr))
diff --git a/src/sync/cond.go b/src/sync/cond.go
index d86ebc8b507..841be96896e 100644
--- a/src/sync/cond.go
+++ b/src/sync/cond.go
@@ -42,12 +42,12 @@ func NewCond(l Locker) *Cond {
// typically cannot assume that the condition is true when
// Wait returns. Instead, the caller should Wait in a loop:
//
-// c.L.Lock()
-// for !condition() {
-// c.Wait()
-// }
-// ... make use of condition ...
-// c.L.Unlock()
+// c.L.Lock()
+// for !condition() {
+// c.Wait()
+// }
+// ... make use of condition ...
+// c.L.Unlock()
func (c *Cond) Wait() {
c.checker.check()
t := runtime_notifyListAdd(&c.notify)
diff --git a/src/sync/once.go b/src/sync/once.go
index e5ba257d875..38373160b9f 100644
--- a/src/sync/once.go
+++ b/src/sync/once.go
@@ -23,7 +23,9 @@ type Once struct {
// Do calls the function f if and only if Do is being called for the
// first time for this instance of Once. In other words, given
-// var once Once
+//
+// var once Once
+//
// if once.Do(f) is called multiple times, only the first call will invoke f,
// even if f has a different value in each invocation. A new instance of
// Once is required for each function to execute.
@@ -31,7 +33,8 @@ type Once struct {
// Do is intended for initialization that must be run exactly once. Since f
// is niladic, it may be necessary to use a function literal to capture the
// arguments to a function to be invoked by Do:
-// config.once.Do(func() { config.init(filename) })
+//
+// config.once.Do(func() { config.init(filename) })
//
// Because no call to Do returns until the one call to f returns, if f causes
// Do to be called, it will deadlock.
diff --git a/src/sync/pool_test.go b/src/sync/pool_test.go
index bb20043a543..5e385974414 100644
--- a/src/sync/pool_test.go
+++ b/src/sync/pool_test.go
@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file.
// Pool is no-op under race detector, so all these tests do not work.
+//
//go:build !race
package sync_test
diff --git a/src/syscall/dir_plan9.go b/src/syscall/dir_plan9.go
index 4ed052de761..1667cbc02f4 100644
--- a/src/syscall/dir_plan9.go
+++ b/src/syscall/dir_plan9.go
@@ -184,6 +184,7 @@ func gbit8(b []byte) (uint8, []byte) {
}
// gbit16 reads a 16-bit number in little-endian order from b and returns it with the remaining slice of b.
+//
//go:nosplit
func gbit16(b []byte) (uint16, []byte) {
return uint16(b[0]) | uint16(b[1])<<8, b[2:]
diff --git a/src/syscall/exec_bsd.go b/src/syscall/exec_bsd.go
index 530b48cb707..4762ae751a3 100644
--- a/src/syscall/exec_bsd.go
+++ b/src/syscall/exec_bsd.go
@@ -49,6 +49,7 @@ func runtime_AfterForkInChild()
// For the same reason compiler does not race instrument it.
// The calls to RawSyscall are okay because they are assembly
// functions that do not grow the stack.
+//
//go:norace
func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) {
// Declare all variables at top in case any
diff --git a/src/syscall/exec_freebsd.go b/src/syscall/exec_freebsd.go
index 90793fe83fa..851b8fbd06a 100644
--- a/src/syscall/exec_freebsd.go
+++ b/src/syscall/exec_freebsd.go
@@ -54,6 +54,7 @@ func runtime_AfterForkInChild()
// For the same reason compiler does not race instrument it.
// The calls to RawSyscall are okay because they are assembly
// functions that do not grow the stack.
+//
//go:norace
func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) {
// Declare all variables at top in case any
diff --git a/src/syscall/exec_libc.go b/src/syscall/exec_libc.go
index c8549c49644..aee1b8c98ab 100644
--- a/src/syscall/exec_libc.go
+++ b/src/syscall/exec_libc.go
@@ -75,6 +75,7 @@ func init() {
// because we need to avoid lazy-loading the functions (might malloc,
// split the stack, or acquire mutexes). We can't call RawSyscall
// because it's not safe even for BSD-subsystem calls.
+//
//go:norace
func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) {
// Declare all variables at top in case any
diff --git a/src/syscall/exec_libc2.go b/src/syscall/exec_libc2.go
index 91a39ba1b8f..9eb61a5d351 100644
--- a/src/syscall/exec_libc2.go
+++ b/src/syscall/exec_libc2.go
@@ -50,6 +50,7 @@ func runtime_AfterForkInChild()
// For the same reason compiler does not race instrument it.
// The calls to rawSyscall are okay because they are assembly
// functions that do not grow the stack.
+//
//go:norace
func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) {
// Declare all variables at top in case any
diff --git a/src/syscall/exec_linux.go b/src/syscall/exec_linux.go
index 0f0dee8ea5a..6d4b6939ada 100644
--- a/src/syscall/exec_linux.go
+++ b/src/syscall/exec_linux.go
@@ -77,6 +77,7 @@ func runtime_AfterForkInChild()
// For the same reason compiler does not race instrument it.
// The calls to RawSyscall are okay because they are assembly
// functions that do not grow the stack.
+//
//go:norace
func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) {
// Set up and fork. This returns immediately in the parent or
diff --git a/src/syscall/exec_plan9.go b/src/syscall/exec_plan9.go
index c469fe18126..6680e6f2ef9 100644
--- a/src/syscall/exec_plan9.go
+++ b/src/syscall/exec_plan9.go
@@ -19,6 +19,7 @@ var ForkLock sync.RWMutex
// gstringb reads a non-empty string from b, prefixed with a 16-bit length in little-endian order.
// It returns the string as a byte slice, or nil if b is too short to contain the length or
// the full string.
+//
//go:nosplit
func gstringb(b []byte) []byte {
if len(b) < 2 {
@@ -37,6 +38,7 @@ const nameOffset = 39
// gdirname returns the first filename from a buffer of directory entries,
// and a slice containing the remaining directory entries.
// If the buffer doesn't start with a valid directory entry, the returned name is nil.
+//
//go:nosplit
func gdirname(buf []byte) (name []byte, rest []byte) {
if len(buf) < 2 {
@@ -119,6 +121,7 @@ var dupdev, _ = BytePtrFromString("#d")
// no rescheduling, no malloc calls, and no new stack segments.
// The calls to RawSyscall are okay because they are assembly
// functions that do not grow the stack.
+//
//go:norace
func forkAndExecInChild(argv0 *byte, argv []*byte, envv []envItem, dir *byte, attr *ProcAttr, pipe int, rflag int) (pid int, err error) {
// Declare all variables at top in case any
@@ -302,6 +305,7 @@ childerror1:
}
// close the numbered file descriptor, unless it is fd1, fd2, or a member of fds.
+//
//go:nosplit
func closeFdExcept(n int, fd1 int, fd2 int, fds []int) {
if n == fd1 || n == fd2 {
diff --git a/src/syscall/exec_windows.go b/src/syscall/exec_windows.go
index 41e58d4355a..92464e089c5 100644
--- a/src/syscall/exec_windows.go
+++ b/src/syscall/exec_windows.go
@@ -19,11 +19,11 @@ var ForkLock sync.RWMutex
// in https://msdn.microsoft.com/en-us/library/ms880421.
// This function returns "" (2 double quotes) if s is empty.
// Alternatively, these transformations are done:
-// - every back slash (\) is doubled, but only if immediately
-// followed by double quote (");
-// - every double quote (") is escaped by back slash (\);
-// - finally, s is wrapped with double quotes (arg -> "arg"),
-// but only if there is space or tab inside s.
+// - every back slash (\) is doubled, but only if immediately
+// followed by double quote (");
+// - every double quote (") is escaped by back slash (\);
+// - finally, s is wrapped with double quotes (arg -> "arg"),
+// but only if there is space or tab inside s.
func EscapeArg(s string) string {
if len(s) == 0 {
return `""`
diff --git a/src/syscall/js/js.go b/src/syscall/js/js.go
index a5210faf7fd..2f4f5adda02 100644
--- a/src/syscall/js/js.go
+++ b/src/syscall/js/js.go
@@ -136,16 +136,16 @@ func Global() Value {
// ValueOf returns x as a JavaScript value:
//
-// | Go | JavaScript |
-// | ---------------------- | ---------------------- |
-// | js.Value | [its value] |
-// | js.Func | function |
-// | nil | null |
-// | bool | boolean |
-// | integers and floats | number |
-// | string | string |
-// | []interface{} | new array |
-// | map[string]interface{} | new object |
+// | Go | JavaScript |
+// | ---------------------- | ---------------------- |
+// | js.Value | [its value] |
+// | js.Func | function |
+// | nil | null |
+// | bool | boolean |
+// | integers and floats | number |
+// | string | string |
+// | []interface{} | new array |
+// | map[string]interface{} | new object |
//
// Panics if x is not one of the expected types.
func ValueOf(x any) Value {
diff --git a/src/syscall/syscall.go b/src/syscall/syscall.go
index 98e30052531..62bfa449cff 100644
--- a/src/syscall/syscall.go
+++ b/src/syscall/syscall.go
@@ -23,7 +23,6 @@
// 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
import "internal/bytealg"
diff --git a/src/syscall/syscall_js.go b/src/syscall/syscall_js.go
index cd954990636..c9c65229804 100644
--- a/src/syscall/syscall_js.go
+++ b/src/syscall/syscall_js.go
@@ -41,6 +41,7 @@ const PathMax = 256
// An Errno is an unsigned number describing an error condition.
// It implements the error interface. The zero Errno is by convention
// a non-error, so code to convert from Errno to error should use:
+//
// err = nil
// if errno != 0 {
// err = errno
diff --git a/src/syscall/syscall_linux.go b/src/syscall/syscall_linux.go
index f74a79c2851..74322caea12 100644
--- a/src/syscall/syscall_linux.go
+++ b/src/syscall/syscall_linux.go
@@ -109,7 +109,7 @@ func Faccessat(dirfd int, path string, mode uint32, flags int) (err error) {
gid = Getgid()
}
- if uint32(gid) == st.Gid || isGroupMember(gid) {
+ if uint32(gid) == st.Gid || isGroupMember(int(st.Gid)) {
fmode = (st.Mode >> 3) & 7
} else {
fmode = st.Mode & 7
@@ -968,6 +968,7 @@ func Getpgrp() (pid int) {
// Provided by runtime.syscall_runtime_doAllThreadsSyscall which stops the
// world and invokes the syscall on each OS thread. Once this function returns,
// all threads are in sync.
+//
//go:uintptrescapes
func runtime_doAllThreadsSyscall(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)
@@ -986,6 +987,7 @@ func runtime_doAllThreadsSyscall(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2,
// AllThreadsSyscall is unaware of any threads that are launched
// explicitly by cgo linked code, so the function always returns
// ENOTSUP in binaries that use cgo.
+//
//go:uintptrescapes
func AllThreadsSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) {
if cgo_libc_setegid != nil {
@@ -997,6 +999,7 @@ func AllThreadsSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) {
// AllThreadsSyscall6 is like AllThreadsSyscall, but extended to six
// arguments.
+//
//go:uintptrescapes
func AllThreadsSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno) {
if cgo_libc_setegid != nil {
@@ -1007,6 +1010,7 @@ func AllThreadsSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, e
}
// linked by runtime.cgocall.go
+//
//go:uintptrescapes
func cgocaller(unsafe.Pointer, ...uintptr) uintptr
diff --git a/src/syscall/syscall_unix.go b/src/syscall/syscall_unix.go
index 56d21b4ec13..e12f024fe75 100644
--- a/src/syscall/syscall_unix.go
+++ b/src/syscall/syscall_unix.go
@@ -101,6 +101,7 @@ func (m *mmapper) Munmap(data []byte) (err error) {
// An Errno is an unsigned number describing an error condition.
// It implements the error interface. The zero Errno is by convention
// a non-error, so code to convert from Errno to error should use:
+//
// err = nil
// if errno != 0 {
// err = errno
diff --git a/src/testing/fuzz.go b/src/testing/fuzz.go
index b5e1339debd..b9f3a3d1598 100644
--- a/src/testing/fuzz.go
+++ b/src/testing/fuzz.go
@@ -189,7 +189,7 @@ var supportedTypes = map[reflect.Type]bool{
// whose remaining arguments are the types to be fuzzed.
// For example:
//
-// f.Fuzz(func(t *testing.T, b []byte, i int) { ... })
+// f.Fuzz(func(t *testing.T, b []byte, i int) { ... })
//
// The following types are allowed: []byte, string, bool, byte, rune, float32,
// float64, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64.
diff --git a/src/testing/quick/quick.go b/src/testing/quick/quick.go
index e73d307c13a..95a635badec 100644
--- a/src/testing/quick/quick.go
+++ b/src/testing/quick/quick.go
@@ -251,15 +251,15 @@ func (s *CheckEqualError) Error() string {
// Check returns that input as a *CheckError.
// For example:
//
-// func TestOddMultipleOfThree(t *testing.T) {
-// f := func(x int) bool {
-// y := OddMultipleOfThree(x)
-// return y%2 == 1 && y%3 == 0
-// }
-// if err := quick.Check(f, nil); err != nil {
-// t.Error(err)
-// }
-// }
+// func TestOddMultipleOfThree(t *testing.T) {
+// f := func(x int) bool {
+// y := OddMultipleOfThree(x)
+// return y%2 == 1 && y%3 == 0
+// }
+// if err := quick.Check(f, nil); err != nil {
+// t.Error(err)
+// }
+// }
func Check(f any, config *Config) error {
if config == nil {
config = &defaultConfig
diff --git a/src/testing/testing.go b/src/testing/testing.go
index 05d8f22affd..1f701e0b217 100644
--- a/src/testing/testing.go
+++ b/src/testing/testing.go
@@ -5,7 +5,9 @@
// Package testing provides support for automated testing of Go packages.
// It is intended to be used in concert with the "go test" command, which automates
// execution of any function of the form
-// func TestXxx(*testing.T)
+//
+// func TestXxx(*testing.T)
+//
// where Xxx does not start with a lowercase letter. The function name
// serves to identify the test routine.
//
@@ -19,17 +21,19 @@
//
// A simple test function looks like this:
//
-// func TestAbs(t *testing.T) {
-// got := Abs(-1)
-// if got != 1 {
-// t.Errorf("Abs(-1) = %d; want 1", got)
-// }
-// }
+// func TestAbs(t *testing.T) {
+// got := Abs(-1)
+// if got != 1 {
+// t.Errorf("Abs(-1) = %d; want 1", got)
+// }
+// }
//
-// Benchmarks
+// # Benchmarks
//
// Functions of the form
-// func BenchmarkXxx(*testing.B)
+//
+// func BenchmarkXxx(*testing.B)
+//
// are considered benchmarks, and are executed by the "go test" command when
// its -bench flag is provided. Benchmarks are run sequentially.
//
@@ -37,43 +41,46 @@
// https://golang.org/cmd/go/#hdr-Testing_flags.
//
// A sample benchmark function looks like this:
-// func BenchmarkRandInt(b *testing.B) {
-// for i := 0; i < b.N; i++ {
-// rand.Int()
-// }
-// }
+//
+// func BenchmarkRandInt(b *testing.B) {
+// for i := 0; i < b.N; i++ {
+// rand.Int()
+// }
+// }
//
// The benchmark function must run the target code b.N times.
// During benchmark execution, b.N is adjusted until the benchmark function lasts
// long enough to be timed reliably. The output
-// BenchmarkRandInt-8 68453040 17.8 ns/op
+//
+// BenchmarkRandInt-8 68453040 17.8 ns/op
+//
// means that the loop ran 68453040 times at a speed of 17.8 ns per loop.
//
// If a benchmark needs some expensive setup before running, the timer
// may be reset:
//
-// func BenchmarkBigLen(b *testing.B) {
-// big := NewBig()
-// b.ResetTimer()
-// for i := 0; i < b.N; i++ {
-// big.Len()
-// }
-// }
+// func BenchmarkBigLen(b *testing.B) {
+// big := NewBig()
+// b.ResetTimer()
+// for i := 0; i < b.N; i++ {
+// big.Len()
+// }
+// }
//
// If a benchmark needs to test performance in a parallel setting, it may use
// the RunParallel helper function; such benchmarks are intended to be used with
// the go test -cpu flag:
//
-// func BenchmarkTemplateParallel(b *testing.B) {
-// templ := template.Must(template.New("test").Parse("Hello, {{.}}!"))
-// b.RunParallel(func(pb *testing.PB) {
-// var buf bytes.Buffer
-// for pb.Next() {
-// buf.Reset()
-// templ.Execute(&buf, "World")
-// }
-// })
-// }
+// func BenchmarkTemplateParallel(b *testing.B) {
+// templ := template.Must(template.New("test").Parse("Hello, {{.}}!"))
+// b.RunParallel(func(pb *testing.PB) {
+// var buf bytes.Buffer
+// for pb.Next() {
+// buf.Reset()
+// templ.Execute(&buf, "World")
+// }
+// })
+// }
//
// A detailed specification of the benchmark results format is given
// in https://golang.org/design/14313-benchmark-format.
@@ -83,90 +90,92 @@
// In particular, https://golang.org/x/perf/cmd/benchstat performs
// statistically robust A/B comparisons.
//
-// Examples
+// # Examples
//
// The package also runs and verifies example code. Example functions may
// include a concluding line comment that begins with "Output:" and is compared with
// the standard output of the function when the tests are run. (The comparison
// ignores leading and trailing space.) These are examples of an example:
//
-// func ExampleHello() {
-// fmt.Println("hello")
-// // Output: hello
-// }
+// func ExampleHello() {
+// fmt.Println("hello")
+// // Output: hello
+// }
//
-// func ExampleSalutations() {
-// fmt.Println("hello, and")
-// fmt.Println("goodbye")
-// // Output:
-// // hello, and
-// // goodbye
-// }
+// func ExampleSalutations() {
+// fmt.Println("hello, and")
+// fmt.Println("goodbye")
+// // Output:
+// // hello, and
+// // goodbye
+// }
//
// The comment prefix "Unordered output:" is like "Output:", but matches any
// line order:
//
-// func ExamplePerm() {
-// for _, value := range Perm(5) {
-// fmt.Println(value)
-// }
-// // Unordered output: 4
-// // 2
-// // 1
-// // 3
-// // 0
-// }
+// func ExamplePerm() {
+// for _, value := range Perm(5) {
+// fmt.Println(value)
+// }
+// // Unordered output: 4
+// // 2
+// // 1
+// // 3
+// // 0
+// }
//
// Example functions without output comments are compiled but not executed.
//
// The naming convention to declare examples for the package, a function F, a type T and
// method M on type T are:
//
-// func Example() { ... }
-// func ExampleF() { ... }
-// func ExampleT() { ... }
-// func ExampleT_M() { ... }
+// func Example() { ... }
+// func ExampleF() { ... }
+// func ExampleT() { ... }
+// func ExampleT_M() { ... }
//
// Multiple example functions for a package/type/function/method may be provided by
// appending a distinct suffix to the name. The suffix must start with a
// lower-case letter.
//
-// func Example_suffix() { ... }
-// func ExampleF_suffix() { ... }
-// func ExampleT_suffix() { ... }
-// func ExampleT_M_suffix() { ... }
+// func Example_suffix() { ... }
+// func ExampleF_suffix() { ... }
+// func ExampleT_suffix() { ... }
+// func ExampleT_M_suffix() { ... }
//
// The entire test file is presented as the example when it contains a single
// example function, at least one other function, type, variable, or constant
// declaration, and no test or benchmark functions.
//
-// Fuzzing
+// # Fuzzing
//
// 'go test' and the testing package support fuzzing, a testing technique where
// a function is called with randomly generated inputs to find bugs not
// anticipated by unit tests.
//
// Functions of the form
-// func FuzzXxx(*testing.F)
+//
+// func FuzzXxx(*testing.F)
+//
// are considered fuzz tests.
//
// For example:
//
-// func FuzzHex(f *testing.F) {
-// for _, seed := range [][]byte{{}, {0}, {9}, {0xa}, {0xf}, {1, 2, 3, 4}} {
-// f.Add(seed)
-// }
-// f.Fuzz(func(t *testing.T, in []byte) {
-// enc := hex.EncodeToString(in)
-// out, err := hex.DecodeString(enc)
-// if err != nil {
-// t.Fatalf("%v: decode: %v", in, err)
-// }
-// if !bytes.Equal(in, out) {
-// t.Fatalf("%v: not equal after round trip: %v", in, out)
-// }
-// })
-// }
+// func FuzzHex(f *testing.F) {
+// for _, seed := range [][]byte{{}, {0}, {9}, {0xa}, {0xf}, {1, 2, 3, 4}} {
+// f.Add(seed)
+// }
+// f.Fuzz(func(t *testing.T, in []byte) {
+// enc := hex.EncodeToString(in)
+// out, err := hex.DecodeString(enc)
+// if err != nil {
+// t.Fatalf("%v: decode: %v", in, err)
+// }
+// if !bytes.Equal(in, out) {
+// t.Fatalf("%v: not equal after round trip: %v", in, out)
+// }
+// })
+// }
//
// A fuzz test maintains a seed corpus, or a set of inputs which are run by
// default, and can seed input generation. Seed inputs may be registered by
@@ -204,47 +213,47 @@
//
// See https://go.dev/doc/fuzz for documentation about fuzzing.
//
-// Skipping
+// # Skipping
//
// Tests or benchmarks may be skipped at run time with a call to
// the Skip method of *T or *B:
//
-// func TestTimeConsuming(t *testing.T) {
-// if testing.Short() {
-// t.Skip("skipping test in short mode.")
-// }
-// ...
-// }
+// func TestTimeConsuming(t *testing.T) {
+// if testing.Short() {
+// t.Skip("skipping test in short mode.")
+// }
+// ...
+// }
//
// The Skip method of *T can be used in a fuzz target if the input is invalid,
// but should not be considered a failing input. For example:
//
-// func FuzzJSONMarshalling(f *testing.F) {
-// f.Fuzz(func(t *testing.T, b []byte) {
-// var v interface{}
-// if err := json.Unmarshal(b, &v); err != nil {
-// t.Skip()
-// }
-// if _, err := json.Marshal(v); err != nil {
-// t.Error("Marshal: %v", err)
-// }
-// })
-// }
+// func FuzzJSONMarshaling(f *testing.F) {
+// f.Fuzz(func(t *testing.T, b []byte) {
+// var v interface{}
+// if err := json.Unmarshal(b, &v); err != nil {
+// t.Skip()
+// }
+// if _, err := json.Marshal(v); err != nil {
+// t.Error("Marshal: %v", err)
+// }
+// })
+// }
//
-// Subtests and Sub-benchmarks
+// # Subtests and Sub-benchmarks
//
// The Run methods of T and B allow defining subtests and sub-benchmarks,
// without having to define separate functions for each. This enables uses
// like table-driven benchmarks and creating hierarchical tests.
// It also provides a way to share common setup and tear-down code:
//
-// func TestFoo(t *testing.T) {
-// // <setup code>
-// t.Run("A=1", func(t *testing.T) { ... })
-// t.Run("A=2", func(t *testing.T) { ... })
-// t.Run("B=1", func(t *testing.T) { ... })
-// // <tear-down code>
-// }
+// func TestFoo(t *testing.T) {
+// // <setup code>
+// t.Run("A=1", func(t *testing.T) { ... })
+// t.Run("A=2", func(t *testing.T) { ... })
+// t.Run("B=1", func(t *testing.T) { ... })
+// // <tear-down code>
+// }
//
// Each subtest and sub-benchmark has a unique name: the combination of the name
// of the top-level test and the sequence of names passed to Run, separated by
@@ -257,15 +266,16 @@
// empty expression matches any string.
// For example, using "matching" to mean "whose name contains":
//
-// go test -run '' # Run all tests.
-// go test -run Foo # Run top-level tests matching "Foo", such as "TestFooBar".
-// go test -run Foo/A= # For top-level tests matching "Foo", run subtests matching "A=".
-// go test -run /A=1 # For all top-level tests, run subtests matching "A=1".
-// go test -fuzz FuzzFoo # Fuzz the target matching "FuzzFoo"
+// go test -run '' # Run all tests.
+// go test -run Foo # Run top-level tests matching "Foo", such as "TestFooBar".
+// go test -run Foo/A= # For top-level tests matching "Foo", run subtests matching "A=".
+// go test -run /A=1 # For all top-level tests, run subtests matching "A=1".
+// go test -fuzz FuzzFoo # Fuzz the target matching "FuzzFoo"
//
// The -run argument can also be used to run a specific value in the seed
// corpus, for debugging. For example:
-// go test -run=FuzzFoo/9ddb952d9814
+//
+// go test -run=FuzzFoo/9ddb952d9814
//
// The -fuzz and -run flags can both be set, in order to fuzz a target but
// skip the execution of all other tests.
@@ -275,15 +285,15 @@
// run in parallel with each other, and only with each other, regardless of
// other top-level tests that may be defined:
//
-// func TestGroupedParallel(t *testing.T) {
-// for _, tc := range tests {
-// tc := tc // capture range variable
-// t.Run(tc.Name, func(t *testing.T) {
-// t.Parallel()
-// ...
-// })
-// }
-// }
+// func TestGroupedParallel(t *testing.T) {
+// for _, tc := range tests {
+// tc := tc // capture range variable
+// t.Run(tc.Name, func(t *testing.T) {
+// t.Parallel()
+// ...
+// })
+// }
+// }
//
// The race detector kills the program if it exceeds 8128 concurrent goroutines,
// so use care when running parallel tests with the -race flag set.
@@ -291,17 +301,17 @@
// Run does not return until parallel subtests have completed, providing a way
// to clean up after a group of parallel tests:
//
-// func TestTeardownParallel(t *testing.T) {
-// // This Run will not return until the parallel tests finish.
-// t.Run("group", func(t *testing.T) {
-// t.Run("Test1", parallelTest1)
-// t.Run("Test2", parallelTest2)
-// t.Run("Test3", parallelTest3)
-// })
-// // <tear-down code>
-// }
-//
-// Main
+// func TestTeardownParallel(t *testing.T) {
+// // This Run will not return until the parallel tests finish.
+// t.Run("group", func(t *testing.T) {
+// t.Run("Test1", parallelTest1)
+// t.Run("Test2", parallelTest2)
+// t.Run("Test3", parallelTest3)
+// })
+// // <tear-down code>
+// }
+//
+// # Main
//
// It is sometimes necessary for a test or benchmark program to do extra setup or teardown
// before or after it executes. It is also sometimes necessary to control
diff --git a/src/text/template/funcs.go b/src/text/template/funcs.go
index 1f63b361f88..390d47ebbb6 100644
--- a/src/text/template/funcs.go
+++ b/src/text/template/funcs.go
@@ -751,7 +751,9 @@ func URLQueryEscaper(args ...any) string {
}
// evalArgs formats the list of arguments into a string. It is therefore equivalent to
+//
// fmt.Sprint(args...)
+//
// except that each argument is indirected (if a pointer), as required,
// using the same rules as the default string evaluation during template
// execution.
diff --git a/src/text/template/helper.go b/src/text/template/helper.go
index 57905e613a4..48af3928b39 100644
--- a/src/text/template/helper.go
+++ b/src/text/template/helper.go
@@ -19,6 +19,7 @@ import (
// Must is a helper that wraps a call to a function returning (*Template, error)
// and panics if the error is non-nil. It is intended for use in variable
// initializations such as
+//
// var t = template.Must(template.New("name").Parse("text"))
func Must(t *Template, err error) *Template {
if err != nil {
diff --git a/src/text/template/option.go b/src/text/template/option.go
index 8d7d436bd07..ea2fd80c069 100644
--- a/src/text/template/option.go
+++ b/src/text/template/option.go
@@ -30,6 +30,7 @@ type option struct {
//
// missingkey: Control the behavior during execution if a map is
// indexed with a key that is not present in the map.
+//
// "missingkey=default" or "missingkey=invalid"
// The default behavior: Do nothing and continue execution.
// If printed, the result of the index operation is the string
diff --git a/src/text/template/parse/lex.go b/src/text/template/parse/lex.go
index 40d0411121b..078f714ccf5 100644
--- a/src/text/template/parse/lex.go
+++ b/src/text/template/parse/lex.go
@@ -541,13 +541,25 @@ func (l *lexer) atTerminator() bool {
case eof, '.', ',', '|', ':', ')', '(':
return true
}
- // Does r start the delimiter? This can be ambiguous (with delim=="//", $x/2 will
- // succeed but should fail) but only in extremely rare cases caused by willfully
- // bad choice of delimiter.
- if rd, _ := utf8.DecodeRuneInString(l.rightDelim); rd == r {
- return true
+ // Are we at a right delimiter? TODO: This is harder than it should be
+ // because lookahead is only one rune.
+ rightDelim := l.rightDelim
+ defer func(pos Pos, line int) {
+ l.pos = pos
+ l.line = line
+ }(l.pos, l.line)
+ for len(rightDelim) > 0 {
+ rNext := l.next()
+ if rNext == eof {
+ return false
+ }
+ rDelim, size := utf8.DecodeRuneInString(rightDelim)
+ if rNext != rDelim {
+ return false
+ }
+ rightDelim = rightDelim[size:]
}
- return false
+ return true
}
// lexChar scans a character constant. The initial quote is already
diff --git a/src/text/template/parse/lex_test.go b/src/text/template/parse/lex_test.go
index df6aabffb20..fcb7e8eacdd 100644
--- a/src/text/template/parse/lex_test.go
+++ b/src/text/template/parse/lex_test.go
@@ -469,6 +469,22 @@ func TestDelims(t *testing.T) {
}
}
+func TestDelimsAlphaNumeric(t *testing.T) {
+ test := lexTest{"right delimiter with alphanumeric start", "{{hub .host hub}}", []item{
+ mkItem(itemLeftDelim, "{{hub"),
+ mkItem(itemSpace, " "),
+ mkItem(itemField, ".host"),
+ mkItem(itemSpace, " "),
+ mkItem(itemRightDelim, "hub}}"),
+ tEOF,
+ }}
+ items := collect(&test, "{{hub", "hub}}")
+
+ if !equal(items, test.items, false) {
+ t.Errorf("%s: got\n\t%v\nexpected\n\t%v", test.name, items, test.items)
+ }
+}
+
var lexPosTests = []lexTest{
{"empty", "", []item{{itemEOF, 0, "", 1}}},
{"punctuation", "{{,@%#}}", []item{
diff --git a/src/text/template/parse/parse.go b/src/text/template/parse/parse.go
index ce548b08865..67e2f5b2f4a 100644
--- a/src/text/template/parse/parse.go
+++ b/src/text/template/parse/parse.go
@@ -341,7 +341,9 @@ func (t *Tree) parseDefinition() {
}
// itemList:
+//
// textOrAction*
+//
// Terminates at {{end}} or {{else}}, returned separately.
func (t *Tree) itemList() (list *ListNode, next Node) {
list = t.newList(t.peekNonSpace().pos)
@@ -358,6 +360,7 @@ func (t *Tree) itemList() (list *ListNode, next Node) {
}
// textOrAction:
+//
// text | comment | action
func (t *Tree) textOrAction() Node {
switch token := t.nextNonSpace(); token.typ {
@@ -380,8 +383,10 @@ func (t *Tree) clearActionLine() {
}
// Action:
+//
// control
// command ("|" command)*
+//
// Left delim is past. Now get actions.
// First word could be a keyword such as range.
func (t *Tree) action() (n Node) {
@@ -412,7 +417,9 @@ func (t *Tree) action() (n Node) {
}
// Break:
+//
// {{break}}
+//
// Break keyword is past.
func (t *Tree) breakControl(pos Pos, line int) Node {
if token := t.nextNonSpace(); token.typ != itemRightDelim {
@@ -425,7 +432,9 @@ func (t *Tree) breakControl(pos Pos, line int) Node {
}
// Continue:
+//
// {{continue}}
+//
// Continue keyword is past.
func (t *Tree) continueControl(pos Pos, line int) Node {
if token := t.nextNonSpace(); token.typ != itemRightDelim {
@@ -438,6 +447,7 @@ func (t *Tree) continueControl(pos Pos, line int) Node {
}
// Pipeline:
+//
// declarations? command ('|' command)*
func (t *Tree) pipeline(context string, end itemType) (pipe *PipeNode) {
token := t.peekNonSpace()
@@ -549,16 +559,20 @@ func (t *Tree) parseControl(allowElseIf bool, context string) (pos Pos, line int
}
// If:
+//
// {{if pipeline}} itemList {{end}}
// {{if pipeline}} itemList {{else}} itemList {{end}}
+//
// If keyword is past.
func (t *Tree) ifControl() Node {
return t.newIf(t.parseControl(true, "if"))
}
// Range:
+//
// {{range pipeline}} itemList {{end}}
// {{range pipeline}} itemList {{else}} itemList {{end}}
+//
// Range keyword is past.
func (t *Tree) rangeControl() Node {
r := t.newRange(t.parseControl(false, "range"))
@@ -566,22 +580,28 @@ func (t *Tree) rangeControl() Node {
}
// With:
+//
// {{with pipeline}} itemList {{end}}
// {{with pipeline}} itemList {{else}} itemList {{end}}
+//
// If keyword is past.
func (t *Tree) withControl() Node {
return t.newWith(t.parseControl(false, "with"))
}
// End:
+//
// {{end}}
+//
// End keyword is past.
func (t *Tree) endControl() Node {
return t.newEnd(t.expect(itemRightDelim, "end").pos)
}
// Else:
+//
// {{else}}
+//
// Else keyword is past.
func (t *Tree) elseControl() Node {
// Special case for "else if".
@@ -595,7 +615,9 @@ func (t *Tree) elseControl() Node {
}
// Block:
+//
// {{block stringValue pipeline}}
+//
// Block keyword is past.
// The name must be something that can evaluate to a string.
// The pipeline is mandatory.
@@ -623,7 +645,9 @@ func (t *Tree) blockControl() Node {
}
// Template:
+//
// {{template stringValue pipeline}}
+//
// Template keyword is past. The name must be something that can evaluate
// to a string.
func (t *Tree) templateControl() Node {
@@ -654,7 +678,9 @@ func (t *Tree) parseTemplateName(token item, context string) (name string) {
}
// command:
+//
// operand (space operand)*
+//
// 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 {
@@ -684,7 +710,9 @@ func (t *Tree) command() *CommandNode {
}
// operand:
+//
// term .Field*
+//
// An operand is a space-separated component of a command,
// a term possibly followed by field accesses.
// A nil return means the next item is not an operand.
@@ -718,12 +746,14 @@ func (t *Tree) operand() Node {
}
// term:
+//
// literal (number, string, nil, boolean)
// function (identifier)
// .
// .Field
// $
// '(' pipeline ')'
+//
// A term is a simple "expression".
// A nil return means the next item is not a term.
func (t *Tree) term() Node {
diff --git a/src/time/format.go b/src/time/format.go
index 95fe08b7724..2f66df668b7 100644
--- a/src/time/format.go
+++ b/src/time/format.go
@@ -8,12 +8,16 @@ import "errors"
// These are predefined layouts for use in Time.Format and time.Parse.
// The reference time used in these layouts is the specific time stamp:
+//
// 01/02 03:04:05PM '06 -0700
+//
// (January 2, 15:04:05, 2006, in time zone seven hours west of GMT).
// That value is recorded as the constant named Layout, listed below. As a Unix
// time, this is 1136239445. Since MST is GMT-0700, the reference would be
// printed by the Unix date command as:
+//
// Mon Jan 2 15:04:05 MST 2006
+//
// It is a regrettable historic error that the date uses the American convention
// of putting the numerical month before the day.
//
@@ -59,12 +63,15 @@ import "errors"
// AM/PM mark: "PM"
//
// Numeric time zone offsets format as follows:
+//
// "-0700" ±hhmm
// "-07:00" ±hh:mm
// "-07" ±hh
+//
// Replacing the sign in the format with a Z triggers
// the ISO 8601 behavior of printing Z instead of an
// offset for the UTC zone. Thus:
+//
// "Z0700" Z or ±hhmm
// "Z07:00" Z or ±hh:mm
// "Z07" Z or ±hh
@@ -484,6 +491,7 @@ func formatNano(b []byte, nanosec uint, std int) []byte {
}
// String returns the time formatted using the format string
+//
// "2006-01-02 15:04:05.999999999 -0700 MST"
//
// If the time has a monotonic clock reading, the returned string
diff --git a/src/time/sleep.go b/src/time/sleep.go
index 1ffaabec674..cdab4782ada 100644
--- a/src/time/sleep.go
+++ b/src/time/sleep.go
@@ -62,9 +62,9 @@ type Timer struct {
// return value and drain the channel.
// For example, assuming the program has not received from t.C already:
//
-// if !t.Stop() {
-// <-t.C
-// }
+// if !t.Stop() {
+// <-t.C
+// }
//
// This cannot be done concurrent to other receives from the Timer's
// channel or other calls to the Timer's Stop method.
@@ -110,10 +110,10 @@ func NewTimer(d Duration) *Timer {
// the timer must be stopped and—if Stop reports that the timer expired
// before being stopped—the channel explicitly drained:
//
-// if !t.Stop() {
-// <-t.C
-// }
-// t.Reset(d)
+// if !t.Stop() {
+// <-t.C
+// }
+// t.Reset(d)
//
// This should not be done concurrent to other receives from the Timer's
// channel.
diff --git a/src/time/tick.go b/src/time/tick.go
index babf865aeb0..dcfeca8783c 100644
--- a/src/time/tick.go
+++ b/src/time/tick.go
@@ -6,7 +6,7 @@ package time
import "errors"
-// A Ticker holds a channel that delivers ``ticks'' of a clock
+// A Ticker holds a channel that delivers “ticks” of a clock
// at intervals.
type Ticker struct {
C <-chan Time // The channel on which the ticks are delivered.
diff --git a/src/time/time.go b/src/time/time.go
index 88301ec16bb..95963b6bf31 100644
--- a/src/time/time.go
+++ b/src/time/time.go
@@ -7,7 +7,7 @@
// The calendrical calculations always assume a Gregorian calendar, with
// no leap seconds.
//
-// Monotonic Clocks
+// # Monotonic Clocks
//
// Operating systems provide both a “wall clock,” which is subject to
// changes for clock synchronization, and a “monotonic clock,” which is
@@ -72,7 +72,6 @@
// For debugging, the result of t.String does include the monotonic
// clock reading if present. If t != u because of different monotonic clock readings,
// that difference will be visible when printing t.String() and u.String().
-//
package time
import (
@@ -596,10 +595,12 @@ const (
// to avoid confusion across daylight savings time zone transitions.
//
// To count the number of units in a Duration, divide:
+//
// second := time.Second
// fmt.Print(int64(second/time.Millisecond)) // prints 1000
//
// To convert an integer number of units to a Duration, multiply:
+//
// seconds := 10
// fmt.Print(time.Duration(seconds)*time.Second) // prints 10s
const (
@@ -1068,6 +1069,7 @@ func daysSinceEpoch(year int) uint64 {
func now() (sec int64, nsec int32, mono int64)
// runtimeNano returns the current value of the runtime clock in nanoseconds.
+//
//go:linkname runtimeNano runtime.nanotime
func runtimeNano() int64
@@ -1378,6 +1380,7 @@ func isLeap(year int) bool {
}
// norm returns nhi, nlo such that
+//
// hi * base + lo == nhi * base + nlo
// 0 <= nlo < base
func norm(hi, lo, base int) (nhi, nlo int) {
@@ -1395,7 +1398,9 @@ func norm(hi, lo, base int) (nhi, nlo int) {
}
// Date returns the Time corresponding to
+//
// yyyy-mm-dd hh:mm:ss + nsec nanoseconds
+//
// in the appropriate zone for that time in the given location.
//
// The month, day, hour, min, sec, and nsec values may be outside
diff --git a/src/time/tzdata/tzdata.go b/src/time/tzdata/tzdata.go
index 25725bd84d2..324de5cd85c 100644
--- a/src/time/tzdata/tzdata.go
+++ b/src/time/tzdata/tzdata.go
@@ -29,6 +29,7 @@ import (
)
// registerLoadFromEmbeddedTZData is defined in package time.
+//
//go:linkname registerLoadFromEmbeddedTZData time.registerLoadFromEmbeddedTZData
func registerLoadFromEmbeddedTZData(func(string) (string, error))
diff --git a/src/time/zoneinfo.go b/src/time/zoneinfo.go
index 9bcb183d77b..b3313583d89 100644
--- a/src/time/zoneinfo.go
+++ b/src/time/zoneinfo.go
@@ -197,14 +197,14 @@ func (l *Location) lookup(sec int64) (name string, offset int, start, end int64,
// The reference implementation in localtime.c from
// https://www.iana.org/time-zones/repository/releases/tzcode2013g.tar.gz
// implements the following algorithm for these cases:
-// 1) If the first zone is unused by the transitions, use it.
-// 2) Otherwise, if there are transition times, and the first
+// 1. If the first zone is unused by the transitions, use it.
+// 2. Otherwise, if there are transition times, and the first
// transition is to a zone in daylight time, find the first
// non-daylight-time zone before and closest to the first transition
// zone.
-// 3) Otherwise, use the first zone that is not daylight time, if
+// 3. Otherwise, use the first zone that is not daylight time, if
// there is one.
-// 4) Otherwise, use the first zone.
+// 4. Otherwise, use the first zone.
func (l *Location) lookupFirstZone() int {
// Case 1.
if !l.firstZoneUsed() {
diff --git a/src/unicode/graphic.go b/src/unicode/graphic.go
index ca6241949a2..2af29778bf3 100644
--- a/src/unicode/graphic.go
+++ b/src/unicode/graphic.go
@@ -120,7 +120,9 @@ func IsPunct(r rune) bool {
// IsSpace reports whether the rune is a space character as defined
// by Unicode's White Space property; in the Latin-1 space
// this is
+//
// '\t', '\n', '\v', '\f', '\r', ' ', U+0085 (NEL), U+00A0 (NBSP).
+//
// Other definitions of spacing characters are set by category
// Z and property Pattern_White_Space.
func IsSpace(r rune) bool {
diff --git a/src/unicode/letter.go b/src/unicode/letter.go
index f4c950a883c..f3f8e529648 100644
--- a/src/unicode/letter.go
+++ b/src/unicode/letter.go
@@ -49,7 +49,9 @@ type Range32 struct {
// means the character is in the corresponding case. There is a special
// case representing sequences of alternating corresponding Upper and Lower
// pairs. It appears with a fixed Delta of
+//
// {UpperLower, UpperLower, UpperLower}
+//
// The constant UpperLower has an otherwise impossible delta value.
type CaseRange struct {
Lo uint32
@@ -324,6 +326,7 @@ type foldPair struct {
// If r is not a valid Unicode code point, SimpleFold(r) returns r.
//
// For example:
+//
// SimpleFold('A') = 'a'
// SimpleFold('a') = 'A'
//
diff --git a/src/unsafe/unsafe.go b/src/unsafe/unsafe.go
index ae69dea4af7..da15902b293 100644
--- a/src/unsafe/unsafe.go
+++ b/src/unsafe/unsafe.go
@@ -20,10 +20,11 @@ type IntegerType int
// Pointer represents a pointer to an arbitrary type. There are four special operations
// available for type Pointer that are not available for other types:
-// - A pointer value of any type can be converted to a Pointer.
-// - A Pointer can be converted to a pointer value of any type.
-// - A uintptr can be converted to a Pointer.
-// - A Pointer can be converted to a uintptr.
+// - A pointer value of any type can be converted to a Pointer.
+// - A Pointer can be converted to a pointer value of any type.
+// - A uintptr can be converted to a Pointer.
+// - A Pointer can be converted to a uintptr.
+//
// Pointer therefore allows a program to defeat the type system and read and write
// arbitrary memory. It should be used with extreme care.
//
diff --git a/test/codegen/bmi.go b/test/codegen/bmi.go
index 2908d1b796f..3b125a1b590 100644
--- a/test/codegen/bmi.go
+++ b/test/codegen/bmi.go
@@ -46,7 +46,49 @@ func blsr32(x int32) int32 {
return x & (x - 1)
}
-func shlrx64(x []uint64, i int, s uint64) uint64 {
+func sarx64(x, y int64) int64 {
+ // amd64/v3:"SARXQ"
+ return x >> y
+}
+
+func sarx32(x, y int32) int32 {
+ // amd64/v3:"SARXL"
+ return x >> y
+}
+
+func sarx64_load(x []int64, i int) int64 {
+ // amd64/v3: `SARXQ\t[A-Z]+[0-9]*, \([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), [A-Z]+[0-9]*`
+ s := x[i] >> (i & 63)
+ // amd64/v3: `SARXQ\t[A-Z]+[0-9]*, 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), [A-Z]+[0-9]*`
+ s = x[i+1] >> (s & 63)
+ return s
+}
+
+func sarx32_load(x []int32, i int) int32 {
+ // amd64/v3: `SARXL\t[A-Z]+[0-9]*, \([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), [A-Z]+[0-9]*`
+ s := x[i] >> (i & 63)
+ // amd64/v3: `SARXL\t[A-Z]+[0-9]*, 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), [A-Z]+[0-9]*`
+ s = x[i+1] >> (s & 63)
+ return s
+}
+
+func shlrx64(x, y uint64) uint64 {
+ // amd64/v3:"SHRXQ"
+ s := x >> y
+ // amd64/v3:"SHLXQ"
+ s = s << y
+ return s
+}
+
+func shlrx32(x, y uint32) uint32 {
+ // amd64/v3:"SHRXL"
+ s := x >> y
+ // amd64/v3:"SHLXL"
+ s = s << y
+ return s
+}
+
+func shlrx64_load(x []uint64, i int, s uint64) uint64 {
// amd64/v3: `SHRXQ\t[A-Z]+[0-9]*, \([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), [A-Z]+[0-9]*`
s = x[i] >> i
// amd64/v3: `SHLXQ\t[A-Z]+[0-9]*, 8\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\), [A-Z]+[0-9]*`
@@ -54,7 +96,7 @@ func shlrx64(x []uint64, i int, s uint64) uint64 {
return s
}
-func shlrx32(x []uint32, i int, s uint32) uint32 {
+func shlrx32_load(x []uint32, i int, s uint32) uint32 {
// amd64/v3: `SHRXL\t[A-Z]+[0-9]*, \([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), [A-Z]+[0-9]*`
s = x[i] >> i
// amd64/v3: `SHLXL\t[A-Z]+[0-9]*, 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), [A-Z]+[0-9]*`
diff --git a/test/codegen/memcombine.go b/test/codegen/memcombine.go
index ad42538dcdb..0292d7f0f3e 100644
--- a/test/codegen/memcombine.go
+++ b/test/codegen/memcombine.go
@@ -11,98 +11,94 @@ import (
"runtime"
)
-var sink64 uint64
-var sink32 uint32
-var sink16 uint16
-
// ------------- //
// Loading //
// ------------- //
-func load_le64(b []byte) {
+func load_le64(b []byte) uint64 {
// amd64:`MOVQ\s\(.*\),`,-`MOV[BWL]\t[^$]`,-`OR`
// s390x:`MOVDBR\s\(.*\),`
// arm64:`MOVD\s\(R[0-9]+\),`,-`MOV[BHW]`
// ppc64le:`MOVD\s`,-`MOV[BHW]Z`
- sink64 = binary.LittleEndian.Uint64(b)
+ return binary.LittleEndian.Uint64(b)
}
-func load_le64_idx(b []byte, idx int) {
+func load_le64_idx(b []byte, idx int) uint64 {
// amd64:`MOVQ\s\(.*\)\(.*\*1\),`,-`MOV[BWL]\t[^$]`,-`OR`
// s390x:`MOVDBR\s\(.*\)\(.*\*1\),`
// arm64:`MOVD\s\(R[0-9]+\)\(R[0-9]+\),`,-`MOV[BHW]`
// ppc64le:`MOVD\s`,-`MOV[BHW]Z\s`
- sink64 = binary.LittleEndian.Uint64(b[idx:])
+ return binary.LittleEndian.Uint64(b[idx:])
}
-func load_le32(b []byte) {
+func load_le32(b []byte) uint32 {
// amd64:`MOVL\s\(.*\),`,-`MOV[BW]`,-`OR`
// 386:`MOVL\s\(.*\),`,-`MOV[BW]`,-`OR`
// s390x:`MOVWBR\s\(.*\),`
// arm64:`MOVWU\s\(R[0-9]+\),`,-`MOV[BH]`
// ppc64le:`MOVWZ\s`,-`MOV[BH]Z\s`
- sink32 = binary.LittleEndian.Uint32(b)
+ return binary.LittleEndian.Uint32(b)
}
-func load_le32_idx(b []byte, idx int) {
+func load_le32_idx(b []byte, idx int) uint32 {
// amd64:`MOVL\s\(.*\)\(.*\*1\),`,-`MOV[BW]`,-`OR`
// 386:`MOVL\s\(.*\)\(.*\*1\),`,-`MOV[BW]`,-`OR`
// s390x:`MOVWBR\s\(.*\)\(.*\*1\),`
// arm64:`MOVWU\s\(R[0-9]+\)\(R[0-9]+\),`,-`MOV[BH]`
// ppc64le:`MOVWZ\s`,-`MOV[BH]Z\s`
- sink32 = binary.LittleEndian.Uint32(b[idx:])
+ return binary.LittleEndian.Uint32(b[idx:])
}
-func load_le16(b []byte) {
+func load_le16(b []byte) uint16 {
// amd64:`MOVWLZX\s\(.*\),`,-`MOVB`,-`OR`
// ppc64le:`MOVHZ\s`,-`MOVBZ`
// arm64:`MOVHU\s\(R[0-9]+\),`,-`MOVB`
// s390x:`MOVHBR\s\(.*\),`
- sink16 = binary.LittleEndian.Uint16(b)
+ return binary.LittleEndian.Uint16(b)
}
-func load_le16_idx(b []byte, idx int) {
+func load_le16_idx(b []byte, idx int) uint16 {
// amd64:`MOVWLZX\s\(.*\),`,-`MOVB`,-`OR`
// ppc64le:`MOVHZ\s`,-`MOVBZ`
// arm64:`MOVHU\s\(R[0-9]+\)\(R[0-9]+\),`,-`MOVB`
// s390x:`MOVHBR\s\(.*\)\(.*\*1\),`
- sink16 = binary.LittleEndian.Uint16(b[idx:])
+ return binary.LittleEndian.Uint16(b[idx:])
}
-func load_be64(b []byte) {
+func load_be64(b []byte) uint64 {
// amd64/v1,amd64/v2:`BSWAPQ`,-`MOV[BWL]\t[^$]`,-`OR`
// amd64/v3:`MOVBEQ`
// s390x:`MOVD\s\(.*\),`
// arm64:`REV`,`MOVD\s\(R[0-9]+\),`,-`MOV[BHW]`,-`REVW`,-`REV16W`
// ppc64le:`MOVDBR`,-`MOV[BHW]Z`
- sink64 = binary.BigEndian.Uint64(b)
+ return binary.BigEndian.Uint64(b)
}
-func load_be64_idx(b []byte, idx int) {
+func load_be64_idx(b []byte, idx int) uint64 {
// amd64/v1,amd64/v2:`BSWAPQ`,-`MOV[BWL]\t[^$]`,-`OR`
- // amd64/v3: `MOVBEQ`
+ // amd64/v3: `MOVBEQ\t\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*1\), [A-Z]+[0-9]*`
// s390x:`MOVD\s\(.*\)\(.*\*1\),`
// arm64:`REV`,`MOVD\s\(R[0-9]+\)\(R[0-9]+\),`,-`MOV[WHB]`,-`REVW`,-`REV16W`
// ppc64le:`MOVDBR`,-`MOV[BHW]Z`
- sink64 = binary.BigEndian.Uint64(b[idx:])
+ return binary.BigEndian.Uint64(b[idx:])
}
-func load_be32(b []byte) {
+func load_be32(b []byte) uint32 {
// amd64/v1,amd64/v2:`BSWAPL`,-`MOV[BW]`,-`OR`
// amd64/v3: `MOVBEL`
// s390x:`MOVWZ\s\(.*\),`
// arm64:`REVW`,`MOVWU\s\(R[0-9]+\),`,-`MOV[BH]`,-`REV16W`
// ppc64le:`MOVWBR`,-`MOV[BH]Z`
- sink32 = binary.BigEndian.Uint32(b)
+ return binary.BigEndian.Uint32(b)
}
-func load_be32_idx(b []byte, idx int) {
+func load_be32_idx(b []byte, idx int) uint32 {
// amd64/v1,amd64/v2:`BSWAPL`,-`MOV[BW]`,-`OR`
- // amd64/v3: `MOVBEL`
+ // amd64/v3: `MOVBEL\t\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*1\), [A-Z]+[0-9]*`
// s390x:`MOVWZ\s\(.*\)\(.*\*1\),`
// arm64:`REVW`,`MOVWU\s\(R[0-9]+\)\(R[0-9]+\),`,-`MOV[HB]`,-`REV16W`
// ppc64le:`MOVWBR`,-`MOV[BH]Z`
- sink32 = binary.BigEndian.Uint32(b[idx:])
+ return binary.BigEndian.Uint32(b[idx:])
}
func load_be16(b []byte) uint16 {
@@ -357,20 +353,20 @@ func safe_point(p, q *[2]*int) {
// Storing //
// ------------- //
-func store_le64(b []byte) {
+func store_le64(b []byte, x uint64) {
// amd64:`MOVQ\s.*\(.*\)$`,-`SHR.`
// arm64:`MOVD`,-`MOV[WBH]`
// ppc64le:`MOVD\s`,-`MOV[BHW]\s`
// s390x:`MOVDBR\s.*\(.*\)$`
- binary.LittleEndian.PutUint64(b, sink64)
+ binary.LittleEndian.PutUint64(b, x)
}
-func store_le64_idx(b []byte, idx int) {
+func store_le64_idx(b []byte, x uint64, idx int) {
// amd64:`MOVQ\s.*\(.*\)\(.*\*1\)$`,-`SHR.`
// arm64:`MOVD\sR[0-9]+,\s\(R[0-9]+\)\(R[0-9]+\)`,-`MOV[BHW]`
// ppc64le:`MOVD\s`,-`MOV[BHW]\s`
// s390x:`MOVDBR\s.*\(.*\)\(.*\*1\)$`
- binary.LittleEndian.PutUint64(b[idx:], sink64)
+ binary.LittleEndian.PutUint64(b[idx:], x)
}
func store_le64_load(b []byte, x *[8]byte) {
@@ -382,63 +378,63 @@ func store_le64_load(b []byte, x *[8]byte) {
binary.LittleEndian.PutUint64(b, binary.LittleEndian.Uint64(x[:]))
}
-func store_le32(b []byte) {
+func store_le32(b []byte, x uint32) {
// amd64:`MOVL\s`
// arm64:`MOVW`,-`MOV[BH]`
// ppc64le:`MOVW\s`
// s390x:`MOVWBR\s.*\(.*\)$`
- binary.LittleEndian.PutUint32(b, sink32)
+ binary.LittleEndian.PutUint32(b, x)
}
-func store_le32_idx(b []byte, idx int) {
+func store_le32_idx(b []byte, x uint32, idx int) {
// amd64:`MOVL\s`
// arm64:`MOVW\sR[0-9]+,\s\(R[0-9]+\)\(R[0-9]+\)`,-`MOV[BH]`
// ppc64le:`MOVW\s`
// s390x:`MOVWBR\s.*\(.*\)\(.*\*1\)$`
- binary.LittleEndian.PutUint32(b[idx:], sink32)
+ binary.LittleEndian.PutUint32(b[idx:], x)
}
-func store_le16(b []byte) {
+func store_le16(b []byte, x uint16) {
// amd64:`MOVW\s`
// arm64:`MOVH`,-`MOVB`
// ppc64le:`MOVH\s`
// s390x:`MOVHBR\s.*\(.*\)$`
- binary.LittleEndian.PutUint16(b, sink16)
+ binary.LittleEndian.PutUint16(b, x)
}
-func store_le16_idx(b []byte, idx int) {
+func store_le16_idx(b []byte, x uint16, idx int) {
// amd64:`MOVW\s`
// arm64:`MOVH\sR[0-9]+,\s\(R[0-9]+\)\(R[0-9]+\)`,-`MOVB`
// ppc64le:`MOVH\s`
// s390x:`MOVHBR\s.*\(.*\)\(.*\*1\)$`
- binary.LittleEndian.PutUint16(b[idx:], sink16)
+ binary.LittleEndian.PutUint16(b[idx:], x)
}
-func store_be64(b []byte) {
+func store_be64(b []byte, x uint64) {
// amd64/v1,amd64/v2:`BSWAPQ`,-`SHR.`
// amd64/v3: `MOVBEQ`
// arm64:`MOVD`,`REV`,-`MOV[WBH]`,-`REVW`,-`REV16W`
// ppc64le:`MOVDBR`
// s390x:`MOVD\s.*\(.*\)$`,-`SRW\s`,-`SRD\s`
- binary.BigEndian.PutUint64(b, sink64)
+ binary.BigEndian.PutUint64(b, x)
}
-func store_be64_idx(b []byte, idx int) {
+func store_be64_idx(b []byte, x uint64, idx int) {
// amd64/v1,amd64/v2:`BSWAPQ`,-`SHR.`
- // amd64/v3:`MOVBEQ`
+ // amd64/v3:`MOVBEQ\t[A-Z]+[0-9]*, \([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*1\)`
// arm64:`REV`,`MOVD\sR[0-9]+,\s\(R[0-9]+\)\(R[0-9]+\)`,-`MOV[BHW]`,-`REV16W`,-`REVW`
// ppc64le:`MOVDBR`
// s390x:`MOVD\s.*\(.*\)\(.*\*1\)$`,-`SRW\s`,-`SRD\s`
- binary.BigEndian.PutUint64(b[idx:], sink64)
+ binary.BigEndian.PutUint64(b[idx:], x)
}
-func store_be32(b []byte) {
+func store_be32(b []byte, x uint32) {
// amd64/v1,amd64/v2:`BSWAPL`,-`SHR.`
// amd64/v3:`MOVBEL`
// arm64:`MOVW`,`REVW`,-`MOV[BH]`,-`REV16W`
// ppc64le:`MOVWBR`
// s390x:`MOVW\s.*\(.*\)$`,-`SRW\s`,-`SRD\s`
- binary.BigEndian.PutUint32(b, sink32)
+ binary.BigEndian.PutUint32(b, x)
}
func store_be64_load(b, x *[8]byte) {
@@ -453,31 +449,31 @@ func store_be32_load(b, x *[8]byte) {
binary.BigEndian.PutUint32(b[:], binary.BigEndian.Uint32(x[:]))
}
-func store_be32_idx(b []byte, idx int) {
+func store_be32_idx(b []byte, x uint32, idx int) {
// amd64/v1,amd64/v2:`BSWAPL`,-`SHR.`
- // amd64/v3:`MOVBEL`
+ // amd64/v3:`MOVBEL\t[A-Z]+[0-9]*, \([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*1\)`
// arm64:`REVW`,`MOVW\sR[0-9]+,\s\(R[0-9]+\)\(R[0-9]+\)`,-`MOV[BH]`,-`REV16W`
// ppc64le:`MOVWBR`
// s390x:`MOVW\s.*\(.*\)\(.*\*1\)$`,-`SRW\s`,-`SRD\s`
- binary.BigEndian.PutUint32(b[idx:], sink32)
+ binary.BigEndian.PutUint32(b[idx:], x)
}
-func store_be16(b []byte) {
+func store_be16(b []byte, x uint16) {
// amd64/v1,amd64/v2:`ROLW\s\$8`,-`SHR.`
// amd64/v3:`MOVBEW`,-`ROLW`
// arm64:`MOVH`,`REV16W`,-`MOVB`
// ppc64le:`MOVHBR`
// s390x:`MOVH\s.*\(.*\)$`,-`SRW\s`,-`SRD\s`
- binary.BigEndian.PutUint16(b, sink16)
+ binary.BigEndian.PutUint16(b, x)
}
-func store_be16_idx(b []byte, idx int) {
+func store_be16_idx(b []byte, x uint16, idx int) {
// amd64/v1,amd64/v2:`ROLW\s\$8`,-`SHR.`
- // amd64/v3: `MOVBEW`
+ // amd64/v3:`MOVBEW\t[A-Z]+[0-9]*, \([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*1\)`
// arm64:`MOVH\sR[0-9]+,\s\(R[0-9]+\)\(R[0-9]+\)`,`REV16W`,-`MOVB`
// ppc64le:`MOVHBR`
// s390x:`MOVH\s.*\(.*\)\(.*\*1\)$`,-`SRW\s`,-`SRD\s`
- binary.BigEndian.PutUint16(b[idx:], sink16)
+ binary.BigEndian.PutUint16(b[idx:], x)
}
func store_le_byte_2(b []byte, val uint16) {
diff --git a/test/codegen/switch.go b/test/codegen/switch.go
index 2ac817d14c8..a6566834a86 100644
--- a/test/codegen/switch.go
+++ b/test/codegen/switch.go
@@ -20,3 +20,53 @@ func f(x string) int {
return -3
}
}
+
+// use jump tables for 8+ int cases
+func square(x int) int {
+ // amd64:`JMP\s\(.*\)\(.*\)$`
+ switch x {
+ case 1:
+ return 1
+ case 2:
+ return 4
+ case 3:
+ return 9
+ case 4:
+ return 16
+ case 5:
+ return 25
+ case 6:
+ return 36
+ case 7:
+ return 49
+ case 8:
+ return 64
+ default:
+ return x * x
+ }
+}
+
+// use jump tables for 8+ string lengths
+func length(x string) int {
+ // amd64:`JMP\s\(.*\)\(.*\)$`
+ switch x {
+ case "a":
+ return 1
+ case "bb":
+ return 2
+ case "ccc":
+ return 3
+ case "dddd":
+ return 4
+ case "eeeee":
+ return 5
+ case "ffffff":
+ return 6
+ case "ggggggg":
+ return 7
+ case "hhhhhhhh":
+ return 8
+ default:
+ return len(x)
+ }
+}
diff --git a/test/fixedbugs/issue52127.go b/test/fixedbugs/issue52127.go
new file mode 100644
index 00000000000..7738c3fabf7
--- /dev/null
+++ b/test/fixedbugs/issue52127.go
@@ -0,0 +1,62 @@
+// run
+//go:build !js
+// +build !js
+
+// Copyright 2022 The Go 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 52127: Too many syntax errors in many files can
+// cause deadlocks instead of displaying error messages
+// correctly.
+
+package main
+
+import (
+ "bytes"
+ "fmt"
+ "os"
+ "os/exec"
+ "path/filepath"
+)
+
+func main() {
+ dir, err := os.MkdirTemp("", "issue52127")
+ if err != nil {
+ panic(err)
+ }
+ defer os.RemoveAll(dir)
+
+ args := []string{"go", "build"}
+ write := func(prefix string, i int, data string) {
+ filename := filepath.Join(dir, fmt.Sprintf("%s%d.go", prefix, i))
+ if err := os.WriteFile(filename, []byte(data), 0o644); err != nil {
+ panic(err)
+ }
+ args = append(args, filename)
+ }
+
+ for i := 0; i < 100; i++ {
+ write("a", i, `package p
+`)
+ }
+ for i := 0; i < 100; i++ {
+ write("b", i, `package p
+var
+var
+var
+var
+var
+`)
+ }
+
+ cmd := exec.Command(args[0], args[1:]...)
+ output, err := cmd.CombinedOutput()
+ if err == nil {
+ panic("compile succeeded unexpectedly")
+ }
+ if !bytes.Contains(output, []byte("syntax error:")) {
+ panic(fmt.Sprintf(`missing "syntax error" in compiler output; got:
+%s`, output))
+ }
+} \ No newline at end of file
diff --git a/test/fixedbugs/issue52278.go b/test/fixedbugs/issue52278.go
new file mode 100644
index 00000000000..56169e68714
--- /dev/null
+++ b/test/fixedbugs/issue52278.go
@@ -0,0 +1,12 @@
+// compile
+
+// Copyright 2022 The Go Authors. 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
+
+func main() {
+_:
+_:
+}
diff --git a/test/inline.go b/test/inline.go
index cb8403e9ce1..400898bceeb 100644
--- a/test/inline.go
+++ b/test/inline.go
@@ -160,6 +160,51 @@ func switchType(x interface{}) int { // ERROR "can inline switchType" "x does no
}
}
+// Test that switches on constant things, with constant cases, only cost anything for
+// the case that matches. See issue 50253.
+func switchConst1(p func(string)) { // ERROR "can inline switchConst" "p does not escape"
+ const c = 1
+ switch c {
+ case 0:
+ p("zero")
+ case 1:
+ p("one")
+ case 2:
+ p("two")
+ default:
+ p("other")
+ }
+}
+
+func switchConst2() string { // ERROR "can inline switchConst2"
+ switch runtime.GOOS {
+ case "linux":
+ return "Leenooks"
+ case "windows":
+ return "Windoze"
+ case "darwin":
+ return "MackBone"
+ case "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "100":
+ return "Numbers"
+ default:
+ return "oh nose!"
+ }
+}
+func switchConst3() string { // ERROR "can inline switchConst3"
+ switch runtime.GOOS {
+ case "Linux":
+ panic("Linux")
+ case "Windows":
+ panic("Windows")
+ case "Darwin":
+ panic("Darwin")
+ case "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "100":
+ panic("Numbers")
+ default:
+ return "oh nose!"
+ }
+}
+
func inlineRangeIntoMe(data []int) { // ERROR "can inline inlineRangeIntoMe" "data does not escape"
rangeFunc(data, 12) // ERROR "inlining call to rangeFunc"
}
diff --git a/test/nosplit.go b/test/nosplit.go
index 7c7e1bfd99e..9cedb93ec36 100644
--- a/test/nosplit.go
+++ b/test/nosplit.go
@@ -51,7 +51,8 @@ var tests = `
start 0
# Large frame marked nosplit is always wrong.
-start 10000 nosplit
+# Frame is so large it overflows cmd/link's int16.
+start 100000 nosplit
REJECT
# Calling a large frame is okay.
@@ -70,6 +71,18 @@ start 0 call start
start 0 nosplit call start
REJECT
+# Non-trivial recursion runs out of space.
+start 0 call f1
+f1 0 nosplit call f2
+f2 0 nosplit call f1
+REJECT
+# Same but cycle starts below nosplit entry.
+start 0 call f1
+f1 0 nosplit call f2
+f2 0 nosplit call f3
+f3 0 nosplit call f2
+REJECT
+
# Chains of ordinary functions okay.
start 0 call f1
f1 80 call f2
@@ -105,6 +118,14 @@ f8 16 nosplit call end
end 1000
REJECT
+# Two paths both go over the stack limit.
+start 0 call f1
+f1 80 nosplit call f2 call f3
+f2 40 nosplit call f4
+f3 96 nosplit
+f4 40 nosplit
+REJECT
+
# Test cases near the 128-byte limit.
# Ordinary stack split frame is always okay.
@@ -292,12 +313,13 @@ TestCases:
fmt.Fprintf(&gobuf, "func main() { main0() }\n")
fmt.Fprintf(&buf, "TEXT ·main0(SB),0,$0-0\n\tCALL ·start(SB)\n")
+ adjusted := false
for _, line := range strings.Split(lines, "\n") {
line = strings.TrimSpace(line)
if line == "" {
continue
}
- for i, subline := range strings.Split(line, ";") {
+ for _, subline := range strings.Split(line, ";") {
subline = strings.TrimSpace(subline)
if subline == "" {
continue
@@ -311,10 +333,19 @@ TestCases:
name := m[1]
size, _ := strconv.Atoi(m[2])
+ if size%ptrSize == 4 {
+ continue TestCases
+ }
+ nosplit := m[3]
+ body := m[4]
+
// The limit was originally 128 but is now 800 (928-128).
// Instead of rewriting the test cases above, adjust
- // the first stack frame to use up the extra bytes.
- if i == 0 {
+ // the first nosplit frame to use up the extra bytes.
+ // This isn't exactly right because we could have
+ // nosplit -> split -> nosplit, but it's good enough.
+ if !adjusted && nosplit != "" {
+ adjusted = true
size += (928 - 128) - 128
// Noopt builds have a larger stackguard.
// See ../src/cmd/dist/buildruntime.go:stackGuardMultiplier
@@ -326,12 +357,6 @@ TestCases:
}
}
- if size%ptrSize == 4 {
- continue TestCases
- }
- nosplit := m[3]
- body := m[4]
-
if nosplit != "" {
nosplit = ",7"
} else {
diff --git a/test/run.go b/test/run.go
index 468379b4a98..45cd086fc43 100644
--- a/test/run.go
+++ b/test/run.go
@@ -14,6 +14,7 @@ import (
"flag"
"fmt"
"go/build"
+ "go/build/constraint"
"hash/fnv"
"io"
"io/fs"
@@ -462,40 +463,24 @@ func shouldTest(src string, goos, goarch string) (ok bool, whyNot string) {
return true, ""
}
for _, line := range strings.Split(src, "\n") {
- line = strings.TrimSpace(line)
- if strings.HasPrefix(line, "//") {
- line = line[2:]
- } else {
- continue
- }
- line = strings.TrimSpace(line)
- if len(line) == 0 || line[0] != '+' {
- continue
+ if strings.HasPrefix(line, "package ") {
+ break
}
- gcFlags := os.Getenv("GO_GCFLAGS")
- ctxt := &context{
- GOOS: goos,
- GOARCH: goarch,
- cgoEnabled: cgoEnabled,
- noOptEnv: strings.Contains(gcFlags, "-N") || strings.Contains(gcFlags, "-l"),
- }
-
- words := strings.Fields(line)
- if words[0] == "+build" {
- ok := false
- for _, word := range words[1:] {
- if ctxt.match(word) {
- ok = true
- break
- }
+
+ if expr, err := constraint.Parse(line); err == nil {
+ gcFlags := os.Getenv("GO_GCFLAGS")
+ ctxt := &context{
+ GOOS: goos,
+ GOARCH: goarch,
+ cgoEnabled: cgoEnabled,
+ noOptEnv: strings.Contains(gcFlags, "-N") || strings.Contains(gcFlags, "-l"),
}
- if !ok {
- // no matching tag found.
+
+ if !expr.Eval(ctxt.match) {
return false, line
}
}
}
- // no build tags
return true, ""
}
@@ -503,16 +488,6 @@ func (ctxt *context) match(name string) bool {
if name == "" {
return false
}
- if first, rest, ok := strings.Cut(name, ","); ok {
- // comma-separated list
- return ctxt.match(first) && ctxt.match(rest)
- }
- if strings.HasPrefix(name, "!!") { // bad syntax, reject always
- return false
- }
- if strings.HasPrefix(name, "!") { // negation
- return len(name) > 1 && !ctxt.match(name[1:])
- }
// Tags must be letters, digits, underscores or dots.
// Unlike in Go identifiers, all digits are fine (e.g., "386").
diff --git a/test/shift3.go b/test/shift3.go
new file mode 100644
index 00000000000..bed2fd66ef7
--- /dev/null
+++ b/test/shift3.go
@@ -0,0 +1,41 @@
+// run
+
+// Copyright 2022 The Go 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 the compiler's noder uses the correct type
+// for RHS shift operands that are untyped. Must compile;
+// run for good measure.
+
+package main
+
+import (
+ "fmt"
+ "math"
+)
+
+func f(x, y int) {
+ if x != y {
+ panic(fmt.Sprintf("%d != %d", x, y))
+ }
+}
+
+func main() {
+ var x int = 1
+ f(x<<1, 2)
+ f(x<<1., 2)
+ f(x<<(1+0i), 2)
+ f(x<<0i, 1)
+
+ f(x<<(1<<x), 4)
+ f(x<<(1.<<x), 4)
+ f(x<<((1+0i)<<x), 4)
+ f(x<<(0i<<x), 1)
+
+ // corner cases
+ const M = math.MaxUint
+ f(x<<(M+0), 0) // shift by untyped int representable as uint
+ f(x<<(M+0.), 0) // shift by untyped float representable as uint
+ f(x<<(M+0.+0i), 0) // shift by untyped complex representable as uint
+}
diff --git a/test/typeparam/issue51700.go b/test/typeparam/issue51700.go
new file mode 100644
index 00000000000..bf8a1f6289b
--- /dev/null
+++ b/test/typeparam/issue51700.go
@@ -0,0 +1,26 @@
+// run
+
+// Copyright 2022 The Go Authors. 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
+
+func f[B any](b B) {
+ if b1, ok := any(b).(interface{ m1() }); ok {
+ panic(1)
+ _ = b1.(B)
+ }
+ if b2, ok := any(b).(interface{ m2() }); ok {
+ panic(2)
+ _ = b2.(B)
+ }
+}
+
+type S struct{}
+
+func (S) m3() {}
+
+func main() {
+ f(S{})
+}
diff --git a/test/typeparam/issue52124.go b/test/typeparam/issue52124.go
index 56318d5d4ce..a113fc74441 100644
--- a/test/typeparam/issue52124.go
+++ b/test/typeparam/issue52124.go
@@ -7,3 +7,9 @@
package p
type I interface{ any | int }
+
+var (
+ X I = 42
+ Y I = "xxx"
+ Z I = true
+)
diff --git a/test/typeparam/issue52228.go b/test/typeparam/issue52228.go
new file mode 100644
index 00000000000..3fbbde59ab6
--- /dev/null
+++ b/test/typeparam/issue52228.go
@@ -0,0 +1,30 @@
+// run
+
+// Copyright 2022 The Go Authors. 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
+
+type SomeInterface interface {
+ Whatever()
+}
+
+func X[T any]() T {
+ var m T
+
+ // for this example, this block should never run
+ if _, ok := any(m).(SomeInterface); ok {
+ var dst SomeInterface
+ _, _ = dst.(T)
+ return dst.(T)
+ }
+
+ return m
+}
+
+type holder struct{}
+
+func main() {
+ X[holder]()
+}