From 946a32da192c3b4e7c57b955eeb2fae4b69f5028 Mon Sep 17 00:00:00 2001 From: Julian Noble Date: Thu, 6 Aug 2026 19:05:05 +1000 Subject: [PATCH] G-172 drafted (proposed) + the four recipe pins: distributed binaries get a baseline CPU floor The published tclsh9.0.5 runtime family is AVX-512-only. A flagless bin/punk-runtime.cmd fetch hands a fresh checkout the curated win32-x86_64 default (tclsh9.0.5-punk-r2.exe), which on a CPU without AVX-512 raises #UD and is killed with STATUS_ILLEGAL_INSTRUCTION (0xC000001D, errorlevel -1073741795) before writing a byte - no message, no prompt, no error. First reported as a Windows-version difference (25H2 works, 23H2 does not); it is not one. Cause: the recipes called b.standardTargetOptions(.{}) and no build path passed -Dtarget/-Dcpu, so zig resolved cpu_model .determined_by_arch_os by NATIVE detection and ReleaseFast auto-vectorised to the Zen 5 build host. Measured: all six published tclsh9.0.5-* artifacts (punk + plain, r1 + r2, both -bi- variants) carry 2756 zmm-operand instructions each - real EVEX encodings, first site at the front of .text, and Tcl has no runtime CPU dispatch - as do bin/punk905.exe, punk9-dev.exe, punk9_beta.exe, punk9bi_beta.exe, punkdeclare.exe and punkfiledemo.exe. bin/punkzip.exe carries 1724 and bin/punkres.exe 1423, so a bake on such a machine fails too. An older generation (punk901*, punk9.exe, xcritcl.exe, tclsh90s*) is AVX2-only from an earlier host: same defect, milder floor. Confirmed across three machines - Zen 2 dies, Zen 4 and Zen 5 run. Pins applied to all four in-repo recipes (suite_tcl90, suite_tcl86, punkzip, punkres): standardTargetOptions gains .default_target = .{ .cpu_model = .baseline }, so a flagless build is portable while -Dcpu=native remains the developer opt-in and -Dtarget= is unaffected. Floor chosen as baseline (x86-64 v1) - for a Tcl interpreter the codegen delta against v2/v3 is negligible set against the default runtime failing to start. This has to be a recipe DEFAULT rather than a build-command convention: "remember to pass -Dcpu= when publishing" is precisely what failed silently for six artifacts. Verified by zig ast-check on all four recipes, and by reproducing the mechanism with the same toolchain and optimize level on an equivalent recipe (flagless -> x86_64 baseline, zmm=0; -Dcpu=native -> znver5, zmm=805). No suite build was run: all four recipes gate on required_zig = "0.16.0" with a .lt SemVer compare, which rejects the bundled prerelease bin/tools/zig-0.16.0-dev.254 - a separate pre-existing defect, recorded in the goal's Notes and deliberately not fixed here. Nothing is rebuilt or republished, and no shipped surface changes, so no project version bump. Recording the floor in the artifact metadata record, an audit tool, punk-runtime surfacing it on use/run (fetch stays ungated - fetching for another platform is a normal workflow), and the rebuild/republish with the defaults.txt repoint all remain open as G-172. Assisted-by: harness=claude; primary-model=claude-opus-5[1m]; api-location=anthropic.com --- GOALS.md | 4 + goals/G-172-distributed-binary-cpu-floor.md | 177 ++++++++++++++++++++ src/buildsuites/suite_tcl86/README.md | 10 ++ src/buildsuites/suite_tcl86/build86.zig | 11 +- src/buildsuites/suite_tcl90/README.md | 30 ++++ src/buildsuites/suite_tcl90/build905.zig | 19 ++- src/tools/punkres/build.zig | 12 +- src/tools/punkzip/build.zig | 17 +- 8 files changed, 273 insertions(+), 7 deletions(-) create mode 100644 goals/G-172-distributed-binary-cpu-floor.md diff --git a/GOALS.md b/GOALS.md index ba052f30..284843f4 100644 --- a/GOALS.md +++ b/GOALS.md @@ -402,3 +402,7 @@ Detail: goals/G-167-punkargs-version-delta-availability.md Scope: src/make.tcl (bake/bakehouse/vfslibs - VFSPAYLOAD + PACKAGES phases, kit assembly, new explicit materialize/tidy commands); src/make.tcl sync_layouts phase (the mint-side tracked-path write) with its targets src/project_layouts/ and src/modules/punk/mix/#modpod-templates-999999.0a1.0/templates/project_layouts/ plus the workflow_text TERMINOLOGY/DIAGRAM 1b stage-verb assignment; src/vfs/ (payload declarations, _targets staging generalization, README.md + AGENTS.md precedence spec); src/_bake (assembly staging); bin/packages (lib-tier consumption); .gitignore + .fossil-settings/ignore-glob (staging ignore coverage); src/tests/shell/testsuites/punkexe/ (kill-probe + precedence characterization) Detail: goals/G-171-bake-writes-land-untracked.md +### G-172 [proposed] Distributed binaries declare a CPU floor - no shipped artifact is tuned to its build host +Scope: src/buildsuites/suite_tcl90/build905.zig and src/buildsuites/suite_tcl86/build86.zig (flagless target/cpu resolution defaults); src/tools/punkzip/build.zig and src/tools/punkres/build.zig (distributed build-path tools); src/buildsuites/suite_tcl90/tools/family_artifacts.tcl (artifact record emission - the recorded floor field); punkbin artifact repo (external c:/repo/jn/punkbin - win32-x86_64/*.toml sidecars, defaults.txt curation); bin/ (the shipped zig-built exes); src/scriptapps/bin/punk-runtime.* (floor reporting on fetch, floor-vs-host verdict on use/run); scriptlib/developer/ (cpu-floor audit tool) +Detail: goals/G-172-distributed-binary-cpu-floor.md + diff --git a/goals/G-172-distributed-binary-cpu-floor.md b/goals/G-172-distributed-binary-cpu-floor.md new file mode 100644 index 00000000..7bf0e582 --- /dev/null +++ b/goals/G-172-distributed-binary-cpu-floor.md @@ -0,0 +1,177 @@ +# G-172 Distributed binaries declare a CPU floor - no shipped artifact is tuned to its build host + +Status: proposed +Scope: src/buildsuites/suite_tcl90/build905.zig and src/buildsuites/suite_tcl86/build86.zig (flagless target/cpu resolution defaults); src/tools/punkzip/build.zig and src/tools/punkres/build.zig (distributed build-path tools); src/buildsuites/suite_tcl90/tools/family_artifacts.tcl (artifact record emission - the recorded floor field); punkbin artifact repo (external c:/repo/jn/punkbin - win32-x86_64/*.toml sidecars, defaults.txt curation); bin/ (the shipped zig-built exes); src/scriptapps/bin/punk-runtime.* (floor reporting on fetch, floor-vs-host verdict on use/run); scriptlib/developer/ (cpu-floor audit tool) +Goal: Every binary this project distributes runs on any CPU meeting a declared instruction-set floor - the floor is a property of the build recipe rather than of whichever machine happened to run it, it travels with the artifact in its metadata record, and neither a build nor a publication can silently ship host-tuned code. +Acceptance: A flagless build of each distributed-artifact recipe (suite_tcl90, suite_tcl86, punkzip, punkres) on an AVX-512-capable host emits zero instructions above the declared floor - verified by an audit tool that disassembles the produced binaries and reports out-of-floor instructions, carrying the current AVX-512 regression as its fixture; `-Dcpu=native` still yields a host-tuned local build, proving the floor is a default and not a restriction; each published artifact's metadata record states the floor it was built to and punk-runtime surfaces it; and selecting or launching a runtime whose recorded floor the local CPU does not meet produces a named diagnosis instead of a silent 0xC000001D, while fetching for any platform stays ungated. + +## Context + +Reported 2026-08-06: on a freshly pulled checkout, `bin/punk-runtime.cmd fetch` +with no runtime name retrieved the curated default `tclsh9.0.5-punk-r2.exe`, +which exited immediately with no prompt, no error and no output. Two other +runtimes fetched onto the same machine (`tclsh902z.exe`, `tclsfe-x64.exe`) +started normally. The failure was initially read as a Windows-version +difference (25H2 works, 23H2 does not). + +It is not an OS difference. Disassembling `.text` of the artifacts: + +| artifact | zmm sites | ymm sites | built by | +|---|---|---|---| +| `tclsh9.0.5-punk-r2.exe` | 2756 | 617 | zig 0.16.0, ReleaseFast | +| `tclsh9.0.5-r2.exe` (plain) | 2756 | 617 | zig 0.16.0, ReleaseFast | +| `tclsh902z.exe` | 0 | 0 | BAWT / MSYS2 mingw GCC | +| `tclsfe-x64.exe` | 0 | 0 | apnadkarni MSVC | + +The 9.0.5 binaries carry real EVEX-encoded AVX-512 - `vmovdqu64`, +`vpscatterqq`, `vpermt2q`, `vpmovm2q`, `vptestnmq`, `vshufi64x2` - with the +first site at RVA 0x2360, the very front of `.text`. Tcl has no runtime CPU +dispatch, so none of it is guarded. On a CPU without AVX-512 the first one +raises #UD and the process is killed with STATUS_ILLEGAL_INSTRUCTION +(`0xC000001D`, errorlevel -1073741795) before Tcl writes a byte. + +Confirmed against the reporting fleet - the split is exactly the AVX-512 line: + +| host | CPU | uarch | AVX-512 | 9.0.5 runtime | +|---|---|---|---|---| +| is-vmhost-1 (Win11 23H2) | TR PRO 3955WX | Zen 2 | no | dies, errorlevel -1073741795 | +| jcross1 (Win11) | Ryzen 5 7600X | Zen 4 | yes | works | +| build/dev box (Win11 25H2) | TR PRO 9955WX | Zen 5 | yes | works | + +Cause: the recipes called `b.standardTargetOptions(.{})` and no build path +passed `-Dtarget`/`-Dcpu`. With no flag, zig resolves cpu_model +`determined_by_arch_os` by NATIVE detection; on a Zen 5 host that is `znver5`, +and `ReleaseFast` then auto-vectorises freely. Measured with the pinned zig on +an auto-vectorisable C loop: flagless -> `znver5`, zmm=805; `-Dcpu=native` -> +`znver5`, zmm=805; `-Dtarget=x86_64-windows` -> `x86_64`, zmm=0; +`-Dcpu=baseline` -> `x86_64`, zmm=0; `-Dcpu=x86_64_v2` -> `x86_64_v2`, zmm=0. + +The class is wider than the runtime family. Every zig-built binary the project +distributes inherits its build host's instruction set: + +- all six published `tclsh9.0.5-*` artifacts (punk + plain, r1 + r2, both + `-bi-` variants): 2756 zmm each - the whole family, so there is no 9.0.5 + fallback to fetch instead; +- `bin/punkzip.exe` 1724 zmm, `bin/punkres.exe` 1423 zmm - both build-path + tools, so a `bake` on a non-AVX-512 machine dies too; +- `bin/punk905.exe`, `punk9-dev.exe`, `punk9_beta.exe`, `punk9bi_beta.exe`, + `punkdeclare.exe`, `punkfiledemo.exe`: 2756 zmm; +- an older generation (`punk901*.exe`, `punk9.exe`, `xcritcl.exe`, + `tclsh90s*.exe`, `mkzipfix.exe`) carries ~4500 ymm sites and no AVX-512 - + AVX2-only, from an earlier build host. Same defect, milder floor. + +Two things make this worse than a portability nit. The artifact record already +carries `toolchain`, `optimize` and six source-checkout digests but says +nothing about the instruction set, so the one fact that determines whether the +binary can execute is the one fact not recorded. And punkbin `defaults.txt` +points `win32-x86_64` at `tclsh9.0.5-punk-r2.exe`, so a flagless fetch on a +fresh checkout hands every non-AVX-512 machine a dead default runtime - the +project's first contact with a new user fails with no message at all. + +## Approach + +1. Recipe default, not a build-command convention. `standardTargetOptions` + takes `.{ .default_target = .{ .cpu_model = .baseline } }`, so a flagless + build is portable and `-Dcpu=native` is the opt-in for a host-tuned local + build. "Remember to pass `-Dcpu=` when publishing" is precisely what failed + silently for six artifacts and four recipes; the default is what publishes. + (Applied 2026-08-06 to all four recipes - see Progress.) +2. Floor choice: `baseline` (x86-64 v1) for the x86_64 family (user decision + 2026-08-06). For a Tcl interpreter the codegen delta against v2/v3 is + negligible against the cost of the default runtime failing to start. The + win32-ix86 lane (G-130) makes the same choice on its own axis. +3. Record the floor in the artifact metadata: `[provenance]` gains the resolved + cpu model and the floor the recipe declared, emitted by + `family_artifacts.tcl` alongside `toolchain`/`optimize`, and carried in both + the embedded record and the sidecar toml. +4. Audit tool in `scriptlib/developer/` (the advisory-tool tier): disassemble a + binary, classify instructions against a named floor, report anything above + it. The current AVX-512 artifacts are its fixture - the tool must flag them + and must pass a rebuilt one. This is what makes the acceptance criterion + measurable rather than a promise about build habits. +5. punk-runtime surfaces the floor, gating only where execution is implied. + `fetch` stays ungated and reports the floor as information - fetching for + another platform (`-platform

