From 5403ba1c8688543eceddd7f4646987a29f794234 Mon Sep 17 00:00:00 2001 From: thesyncim Date: Sat, 31 Oct 2026 19:55:19 +0000 Subject: [PATCH] cmd/loopvec-toolexec: rewrite default builds on darwin, ios, android and windows The wrapper leaves a package alone when its compile has -shared, since the nested build of simd and its dependencies would not match its objects. But -shared is also how the go command compiles a position-independent executable, the default build mode on darwin, ios, android and windows. There every default build was left alone, so loopvec-toolexec rewrote nothing on macOS, and seven of the toolexec scripts failed there. On those targets the nested build is position-independent by default too, so -shared is accepted. Elsewhere, where -buildmode=pie must be asked for, it still leaves the package alone, as toolexec_buildmode_pie.txt checks on linux. The go command sets GOOS for the tools it runs. --- cmd/loopvec-toolexec/compile.go | 26 +++++++++++- testdata/toolexec_buildmode_pie.txt | 64 +++++++++++++++++++++++++++++ toolexec.md | 4 +- 3 files changed, 91 insertions(+), 3 deletions(-) create mode 100644 testdata/toolexec_buildmode_pie.txt diff --git a/cmd/loopvec-toolexec/compile.go b/cmd/loopvec-toolexec/compile.go index 13f0d91..42099dc 100644 --- a/cmd/loopvec-toolexec/compile.go +++ b/cmd/loopvec-toolexec/compile.go @@ -34,10 +34,30 @@ type compileArgs struct { importcfg int // index in args of the importcfg path, or -1 files []int // indexes in args of the Go source files // unsupported is set for build modes whose objects the nested simd build - // would not match (race, msan, asan, shared, dynlink, -trimpath). + // would not match (race, msan, asan, shared, dynlink, -trimpath). -shared + // is also what the go command passes for a position-independent + // executable, the default build mode on some targets (see defaultPIE); + // there the nested build uses the same default and matches. unsupported bool } +// defaultPIE reports whether the target builds position-independent +// executables by default (internal/platform.DefaultPIE), so that the go command +// passes -shared to every compile of a default build. The go command sets GOOS +// for the tools it runs. On windows -race turns the default off, but -race is +// unsupported here on its own. +func defaultPIE() bool { + goos := os.Getenv("GOOS") + if goos == "" { + goos = runtime.GOOS + } + switch goos { + case "android", "darwin", "ios", "windows": + return true + } + return false +} + func parseCompileArgs(args []string) compileArgs { c := compileArgs{importcfg: -1} first := len(args) @@ -59,8 +79,10 @@ func parseCompileArgs(args []string) compileArgs { c.lang = args[i+1] case strings.HasPrefix(arg, "-lang="): c.lang = strings.TrimPrefix(arg, "-lang=") - case arg == "-race", arg == "-msan", arg == "-asan", arg == "-shared", arg == "-dynlink": + case arg == "-race", arg == "-msan", arg == "-asan", arg == "-dynlink": c.unsupported = true + case arg == "-shared": + c.unsupported = !defaultPIE() case arg == "-trimpath" && hasValue && strings.Contains(args[i+1], ";"): c.unsupported = true } diff --git a/testdata/toolexec_buildmode_pie.txt b/testdata/toolexec_buildmode_pie.txt new file mode 100644 index 0000000..b088de5 --- /dev/null +++ b/testdata/toolexec_buildmode_pie.txt @@ -0,0 +1,64 @@ +# The go command compiles a position-independent executable with -shared. It is the +# default build mode on darwin, ios, android and windows, where the nested simd build +# is one too, so a default build is rewritten there. Asking for one where it is not +# the default (-buildmode=pie on linux) still leaves the package alone: the nested +# build would not match it. + +# Reference: the scalar program. +exec go run . +cmp stdout want.out + +env GOEXPERIMENT=simd +env GOCACHE=$WORK/gocache +env LOOPVEC_TOOLEXEC_LOG=$WORK/log +env LOOPVEC_TOOLEXEC_LOG_PKGS=example.com/app/vec + +[GOOS:darwin] exec go build -toolexec=$LOOPVEC_TOOLEXEC -o app . +[GOOS:darwin] cmp log rewrote.want + +[GOOS:linux] env LOOPVEC_TOOLEXEC_DEBUG=1 +[GOOS:linux] exec go build -buildmode=pie -toolexec=$LOOPVEC_TOOLEXEC -o app . +[GOOS:linux] cmp log skipped.want + +[GOOS:darwin] exec ./app +[GOOS:darwin] cmp stdout want.out +[GOOS:linux] exec ./app +[GOOS:linux] cmp stdout want.out + +-- go.mod -- +module example.com/app + +go 1.27.1 +-- vec/vec.go -- +package vec + +func AddInt32s(dst, a, b []int32) { + for i := range dst { + dst[i] = a[i] + b[i] + } +} +-- main.go -- +package main + +import ( + "fmt" + + "example.com/app/vec" +) + +func main() { + dst := make([]int32, 21) + a := make([]int32, 21) + b := make([]int32, 21) + for i := range a { + a[i], b[i] = int32(i), int32(2*i) + } + vec.AddInt32s(dst, a, b) + fmt.Println(dst) +} +-- want.out -- +[0 3 6 9 12 15 18 21 24 27 30 33 36 39 42 45 48 51 54 57 60] +-- rewrote.want -- +rewrote 1 loop(s) in example.com/app/vec +-- skipped.want -- +skipped example.com/app/vec: simd unavailable or unsupported build mode diff --git a/toolexec.md b/toolexec.md index a46931d..35cd46f 100644 --- a/toolexec.md +++ b/toolexec.md @@ -28,7 +28,9 @@ Packages are left alone when: `os`, `strconv`, `sync`, `sync/atomic`, `math`, and more). Rewritten code imports `simd`, so rewriting these would create an import cycle. - They use cgo, or the build uses `-race`, `-msan`, `-asan`, `-shared`, - `-dynlink` or `-trimpath`. + `-dynlink` or `-trimpath`. `-shared` is accepted where a position-independent + executable is the default build (darwin, ios, android, windows), since the + nested simd build is one there too. - They are cryptographic: `crypto/...` and `golang.org/x/crypto/...`, vendored copies included. Constant-time code is written so that its running time does not depend on secret data, and a rewrite adds a runtime overlap check, a length