diff --git a/doc/godebug.md b/doc/godebug.md index de47d3a91cbfe4..50142fb1a8b989 100644 --- a/doc/godebug.md +++ b/doc/godebug.md @@ -222,7 +222,7 @@ SecP256r1MLKEM768 and SecP384r1MLKEM1024. The default can be reverted using the [`tlssecpmlkem` setting](/pkg/crypto/tls/#Config.CurvePreferences). Go 1.26 added a new `tracebacklabels` setting that controls the inclusion of -goroutine labels set through the the `runtime/pprof` package. Setting `tracebacklabels=1` +goroutine labels set through the `runtime/pprof` package. Setting `tracebacklabels=1` includes these key/value pairs in the goroutine status header of runtime tracebacks and debug=2 runtime/pprof stack dumps. This format may change in the future. (see go.dev/issue/76349) diff --git a/src/archive/tar/format.go b/src/archive/tar/format.go index 32e58a9d9b4e1e..97d5b4190c1416 100644 --- a/src/archive/tar/format.go +++ b/src/archive/tar/format.go @@ -235,10 +235,16 @@ func (b *block) setFormat(format Format) { // signed byte values. // We compute and return both. func (b *block) computeChecksum() (unsigned, signed int64) { - for i, c := range b { - if 148 <= i && i < 156 { - c = ' ' // Treat the checksum field itself as all spaces. - } + for _, c := range b[:148] { + unsigned += int64(c) + signed += int64(int8(c)) + } + // Treat the checksum field itself (bytes 148 to 155, inclusive) + // as if it were all spaces. + const chksumSpaces = 8 * int64(' ') + unsigned += chksumSpaces + signed += chksumSpaces + for _, c := range b[156:] { unsigned += int64(c) signed += int64(int8(c)) } diff --git a/src/archive/tar/reader.go b/src/archive/tar/reader.go index e426c72ffe9cc3..9f47775c9aec5f 100644 --- a/src/archive/tar/reader.go +++ b/src/archive/tar/reader.go @@ -405,9 +405,11 @@ func (tr *Reader) readHeader() (*Header, *block, error) { // For Format detection, check if block is properly formatted since // the parser is more liberal than what USTAR actually permits. - notASCII := func(r rune) bool { return r >= 0x80 } - if bytes.IndexFunc(tr.blk[:], notASCII) >= 0 { - hdr.Format = FormatUnknown // Non-ASCII characters in block. + for _, c := range tr.blk[:] { + if c >= 0x80 { + hdr.Format = FormatUnknown // Non-ASCII characters in block. + break + } } nul := func(b []byte) bool { return int(b[len(b)-1]) == 0 } if !(nul(v7.size()) && nul(v7.mode()) && nul(v7.uid()) && nul(v7.gid()) && diff --git a/src/cmd/compile/internal/amd64/ssa.go b/src/cmd/compile/internal/amd64/ssa.go index c312cafebe7217..5353e73a3f2ff1 100644 --- a/src/cmd/compile/internal/amd64/ssa.go +++ b/src/cmd/compile/internal/amd64/ssa.go @@ -19,6 +19,7 @@ import ( "cmd/internal/obj" "cmd/internal/obj/x86" "internal/abi" + "internal/buildcfg" ) // ssaMarkMoves marks any MOVXconst ops that need to avoid clobbering flags. @@ -944,10 +945,15 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { ssagen.AddAux2(&p.To, v, off) case ssa.OpAMD64MOVQstoreconst, ssa.OpAMD64MOVLstoreconst, ssa.OpAMD64MOVWstoreconst, ssa.OpAMD64MOVBstoreconst: - p := s.Prog(v.Op.Asm()) - p.From.Type = obj.TYPE_CONST sc := v.AuxValAndOff() - p.From.Offset = sc.Val64() + p := s.Prog(v.Op.Asm()) + if sc.Val() == 0 && s.ABI == obj.ABIInternal && buildcfg.GOOS != "plan9" && (v.Op == ssa.OpAMD64MOVQstoreconst || v.Op == ssa.OpAMD64MOVLstoreconst) { + p.From.Type = obj.TYPE_REG + p.From.Reg = x86.REG_X15 + } else { + p.From.Type = obj.TYPE_CONST + p.From.Offset = sc.Val64() + } p.To.Type = obj.TYPE_MEM p.To.Reg = v.Args[0].Reg() ssagen.AddAux2(&p.To, v, sc.Off64()) @@ -981,6 +987,14 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) { p.From.Type = obj.TYPE_CONST sc := v.AuxValAndOff() p.From.Offset = sc.Val64() + if sc.Val() == 0 && s.ABI == obj.ABIInternal && buildcfg.GOOS != "plan9" { + switch v.Op { + case ssa.OpAMD64MOVQstoreconstidx1, ssa.OpAMD64MOVQstoreconstidx8, + ssa.OpAMD64MOVLstoreconstidx1, ssa.OpAMD64MOVLstoreconstidx4: + p.From.Type = obj.TYPE_REG + p.From.Reg = x86.REG_X15 + } + } switch { case p.As == x86.AADDQ && p.From.Offset == 1: p.As = x86.AINCQ diff --git a/src/cmd/compile/internal/ssa/_gen/AMD64.rules b/src/cmd/compile/internal/ssa/_gen/AMD64.rules index a3e3f566361483..1d61ab54487712 100644 --- a/src/cmd/compile/internal/ssa/_gen/AMD64.rules +++ b/src/cmd/compile/internal/ssa/_gen/AMD64.rules @@ -1623,7 +1623,6 @@ // CPUID feature: BMI1. (AND(Q|L) x (NOT(Q|L) y)) && buildcfg.GOAMD64 >= 3 => (ANDN(Q|L) x y) -(SUB(Q|L) x (AND(Q|L) x y)) && buildcfg.GOAMD64 >= 3 => (ANDN(Q|L) x y) (AND(Q|L) x (NEG(Q|L) x)) && buildcfg.GOAMD64 >= 3 => (BLSI(Q|L) x) (XOR(Q|L) x (ADD(Q|L)const [-1] x)) && buildcfg.GOAMD64 >= 3 => (BLSMSK(Q|L) x) (AND(Q|L) x (ADD(Q|L)const [-1] x)) && buildcfg.GOAMD64 >= 3 => (Select0 (BLSR(Q|L) x)) diff --git a/src/cmd/compile/internal/ssa/rewriteAMD64.go b/src/cmd/compile/internal/ssa/rewriteAMD64.go index 9be323237e5c39..c1082699dfeb36 100644 --- a/src/cmd/compile/internal/ssa/rewriteAMD64.go +++ b/src/cmd/compile/internal/ssa/rewriteAMD64.go @@ -40715,31 +40715,6 @@ func rewriteValueAMD64_OpAMD64SUBL(v *Value) bool { v.AddArg3(x, ptr, mem) return true } - // match: (SUBL x (ANDL x y)) - // cond: buildcfg.GOAMD64 >= 3 - // result: (ANDNL x y) - for { - x := v_0 - if v_1.Op != OpAMD64ANDL { - 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 x != v_1_0 { - continue - } - y := v_1_1 - if !(buildcfg.GOAMD64 >= 3) { - continue - } - v.reset(OpAMD64ANDNL) - v.AddArg2(x, y) - return true - } - break - } return false } func rewriteValueAMD64_OpAMD64SUBLconst(v *Value) bool { @@ -40982,31 +40957,6 @@ func rewriteValueAMD64_OpAMD64SUBQ(v *Value) bool { v.AddArg3(x, ptr, mem) return true } - // match: (SUBQ x (ANDQ x y)) - // cond: buildcfg.GOAMD64 >= 3 - // result: (ANDNQ x y) - for { - x := v_0 - if v_1.Op != OpAMD64ANDQ { - 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 x != v_1_0 { - continue - } - y := v_1_1 - if !(buildcfg.GOAMD64 >= 3) { - continue - } - v.reset(OpAMD64ANDNQ) - v.AddArg2(x, y) - return true - } - break - } return false } func rewriteValueAMD64_OpAMD64SUBQborrow(v *Value) bool { diff --git a/src/cmd/go/internal/modcmd/why.go b/src/cmd/go/internal/modcmd/why.go index 0815a6d28c171d..4c4efde10ebd8d 100644 --- a/src/cmd/go/internal/modcmd/why.go +++ b/src/cmd/go/internal/modcmd/why.go @@ -12,6 +12,8 @@ import ( "cmd/go/internal/base" "cmd/go/internal/imports" "cmd/go/internal/modload" + + "golang.org/x/mod/module" ) var cmdWhy = &base.Command{ @@ -82,7 +84,11 @@ func runWhy(ctx context.Context, cmd *base.Command, args []string) { if strings.Contains(arg, "@") { base.Fatalf("go: %s: 'go mod why' requires a module path, not a version query", arg) } + if err := checkModulePathPattern(arg); err != nil { + base.Errorf("go mod why: %v", err) + } } + base.ExitIfErrors() mods, err := modload.ListModules(moduleLoader, ctx, args, 0, "") if err != nil { @@ -142,3 +148,33 @@ func runWhy(ctx context.Context, cmd *base.Command, args []string) { } } } + +func checkModulePathPattern(pattern string) error { + parts := strings.Split(pattern, "...") + if len(parts) == 1 { + return modulePathError(pattern, module.CheckImportPath(pattern)) + } + + // Add placeholders for the wildcards adjoining each literal part so that + // separators at wildcard boundaries form complete paths during validation. + if err := module.CheckImportPath(parts[0] + "x"); err != nil { + return modulePathError(pattern, err) + } + for i, part := range parts[1:] { + if i < len(parts)-2 { + part += "x" + } + if err := module.CheckFilePath("x" + part); err != nil { + return modulePathError(pattern, err) + } + } + return nil +} + +func modulePathError(path string, err error) error { + if pathErr, ok := err.(*module.InvalidPathError); ok { + pathErr.Kind = "module" + pathErr.Path = path + } + return err +} diff --git a/src/cmd/go/testdata/script/mod_why.txt b/src/cmd/go/testdata/script/mod_why.txt index b3036fa83040c0..fa34ec3212dfed 100644 --- a/src/cmd/go/testdata/script/mod_why.txt +++ b/src/cmd/go/testdata/script/mod_why.txt @@ -1,4 +1,16 @@ env GO111MODULE=on + +# 'go mod why -m' should reject malformed module paths before loading +# the module graph. +! go mod why -m golang.org/x/text/ +stderr '^go mod why: malformed module path "golang.org/x/text/": trailing slash$' + +# Module patterns should validate the literal path around each wildcard. +! go mod why -m golang.org//x/... +stderr '^go mod why: malformed module path "golang.org//x/...": double slash$' +! go mod why -m golang.org/...//.../text +stderr '^go mod why: malformed module path "golang.org/...//.../text": double slash$' + [short] skip # Populate go.sum. @@ -17,6 +29,14 @@ cmp stdout why-language.txt go mod why -m golang.org... cmp stdout why-text-module.txt +# Module patterns may contain multiple wildcards. +go mod why -m golang...org/.../te... +cmp stdout why-text-module.txt + +# Module paths that are valid only when replaced should be accepted. +go mod why -m mymodule/nested +cmp stdout why-replaced-module.txt + # why a package used only in tests? go mod why rsc.io/testonly cmp stdout why-testonly.txt @@ -56,6 +76,10 @@ cmp go.mod go.mod.orig -- go.mod -- module mymodule require rsc.io/quote v1.5.2 +replace mymodule/nested => ./nested + +-- nested/go.mod -- +module mymodule/nested -- x/x.go -- package x @@ -90,6 +114,9 @@ mymodule/y.test rsc.io/quote rsc.io/sampler golang.org/x/text/language +-- why-replaced-module.txt -- +# mymodule/nested +(main module does not need module mymodule/nested) -- why-testonly.txt -- # rsc.io/testonly mymodule/y diff --git a/src/cmd/link/internal/ld/fallocate_test.go b/src/cmd/link/internal/ld/fallocate_test.go index f463b5b63b3b69..2e753990bd4291 100644 --- a/src/cmd/link/internal/ld/fallocate_test.go +++ b/src/cmd/link/internal/ld/fallocate_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -//go:build darwin || freebsd || linux || (netbsd && go1.25) +//go:build darwin || freebsd || linux || netbsd package ld diff --git a/src/cmd/link/internal/ld/outbuf_bsd.go b/src/cmd/link/internal/ld/outbuf_bsd.go index a1d61aa045a8cb..ede704b0ecc268 100644 --- a/src/cmd/link/internal/ld/outbuf_bsd.go +++ b/src/cmd/link/internal/ld/outbuf_bsd.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. -//go:build freebsd || (netbsd && go1.25) +//go:build freebsd || netbsd package ld diff --git a/src/cmd/link/internal/ld/outbuf_nofallocate.go b/src/cmd/link/internal/ld/outbuf_nofallocate.go index 0207b3988a4f5b..eb8d12538f8898 100644 --- a/src/cmd/link/internal/ld/outbuf_nofallocate.go +++ b/src/cmd/link/internal/ld/outbuf_nofallocate.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. -//go:build !darwin && !freebsd && !linux && !(netbsd && go1.25) +//go:build !darwin && !freebsd && !linux && !netbsd package ld diff --git a/src/embed/embed.go b/src/embed/embed.go index 4d5e418c9024b3..5b99c5e69a6526 100644 --- a/src/embed/embed.go +++ b/src/embed/embed.go @@ -359,12 +359,14 @@ func (f *openFile) Read(b []byte) (int, error) { func (f *openFile) Seek(offset int64, whence int) (int64, error) { switch whence { - case 0: + case io.SeekStart: // offset += 0 - case 1: + case io.SeekCurrent: offset += f.offset - case 2: + case io.SeekEnd: offset += int64(len(f.f.data)) + default: + return 0, &fs.PathError{Op: "seek", Path: f.f.name, Err: fs.ErrInvalid} } if offset < 0 || offset > int64(len(f.f.data)) { return 0, &fs.PathError{Op: "seek", Path: f.f.name, Err: fs.ErrInvalid} diff --git a/src/embed/internal/embedtest/embed_test.go b/src/embed/internal/embedtest/embed_test.go index 875265556f0940..3c69cce21f9059 100644 --- a/src/embed/internal/embedtest/embed_test.go +++ b/src/embed/internal/embedtest/embed_test.go @@ -6,7 +6,9 @@ package embedtest import ( "embed" + "errors" "io" + "io/fs" "reflect" "slices" "testing" @@ -220,6 +222,15 @@ func TestOffset(t *testing.T) { t.Fatal("Seek:", off) } + // Use Seek with an invalid whence. + _, err = seeker.Seek(0, io.SeekEnd+5) + if err == nil { + t.Fatal("Seek: expected error for invalid whence") + } + if !errors.Is(err, fs.ErrInvalid) { + t.Fatalf("Seek: expected fs.ErrInvalid, got %v", err) + } + // Use ReadAt to read the entire file, ignoring the offset. at := file.(io.ReaderAt) got = make([]byte, len(want)) diff --git a/src/runtime/chan.go b/src/runtime/chan.go index 1ebaa0fe011f40..5979616d902002 100644 --- a/src/runtime/chan.go +++ b/src/runtime/chan.go @@ -161,18 +161,10 @@ func chansend1(c *hchan, elem unsafe.Pointer) { chansend(c, elem, true, sys.GetCallerPC()) } -/* - * generic single channel send/recv - * If block is not nil, - * then the protocol will not - * sleep but return if it could - * not complete. - * - * sleep can wake up with g.param == nil - * when a channel involved in the sleep has - * been closed. it is easiest to loop and re-run - * the operation; we'll see that it's now closed. - */ +// chansend sends the element pointed to by ep on channel c. +// A send on a closed channel panics. +// If block == false and the send cannot proceed immediately, it returns false. +// Otherwise, it waits as needed for the send to complete and returns true. func chansend(c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr) bool { if c == nil { if !block { diff --git a/src/runtime/testdata/testgoroutineleakprofile/goker/README.md b/src/runtime/testdata/testgoroutineleakprofile/goker/README.md index e6f8fe23f26c02..c2bd6933dd9157 100644 --- a/src/runtime/testdata/testgoroutineleakprofile/goker/README.md +++ b/src/runtime/testdata/testgoroutineleakprofile/goker/README.md @@ -1708,7 +1708,7 @@ return ### Description -The the lock for the struct svm has already been locked when calling +The lock for the struct svm has already been locked when calling `svm.hotRemoveVHDsAtStart()`. ## Moby/4951 diff --git a/src/runtime/unsafepoint_test.go b/src/runtime/unsafepoint_test.go index 79f0171854191f..0b78606c8b58ec 100644 --- a/src/runtime/unsafepoint_test.go +++ b/src/runtime/unsafepoint_test.go @@ -102,7 +102,7 @@ func TestUnsafePoint(t *testing.T) { if parts[3] == "CMPL" { startedWB = true } - if parts[3] == "MOVQ" && parts[4] == "$0x0," { + if parts[3] == "MOVQ" && (parts[4] == "$0x0," || parts[4] == "X15,") { doneWB = true } } diff --git a/test/codegen/math.go b/test/codegen/math.go index 4eaf811b5d9b55..9317e43c58fc0f 100644 --- a/test/codegen/math.go +++ b/test/codegen/math.go @@ -343,7 +343,7 @@ func outOfBoundsConv(i32 *[2]int32, u32 *[2]uint32, i64 *[2]int64, u64 *[2]uint6 u32[0] = uint32(two41()) // on arm64, this uses an explicit <0 comparison, so it constant folds. // on amd64, this uses an explicit <0 comparison, so it constant folds. - // amd64: "MOVL [$]0," + // amd64: "MOVL X15," u32[1] = uint32(minus1()) // arm64: "FCVTZSD" // amd64: "CVTTSD2SQ" @@ -356,7 +356,7 @@ func outOfBoundsConv(i32 *[2]int32, u32 *[2]uint32, i64 *[2]int64, u64 *[2]uint6 u64[0] = uint64(two81()) // arm64: "FCVTZUD" // on amd64, this uses an explicit <0 comparison, so it constant folds. - // amd64: "MOVQ [$]0," + // amd64: "MOVQ X15," u64[1] = uint64(minus1()) } diff --git a/test/codegen/memcombine.go b/test/codegen/memcombine.go index d3f2e338097193..9e863aab5ce0d2 100644 --- a/test/codegen/memcombine.go +++ b/test/codegen/memcombine.go @@ -1137,7 +1137,7 @@ func zero_byte_2(b1, b2 []byte) { func zero_byte_4(b1, b2 []byte) { _, _ = b1[3], b2[3] // arm64:"MOVW ZR" -"MOVB" -"MOVH" - // amd64:`MOVL [$]0, \([A-Z]+\)` + // amd64:`MOVL X15, \([A-Z]+\)` // 386:`MOVL [$]0, \([A-Z]+\)` // ppc64x:`MOVW ` b1[0], b1[1], b1[2], b1[3] = 0, 0, 0, 0 @@ -1185,12 +1185,12 @@ func zero_byte_2_idx2(b []byte, idx int) { func zero_uint16_2(h1, h2 []uint16) { _, _ = h1[1], h2[1] // arm64:"MOVW ZR" -"MOVB" -"MOVH" - // amd64:`MOVL [$]0, \([A-Z]+\)` + // amd64:`MOVL X15, \([A-Z]+\)` // 386:`MOVL [$]0, \([A-Z]+\)` // ppc64x:`MOVW ` h1[0], h1[1] = 0, 0 // arm64:"MOVW ZR" -"MOVB" -"MOVH" - // amd64:`MOVL [$]0, \([A-Z]+\)` + // amd64:`MOVL X15, \([A-Z]+\)` // 386:`MOVL [$]0, \([A-Z]+\)` // ppc64x:`MOVW` h2[1], h2[0] = 0, 0 @@ -1199,7 +1199,7 @@ func zero_uint16_2(h1, h2 []uint16) { func zero_uint16_4(h1, h2 []uint16) { _, _ = h1[3], h2[3] // arm64:"MOVD ZR" -"MOVB" -"MOVH" -"MOVW" - // amd64:`MOVQ [$]0, \([A-Z]+\)` + // amd64:`MOVQ X15, \([A-Z]+\)` // ppc64x:`MOVD ` h1[0], h1[1], h1[2], h1[3] = 0, 0, 0, 0 // arm64:"MOVD ZR" -"MOVB" -"MOVH" -"MOVW" @@ -1216,11 +1216,11 @@ func zero_uint16_8(h []uint16) { func zero_uint32_2(w1, w2 []uint32) { _, _ = w1[1], w2[1] // arm64:"MOVD ZR" -"MOVB" -"MOVH" -"MOVW" - // amd64:`MOVQ [$]0, \([A-Z]+\)` + // amd64:`MOVQ X15, \([A-Z]+\)` // ppc64x:`MOVD ` w1[0], w1[1] = 0, 0 // arm64:"MOVD ZR" -"MOVB" -"MOVH" -"MOVW" - // amd64:`MOVQ [$]0, \([A-Z]+\)` + // amd64:`MOVQ X15, \([A-Z]+\)` // ppc64x:`MOVD ` w2[1], w2[0] = 0, 0 } diff --git a/test/codegen/memops.go b/test/codegen/memops.go index c7f7d63c0ec16b..167e7d95487399 100644 --- a/test/codegen/memops.go +++ b/test/codegen/memops.go @@ -157,6 +157,36 @@ func idxInt64(x, y []int64, i int) { x[16*i+1] = 77 } +func zero64(x *uint64) { + // amd64: `MOVQ X15, \([A-Z]+[0-9]*\)` + *x = 0 +} + +func zero32Direct(x *uint32) { + // amd64: `MOVL X15, \([A-Z]+[0-9]*\)` + *x = 0 +} + +func zeroIdx8(x []int64, i int) { + // amd64: `MOVQ X15, \([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*8\)` + x[i] = 0 +} + +func zeroIdx1(x []int64, i int) { + // amd64: `MOVQ X15, \([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*1\)` + x[16*i] = 0 +} + +func zero32(x []uint32, i int) { + // amd64: `MOVL X15, \([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\)` + x[i] = 0 +} + +func zero32Idx1(x []uint32, i int) { + // amd64: `MOVL X15, \([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*1\)` + x[4*i] = 0 +} + func idxFloat32(x, y []float32, i int) { var t float32 // amd64: `MOVSS 4\([A-Z]+[0-9]*\)\([A-Z]+[0-9]*\*4\), X[0-9]+` diff --git a/test/codegen/structs.go b/test/codegen/structs.go index f789249ee3b776..cf74385b0f1e87 100644 --- a/test/codegen/structs.go +++ b/test/codegen/structs.go @@ -20,7 +20,7 @@ type Z1 struct { } func Zero1(t *Z1) { // Issue #18370 - // amd64:`MOVUPS X[0-9]+, \(.*\)` `MOVQ \$0, 16\(.*\)` + // amd64:`MOVUPS X[0-9]+, \(.*\)` `MOVQ X15, 16\(.*\)` *t = Z1{} } @@ -29,7 +29,7 @@ type Z2 struct { } func Zero2(t *Z2) { - // amd64:`MOVUPS X[0-9]+, \(.*\)` `MOVQ \$0, 16\(.*\)` + // amd64:`MOVUPS X[0-9]+, \(.*\)` `MOVQ X15, 16\(.*\)` // amd64:`.*runtime[.]gcWriteBarrier.*\(SB\)` *t = Z2{} } diff --git a/test/codegen/zerosize.go b/test/codegen/zerosize.go index 1e93260ef5af1e..a2e653cce08dd3 100644 --- a/test/codegen/zerosize.go +++ b/test/codegen/zerosize.go @@ -12,7 +12,7 @@ package codegen func zeroSize() { c := make(chan struct{}) - // amd64:`MOVQ \$0, command-line-arguments\.s\+56\(SP\)` + // amd64:`MOVQ X15, command-line-arguments\.s\+56\(SP\)` var s *int // force s to be a stack object, also use some (fixed) stack space g(&s, 1, 2, 3, 4, 5) @@ -24,7 +24,7 @@ func zeroSize() { // Like zeroSize, but without hiding the zero-sized struct. func zeroSize2() { c := make(chan struct{}) - // amd64:`MOVQ \$0, command-line-arguments\.s\+48\(SP\)` + // amd64:`MOVQ X15, command-line-arguments\.s\+48\(SP\)` var s *int // force s to be a stack object, also use some (fixed) stack space g(&s, 1, 2, 3, 4, 5) diff --git a/test/fixedbugs/issue80517.go b/test/fixedbugs/issue80517_1.go similarity index 100% rename from test/fixedbugs/issue80517.go rename to test/fixedbugs/issue80517_1.go diff --git a/test/fixedbugs/issue80517_2.go b/test/fixedbugs/issue80517_2.go new file mode 100644 index 00000000000000..a364f1d8a22c21 --- /dev/null +++ b/test/fixedbugs/issue80517_2.go @@ -0,0 +1,33 @@ +// run + +// Copyright 2026 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// The prove pass must not use a fact that only becomes valid after a +// later value executes to simplify an earlier value. Here make([]byte, n) +// teaches prove that n >= 0, but that is only true after the make runs. +// A buggy prove lets that fact travel back in time and rewrites the +// earlier signed shift n>>1 into an unsigned shift, corrupting the result +// for negative n. + +package main + +var sink []byte + +//go:noinline +func trigger(n int) (res int) { + defer func() { recover() }() + if n < 100 { + res = n >> 1 // signed arithmetic shift right + sink = make([]byte, n) // only asserts n >= 0 after this point + } + return +} + +func main() { + if got := trigger(-2); got != -1 { + println("n>>1 =", got, "want -1") + panic("prove miscompiled a signed shift") + } +} diff --git a/test/fixedbugs/issue80517_3.go b/test/fixedbugs/issue80517_3.go new file mode 100644 index 00000000000000..c0a9792cefd7a6 --- /dev/null +++ b/test/fixedbugs/issue80517_3.go @@ -0,0 +1,33 @@ +// run + +// Copyright 2026 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Same time-traveling prove bug as issue80517_2.go, but the victims are a +// signed division and a signed modulo. make([]byte, n) teaches prove that +// n >= 0 only after it runs; a buggy prove lets that fact travel back and +// rewrites the earlier n/4 and n%3 into unsigned operations, corrupting +// the result for negative n. + +package main + +var sink []byte + +//go:noinline +func trigger(n int) (q, r int) { + defer func() { recover() }() + if n < 100 { + q = n / 4 // signed division + r = n % 3 // signed modulo + sink = make([]byte, n) // only asserts n >= 0 after this point + } + return +} + +func main() { + if q, r := trigger(-8); q != -2 || r != -2 { + println("n/4 =", q, "want -2; n%3 =", r, "want -2") + panic("prove miscompiled a signed div/mod") + } +}