`) is a normal cross-machine workflow and a + local-CPU verdict would be meaningless there (user point 2026-08-06). The + actionable moment is selection and launch: `use` and `run` are already the + local-only actions (`run` explicitly takes no `-platform` because "foreign + binaries are not runnable here"), so that is where a floor the host does not + meet becomes a named diagnosis. `list` annotates rows the local CPU cannot + run. +6. Republication of the affected artifacts as a new revision, and repointing + punkbin `defaults.txt`, is the closing step - sequenced after 1-5 so the + rebuilt family is audited and self-describing before it becomes the default. + +## Alternatives considered + +- Pass `-Dcpu=baseline` at publish time only, leaving recipes native by default + - rejected: it is the same unenforced convention that produced the defect, + and it makes a developer's local build and the published artifact differ in a + way nothing checks. +- `x86_64_v2` or `x86_64_v3` floor - deferred, not rejected. v2 (SSE4.2/POPCNT, + 2008+) excludes nothing realistically in service; v3 (AVX2/BMI2, 2013+) + excludes Intel N-series/Pentium Silver, which have no AVX at all. Revisit per + artifact class if a measured win justifies it; the recipe already accepts + `-Dcpu=`. +- Runtime CPU dispatch in the hot paths - rejected as out of proportion: Tcl + upstream has none, and the goal is that a shipped binary starts everywhere, + not that it is optimal everywhere. +- Detect and report at first run instead of building portably - rejected: the + failure is #UD before `main`, so there is no point at which our own code + could report anything. + +## Notes + +- Related: G-105 - cross-target builds parameterize the same recipe's target; + this goal is the CPU axis of that surface. Whichever lands first, the other + inherits the resolved default_target shape. +- Related: G-130 - the win32-ix86 lane consumes the floor decision on its own + axis (an i686 baseline floor is the same class of choice). +- Related: G-116, G-108 - additional products of the same recipe (tcltls + bi-family battery, the debug `-dbg` tier); both inherit the pin. +- Related: G-142 - curated listing manifests are where a per-artifact floor + becomes visible before download. +- Related: G-147 - fetch-side sibling; a floor field joins the facts a revision + check reads. +- Related: G-137 - punkres RT_VERSION stamping consumes `bin/punkres.exe`, + itself one of the affected binaries. +- Related (archived): G-117 (self-describing runtimes - see + goals/archive/G-117-self-describing-runtimes.md) and G-123 (runtime tiers, + schema v2 - see goals/archive/G-123-thirdparty-runtime-tiers.md) - the + artifact record schema this goal extends with the floor field. +- Related (archived): G-126 (punkzip accelerator), G-128 (punkres stamper) - + the two distributed tools whose recipes carry the same defect. +- Related (archived): G-102 - the suite zig-version pin lives in the same + comptime block as the defect. +- Overlap survey 2026-08-06 (`goals_xref.tcl paths src/buildsuites/suite_tcl90 + bin/punkzip.exe bin/punkres.exe`): surveyed and judged not related in + substance - G-110 (shared-lib extraction cache), G-131 (boot payload + autodetection), G-141, G-157, G-158 (bin/ sibling-file matches only). +- Deliberately outside this contract: all four recipes gate on + `required_zig = "0.16.0"` with a `.lt` SemanticVersion compare, so a + prerelease toolchain (`0.16.0-dev.254+6dd0270a1`, the copy under bin/tools) + sorts below the pin and is rejected by the recipes' own gate. A real defect + in the same lines, but a different one - it blocks rebuilding here until a + released 0.16.0 is materialized. +- Verification note: `llvm-objdump -d --section=.text ` counting + `zmm[0-9]` operands is the quick manual check used throughout the Context + measurements. Linear disassembly of a non-AVX-512 binary yields ~3 false + positives from data misparse; real usage is in the hundreds-to-thousands, so + the distinction is unambiguous at a glance but the audit tool (Approach 4) + should decode properly rather than grep. + +## Progress + +- 2026-08-06: recipe pins applied (Approach 1+2) - `default_target` with + `cpu_model = .baseline` in `src/buildsuites/suite_tcl90/build905.zig`, + `src/buildsuites/suite_tcl86/build86.zig`, `src/tools/punkzip/build.zig` and + `src/tools/punkres/build.zig`, each carrying the finding in-comment. Verified + by `zig ast-check` on all four; the mechanism verified separately with the + same toolchain and optimize level on an equivalent recipe (flagless -> + baseline/zmm=0, `-Dcpu=native` -> znver5/zmm=805). A full suite build was NOT + run - blocked by the version-gate defect noted above. The `b.graph.host` + target sites elsewhere in these recipes are build-time helper tools that are + never distributed and are correct as native. +- Remaining for acceptance: Approach 3 (floor in the artifact record), 4 (audit + tool + fixture), 5 (punk-runtime surfacing and the use/run verdict), 6 + (rebuild, republish, repoint `defaults.txt`). diff --git a/src/buildsuites/suite_tcl86/README.md b/src/buildsuites/suite_tcl86/README.md index 5674dd78..42a9daa1 100644 --- a/src/buildsuites/suite_tcl86/README.md +++ b/src/buildsuites/suite_tcl86/README.md @@ -248,6 +248,16 @@ unset, and the bootstrap driver's nested-build scrub. Keep the two lists in step. NOTE: suite_tcl90 has the same exposure via `TCL9_0_TM_PATH` and is deliberately untouched here (out of this goal's scope). +## Portability floor (G-172) + +`build86.zig`'s `standardTargetOptions` call carries +`.default_target = .{ .cpu_model = .baseline }`, so a flagless `zig build` +produces a baseline x86-64 binary rather than one tuned to the build host. +`-Dcpu=native` opts into a host-tuned local build; `-Dtarget=` is +unaffected. The full finding - a whole published runtime family shipped +AVX-512-only and dying on #UD (`0xC000001D`, no output) on any CPU without it - +is in `../suite_tcl90/README.md` "Portability floor". + ## Flows - FOSSIL DEV FLOW: `tclsh suite.tcl build|test|clean` (or via the make.tcl diff --git a/src/buildsuites/suite_tcl86/build86.zig b/src/buildsuites/suite_tcl86/build86.zig index d57df0d2..ebd4944b 100644 --- a/src/buildsuites/suite_tcl86/build86.zig +++ b/src/buildsuites/suite_tcl86/build86.zig @@ -106,7 +106,16 @@ pub fn build(b: *std.Build) !void { "pinned zon flow, or 'tclsh suite.tcl build' for the fossil dev flow."); } - const target = b.standardTargetOptions(.{}); + //PORTABILITY FLOOR (2026-08-06) - see the same pin in + //../suite_tcl90/build905.zig for the full finding. Without an explicit + //default_target, a flagless `zig build` resolves cpu_model by NATIVE + //detection and ReleaseFast tunes the artifact to the build host; that + //shipped the published tclsh9.0.5 family as AVX-512-only binaries that + //die on #UD (0xC000001D, no output) on any other CPU. -Dcpu=native still + //opts into a host-tuned local build; -Dtarget= is unaffected. + const target = b.standardTargetOptions(.{ + .default_target = .{ .cpu_model = .baseline }, + }); const optimize = b.standardOptimizeOption(.{}); if (target.result.os.tag != .windows) { @panic("suite_tcl86 recipe is windows-only for the G-099 arc (a Tcl 8.6 WINDOWS runtime); cross-target parameterization is G-105's goal"); diff --git a/src/buildsuites/suite_tcl90/README.md b/src/buildsuites/suite_tcl90/README.md index 91fb059c..1df656ab 100644 --- a/src/buildsuites/suite_tcl90/README.md +++ b/src/buildsuites/suite_tcl90/README.md @@ -273,6 +273,36 @@ Zig object caches are kept **per zig version** (`.zig-cache-` inside th staged build dir): caches must never be shared across zig versions (observed producing stale/duplicate artifacts at link time). +## Portability floor (G-172) + +The recipe's `standardTargetOptions` call carries +`.default_target = .{ .cpu_model = .baseline }`, so a flagless `zig build` +produces a **baseline x86-64** binary. This is deliberate and load-bearing: with +the stock `.{}`, zig resolves `cpu_model` by NATIVE detection and `ReleaseFast` +auto-vectorises to whatever the build host happens to have. That shipped the +entire published tclsh9.0.5 family (punk + plain, r1 + r2, both `-bi-` variants) +as AVX-512-only binaries carrying 2756 zmm-operand instructions each - Tcl has no +runtime CPU dispatch, so on any CPU without AVX-512 they die on #UD with +STATUS_ILLEGAL_INSTRUCTION (`0xC000001D`) before writing a byte: no message, no +prompt, just an immediate exit. Measured 2026-08-06 across three machines - Zen 2 +dies, Zen 4 and Zen 5 run. + +- `-Dcpu=native` opts a developer into a host-tuned local build. +- `-Dcpu=` (e.g. `x86_64_v2`) picks a different floor. +- `-Dtarget=` cross-compiles as before, unaffected. + +A distributed artifact must never be built for its build host, and this has to +stay a recipe DEFAULT rather than a build-command convention - "remember to pass +`-Dcpu=` when publishing" is exactly what failed silently. Audit any produced +binary with `llvm-objdump -d --section=.text ` and look for `zmm`/`ymm` +operands (linear disassembly of a clean binary yields ~3 false positives from +data misparse; real usage runs to the hundreds or thousands). + +The same pin is applied in `../suite_tcl86/build86.zig`, `src/tools/punkzip` and +`src/tools/punkres`. Remaining work - recording the floor in the artifact +metadata record, an audit tool, punk-runtime surfacing it, and rebuilding and +republishing the affected artifacts - is G-172. + ## Layout - `build905.zig` - the zig recipe (verbatim lineage copy - see Lineage). Expects its diff --git a/src/buildsuites/suite_tcl90/build905.zig b/src/buildsuites/suite_tcl90/build905.zig index f682f591..b8606bd7 100644 --- a/src/buildsuites/suite_tcl90/build905.zig +++ b/src/buildsuites/suite_tcl90/build905.zig @@ -100,7 +100,24 @@ pub fn build(b: *std.Build) !void { "pinned zon flow, or 'tclsh suite.tcl build' for the fossil dev flow."); } - const target = b.standardTargetOptions(.{}); + //PORTABILITY FLOOR (2026-08-06). default_target pins cpu_model to baseline; + //without it a flagless `zig build` resolves cpu_model .determined_by_arch_os + //by NATIVE detection, so ReleaseFast auto-vectorizes to whatever the build + //host happens to have. That shipped the entire published tclsh9.0.5 family + //(punk + plain, r1 + r2, both -bi- variants) as AVX-512-only binaries - + //2756 zmm-operand instructions each, first site at the front of .text, and + //Tcl has no runtime CPU dispatch. On any CPU without AVX-512 they die on + //#UD with STATUS_ILLEGAL_INSTRUCTION (0xC000001D) before writing a byte: + //no message, no prompt. Measured 2026-08-06 across three machines - Zen 2 + //dies, Zen 4 and Zen 5 run. A DISTRIBUTED artifact must not be built for + //the machine that happened to build it. + //-Dcpu=native still opts a developer into a host-tuned local build, -Dcpu= + // picks any other floor, and -Dtarget= cross-compiles as + //before. This must stay a recipe DEFAULT, not a build-command convention: + //"remember to pass -Dcpu= when publishing" is exactly what failed silently. + const target = b.standardTargetOptions(.{ + .default_target = .{ .cpu_model = .baseline }, + }); const optimize = b.standardOptimizeOption(.{}); diff --git a/src/tools/punkres/build.zig b/src/tools/punkres/build.zig index 9e795d68..640145dd 100644 --- a/src/tools/punkres/build.zig +++ b/src/tools/punkres/build.zig @@ -13,7 +13,17 @@ comptime { } pub fn build(b: *std.Build) void { - const target = b.standardTargetOptions(.{}); + //PORTABILITY FLOOR (2026-08-06) - punkres.exe is a distributed build-path + //tool, so it must not be tuned to whichever machine built it. Without an + //explicit default_target the default cpu_model is NATIVE and ReleaseFast + //auto-vectorizes to the build host; the shipped bin/punkres.exe carries + //1423 zmm-operand instructions from that default and dies on #UD + //(0xC000001D, no output) on any CPU without AVX-512. Full finding in + //src/buildsuites/suite_tcl90/build905.zig. -Dcpu=native still opts into a + //host-tuned local build; -Dtarget= is unaffected. + const target = b.standardTargetOptions(.{ + .default_target = .{ .cpu_model = .baseline }, + }); const mode = b.standardOptimizeOption(.{}); const exe_mod = b.createModule(.{ diff --git a/src/tools/punkzip/build.zig b/src/tools/punkzip/build.zig index 1d3cac50..903576ad 100644 --- a/src/tools/punkzip/build.zig +++ b/src/tools/punkzip/build.zig @@ -17,10 +17,19 @@ comptime { pub fn build(b: *std.Build) void { // Standard target options allows the person running `zig build` to choose - // what target to build for. Here we do not override the defaults, which - // means any target is allowed, and the default is native. Other options - // for restricting supported target set are available. - const target = b.standardTargetOptions(.{}); + // what target to build for. Any target is allowed; the default is the + // native ARCH/OS but a BASELINE cpu - punkzip.exe is a distributed + // build-path tool, so it must not be tuned to whichever machine built it. + // PORTABILITY FLOOR (2026-08-06): with the stock `.{}` the default is + // native CPU too, and ReleaseFast then auto-vectorizes to the host's + // instruction set - the measured cause of AVX-512-only artifacts that die + // on #UD (0xC000001D, no output) on non-AVX-512 machines. The shipped + // bin/punkzip.exe carries 1724 zmm-operand instructions from that default. + // See src/buildsuites/suite_tcl90/build905.zig for the full finding. + // -Dcpu=native still opts into a host-tuned local build. + const target = b.standardTargetOptions(.{ + .default_target = .{ .cpu_model = .baseline }, + }); // Standard release options allow the person running `zig build` to select // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.