diff --git a/cmd/loopvec-toolexec/compile.go b/cmd/loopvec-toolexec/compile.go index 13f0d91..da8e8eb 100644 --- a/cmd/loopvec-toolexec/compile.go +++ b/cmd/loopvec-toolexec/compile.go @@ -113,7 +113,7 @@ func tryRewrite(tool string, args []string) (out []string, ok bool) { debugf(c.pkg, "skipped %s: simd unavailable or unsupported build mode", c.pkg) return nil, false } - if want := toolchainRelease(); want != builtRelease() { + if want := toolchainRelease(); want == "" || want != builtRelease() { // go/types can only read export data written by the Go release it was // built with. debugf(c.pkg, "skipped %s: loopvec-toolexec was built with %s but the toolchain is %s", c.pkg, builtRelease(), want) diff --git a/cmd/loopvec-toolexec/simd.go b/cmd/loopvec-toolexec/simd.go index 4b5d8fc..cd3c847 100644 --- a/cmd/loopvec-toolexec/simd.go +++ b/cmd/loopvec-toolexec/simd.go @@ -39,14 +39,31 @@ func simdOn(tool string) bool { return simdEnabled } -var releaseRE = regexp.MustCompile(`go1\.[0-9]+`) +// releaseRE matches a Go version as a whole word: a release and its patch +// (go1.27.3), a prerelease (go1.28rc1), a development build (go1.28-6ae0b1f8a9) +// or a vendor's build (go1.27.3-acme). +var releaseRE = regexp.MustCompile(`go1\.[^\s]+`) + +// release returns the Go version in s, a compiler's -V=full output or a +// runtime.Version, without the experiments: the compiler separates them with a +// space (go1.27.3 X:simd), but a program built with an experiment reports its +// version with a dash when the version has none (go1.27.3-X:simd). +func release(s string) string { + v, _, _ := strings.Cut(releaseRE.FindString(s), "-X:") + return v +} -// toolchainRelease returns the Go release (such as go1.28) of the compiler +// toolchainRelease returns the Go version (such as go1.27.3) of the compiler // probed by simdOn. -func toolchainRelease() string { return releaseRE.FindString(toolVersion) } - -// builtRelease returns the Go release this program was built with. -func builtRelease() string { return releaseRE.FindString(runtime.Version()) } +func toolchainRelease() string { return release(toolVersion) } + +// builtRelease returns the Go version this program was built with. It must be +// the toolchain's exact version, patch included: the simd package can change +// in a patch release without go/types seeing it (go1.27.0 declares +// ShiftAllRight(uint64) but compiles it as taking a uint8; go1.27.1 takes a +// uint64), and then the rewritten package type-checks here but does not +// compile. +func builtRelease() string { return release(runtime.Version()) } // build holds the per-build state shared by the wrapper processes of one go // command invocation. It lives in the go command's work directory, so it is diff --git a/cmd/loopvec-toolexec/simd_test.go b/cmd/loopvec-toolexec/simd_test.go new file mode 100644 index 0000000..12e7deb --- /dev/null +++ b/cmd/loopvec-toolexec/simd_test.go @@ -0,0 +1,25 @@ +package main + +import "testing" + +func TestRelease(t *testing.T) { + for _, tt := range []struct{ in, want string }{ + {"go1.27.3", "go1.27.3"}, + {"go1.27.3-X:simd", "go1.27.3"}, + {"go1.27.3-X:simd,fieldtrack", "go1.27.3"}, + {"compile version go1.27.3", "go1.27.3"}, + {"compile version go1.27.3 X:simd\n", "go1.27.3"}, + {"go1.28rc1-X:simd", "go1.28rc1"}, + {"compile version go1.28rc1 X:simd", "go1.28rc1"}, + {"devel go1.28-6ae0b1f8a9 Tue Oct 27 10:00:00 2026 -0700 X:simd", "go1.28-6ae0b1f8a9"}, + {"compile version devel go1.28-6ae0b1f8a9 Tue Oct 27 10:00:00 2026 -0700 X:simd buildID=x/y", "go1.28-6ae0b1f8a9"}, + {"go1.27.3-acme X:simd", "go1.27.3-acme"}, + {"compile version go1.27.3-acme X:simd", "go1.27.3-acme"}, + {"", ""}, + {"devel +6ae0b1f8a9", ""}, + } { + if got := release(tt.in); got != tt.want { + t.Errorf("release(%q) = %q, want %q", tt.in, got, tt.want) + } + } +} diff --git a/testdata/toolexec_toolchain_mismatch.txt b/testdata/toolexec_toolchain_mismatch.txt index 4c9c9a7..6145616 100644 --- a/testdata/toolexec_toolchain_mismatch.txt +++ b/testdata/toolexec_toolchain_mismatch.txt @@ -1,16 +1,24 @@ # go/types can only read export data written by the Go release it was built -# with. When loopvec-toolexec was built with a different release than the -# toolchain running the build, packages are compiled unchanged, and the log -# (with LOOPVEC_TOOLEXEC_DEBUG) says why. +# with, and the simd package can change in a patch release without go/types +# seeing it (go1.27.0 compiles ShiftAllRight as taking a uint8). When +# loopvec-toolexec was built with a different Go version than the toolchain +# running the build, patch included, packages are compiled unchanged, and the +# log (with LOOPVEC_TOOLEXEC_DEBUG) says why. -chmod 0755 compile +chmod 0755 compile patch/compile env LOOPVEC_TOOLEXEC_DEBUG=1 env LOOPVEC_TOOLEXEC_LOG=$WORK/log exec $LOOPVEC_TOOLEXEC $WORK/compile -p example.com/x -importcfg $WORK/b001/importcfg x.go cmpenv stdout stdout.want ! stderr . stdout -count=1 '^fake compile: ' -grep -count=1 '^skipped example\.com/x: loopvec-toolexec was built with go1\.[0-9]+ but the toolchain is go1\.1$' log +grep -count=1 '^skipped example\.com/x: loopvec-toolexec was built with go1\.[^ ]+ but the toolchain is go1\.1$' log + +# Same release, another patch. +exec $LOOPVEC_TOOLEXEC $WORK/patch/compile -p example.com/y -importcfg $WORK/b001/importcfg x.go +stdout -count=1 '^fake compile: -p example\.com/y ' +! stderr . +grep -count=1 '^skipped example\.com/y: loopvec-toolexec was built with go1\.[^ ]+ but the toolchain is go1\.27\.0$' log -- compile -- #!/bin/sh @@ -19,6 +27,13 @@ if [ "$1" = "-V=full" ]; then exit 0 fi echo "fake compile: $@" +-- patch/compile -- +#!/bin/sh +if [ "$1" = "-V=full" ]; then + echo "compile version go1.27.0 X:simd" + exit 0 +fi +echo "fake compile: $@" -- x.go -- package x -- stdout.want --