Skip to content

Make the architecture options mean what they say - #9

Merged
IvanaGyro merged 12 commits into
mainfrom
claude/arm64-fine-tune-flag-rzdq81
Jul 30, 2026
Merged

IvanaGyro merged 12 commits into
mainfrom
claude/arm64-fine-tune-flag-rzdq81

Conversation

@IvanaGyro

@IvanaGyro IvanaGyro commented Jul 30, 2026 •

Copy link
Copy Markdown
Member

Follows #6, which added the CI matrix and fixed -march=x86-64 and -mfpu=neon on non-x86 targets. This does the same for the remaining options: each one states an ISA floor, is opt-in, is rejected on a target that cannot honour it, and is checked by CI.

Status

Run 30549861824 on 4c09d59: linux-x64, linux-arm64, macos-x64, macos-arm64, windows-x64, windows-arm64 — 6/6 pass.

Problem

Four separate defects, none of which any CI job covered:

1. Default builds were keyed to the machine that compiled them. HPTT is redistributed, but the Apple Silicon branch appended -mcpu=native, the Apple GNU branch -mtune=native, ENABLE_IBM -mtune=native, and the Intel branch -xhost — all with FINE_TUNE=OFF, whose own help text promises portable binaries. -march=native/-mcpu=native select the ISA of the build host, not an architecture: on this Cascade Lake builder -march=native emits kmovb, kortestb and vblendmps, so the artifact needs AVX-512 and faults on any AMD Zen 1–3 or any Intel 12th-gen-or-later consumer part, all of which are x86_64.

2. FINE_TUNE=ON was a no-op on four toolchains. GNU on macOS took an APPLE branch that ignored it; ENABLE_IBM=ON excluded it via elseif(NOT ENABLE_IBM); MSVC and IntelLLVM (icpx, whose compiler id is not Intel) matched no branch at all. On Apple Silicon it was worse than absent — -march=native landed on the same command line as the -mcpu=native above it, two host-tuning flags with different meanings.

3. The same default configuration behaved differently on the three arm64 platforms. ENABLE_ARM defaulted OFF, but the APPLE branch defined HPTT_ARCH_ARM regardless of the options, so macOS arm64 got the Neon kernels and REGISTER_BITS=128 while Linux and Windows arm64 got the scalar path and 256 — from an identical command line. Kernel selection lived in two places at once, and the APPLE one won even against an explicit -DENABLE_ARM=OFF.

4. The ISA options accepted architectures they cannot serve. -DENABLE_ARM=ON on x86 put -mfpu=neon and Neon intrinsics on an x86 command line and failed deep in the build with an unrelated-looking error.

benchmark/reference.cpp also did not compile: std::conj on an arithmetic argument returns a complex type since C++11, so both real instantiations failed and the benchmark could not be built at all.

Fix

All four options default OFF on every platform, so a default build is the portable one everywhere and each departure from portability is named by whoever runs the build. The target architecture's only say over them is to reject one it cannot honour:

option assumed floor flags wrong architecture
FINE_TUNE=ON the build host, every arch -march=native (x86) / -mcpu=native (ARM, POWER) / -xhost (ICC, ICX), chosen by compile probe n/a
ENABLE_AVX=ON x86-64-v3 (AVX, AVX2, FMA) -march=x86-64-v3 -mtune=generic, or /arch:AVX2 under MSVC FATAL_ERROR
ENABLE_ARM=ON ARMv8-A arm64 none — Advanced SIMD is mandatory FATAL_ERROR
ENABLE_IBM=ON POWER -maltivec -mabi=altivec FATAL_ERROR

The three ISA options are mutually exclusive by construction rather than by a separate rule: each names one architecture, the architecture predicates are mutually exclusive, so at most one survives validation on any given target. The validations sit directly beneath the option() calls and all three always run — they cannot live inside the if/elseif selection chain, because a check in one branch never runs once an earlier option has matched, and -DENABLE_AVX=ON -DENABLE_ARM=ON would then take the AVX branch and drop ENABLE_ARM in silence.

The host-tuning flag is probed, not assumed, because no single spelling works: Clang rejects -mcpu= on x86 outright, GCC only warns that it is deprecated (so a bare probe would pick a flag that sets scheduling and nothing else), and -march= does not exist on POWER. Architecture fixes the candidate order, check_cxx_compiler_flag picks the first the toolchain accepts, and a toolchain accepting none says so through message(WARNING) instead of dropping the request silently — which is MSVC on both architectures, the only remaining case with no host-tuning flag at all.

-mfpu=neon is gone. It is a 32-bit-ARM-only spelling, and with ENABLE_ARM assuming ARMv8-A, 32-bit ARM is refused by the option rather than silently opted in. Verified: __ARM_NEON is predefined on aarch64-linux-gnu but not on armv7a-linux-gnueabihf or armv6-linux-gnueabihf.

macOS stops choosing kernels for itself. The APPLE branch appended HPTT_ARCH_AVX/HPTT_ARCH_ARM regardless of the options, which would override an explicit -DENABLE_AVX=OFF. Its arch-independent optimization flags stay; kernel selection goes through the options on macOS exactly as on every other platform, which is what makes the three arm64 platforms agree. (An Intel Mac can always take ENABLE_AVX=ON — the oldest Intel Mac any supported macOS release runs on is a 2013 Haswell — but it is opt-in there like everywhere else.)

Under MSVC the architecture comes from MSVC_CXX_ARCHITECTURE_ID, not CMAKE_SYSTEM_PROCESSOR. CMake derives the latter from the compiler only for WindowsCE (Modules/CMakeDetermineSystem.cmake); every other build copies the host processor into it, so vcvarsall x64_arm64 with the Ninja generator reports AMD64 while cl.exe targets ARM64 — and CMAKE_CROSSCOMPILING is not set either, because CMake clears it unless CMAKE_SYSTEM_NAME was given. Without this, the new validation would reject a cross build that previously worked.

FINE_TUNE moved below the ISA blocks. The compiler honours the last -march on the command line, so -march=native must come after the v3 floor ENABLE_AVX sets, or FINE_TUNE=ON would be silently downgraded to v3.

Benchmark

The v3 floor for ENABLE_AVX is measured, not assumed. Five variants of libhptt differing only in HPTT's own flags, benchmark TU compiled once and linked against each — valid because execute_expert is explicitly instantiated in transpose.cpp, so kernels take the library's flags. HPTT's own harness (best-of-5, 100 MB cache trash between reps), 4 threads, GiB/s, all on one 4-vCPU Cascade Lake host in one session:

case base v3 AVX only v3+AVX native
2D 4096² 13.05 14.19 25.75 27.05 21.80
2D 7264² 12.51 12.60 19.92 25.84 25.63
2D 43408×1216 13.47 12.81 28.84 30.92 31.05
3D perm210 13.89 13.53 21.28 27.12 28.88
3D perm021 44.42 49.09 49.85 44.57 47.44
5D perm04213 47.56 43.78 47.57 47.05 45.18

Three conclusions. Raising -march alone buys nothing (v3 ≈ base) despite unlocking 66,835 ymm instructions — auto-vectorization is not the lever. HPTT_ARCH_AVX at a v3 floor is the whole win, ~1.8–2.1x, but only on genuine transposes; permutations whose inner stride is already 1 are memory-bound and show no ISA sensitivity. AVX-512 adds nothing — native is level with v3+AVX and sometimes slower.

Caveat: this host is a shared 4-vCPU cloud instance and the bandwidth-bound rows vary up to 2.6x between identical runs, so those were re-measured with repeats. Treat the ~2x as reproducible and the exact figures as indicative.

Testing

New CI coverage, on top of the existing per-platform build + find_package consumer runs:

  • the default build is portable — greps compile_commands.json for -m(arch|cpu|tune)=native, -xhost, and any HPTT_ARCH_*, failing with the leak named. Runs on all six platforms; verified to fail on main, which emitted -mcpu=native and -xhost unconditionally. The ISA half is needed because the scalar fallback is correct, so an option turning itself on changes nothing any other check in the job looks at.
  • each architecture's ISA option builds and runs — -DENABLE_ARM=ON on the ARM64 runners, -DENABLE_AVX=ON on the x86 ones, in one step now that the two are symmetric. The macro is grepped for because a scalar fallback would pass the consumer either way, and the consumer then runs against the ISA build, so it is checked for correct results rather than only for compiling. This is first coverage of the /arch:AVX2 path under MSVC.
  • FINE_TUNE=ON builds and lands a flag — building catches the hard-error case, grepping catches the silent no-op a green build would hide; skipped on Windows, which has no such flag. The consumer runs against that build tree too.
  • the ISA options reject the wrong architecture — negative test requiring configure to fail with the diagnostic. This caught a real bug on its first run: the checks were originally inside the if/elseif chain, and ENABLE_AVX defaulted ON on an Intel Mac at that point, so -DENABLE_ARM=ON passed the AVX branch's x86 check and was silently ignored. Fixed in 9c1dfb0 by hoisting all three checks above the chain.

The fine-tuned objects are compiled without ccache, and that is load-bearing rather than tidiness. ccache keys on the literal flag string and cannot know which CPU -march=native resolved to, so a cache restored from a different ubuntu-latest machine served objects built for another CPU: the consumer died with Illegal instruction (exit 132) while ccache reported Hits: 32/32. That is precisely the shipped-artifact failure this PR is about, reproduced by the build cache on real hardware.

Note for review

Twelve commits spanning four separable concerns — FINE_TUNE/host-tuning (296d130–db61f75), the arm64 target predicate (2e893eb), the ISA floors and their validation (606a0f3, 9c1dfb0, 84f0567), and the uniform opt-in defaults (192a014) — plus the benchmark fix (c75e91e), the ccache fix (dfc049b) and a README line (4c09d59). Happy to split into stacked PRs if you would prefer to review them apart.

One thing to know when reading the log: 2e893eb introduces the HPTT_TARGET_IS_ARM64 predicate and takes ENABLE_ARM ON by default for arm64; 192a014 later settles on uniform opt-in and takes that default back out. The predicate is what the validation depends on and survives; the default does not. Net effect at the tip is the predicate plus a uniform OFF.

Two pre-existing warts left untouched, both worth deciding separately: macOS forces -O3 -ffast-math -funroll-loops -ftree-vectorize regardless of CMAKE_BUILD_TYPE, so a macOS Debug build silently gets -O3 and -ffast-math changes FP semantics on one platform only; and the legacy Makefiles (root, benchmark/, testframework/) still hardcode -march=native/-xhost with no opt-in, with make arm still adding -mfpu=neon that GCC rejects on aarch64.

@gemini-code-assist

Copy link
Copy Markdown

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 40387c383e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread CMakeLists.txt Outdated
IvanaGyro and others added 12 commits July 30, 2026 14:02
Nothing in CI configures with FINE_TUNE=ON, so the option's per-platform
behaviour is unverified on all six targets. It is the configuration most
likely to break silently: the flag that targets the host CPU is spelled
differently per architecture and compiler, so a wrong spelling either
fails the build or is dropped with no effect on the generated code.

Add a FINE_TUNE=ON build to each matrix job, alongside the existing
HPTT_BUILD_SHARED=OFF one, and check it two ways. Building catches the
hard-error case. Grepping build-fine-tune/compile_commands.json for a
host-tuning flag catches the silent no-op, which a green build would
otherwise hide; the check is skipped on Windows because MSVC has no such
flag on any architecture. The downstream consumer from
tests/downstream_find_package then runs against that build tree, so the
fine-tuned library is also checked for correct results rather than only
for compiling.

Co-Authored-By: Claude <noreply@anthropic.com>
FINE_TUNE was implemented as one flag per compiler family: -march=native
for GNU and Clang, -xhost for Intel. That structure leaves the option
either wrong or absent on several targets:

* On AArch64 the flag that selects the host ISA together with its
  scheduling model is -mcpu=native. -march= there names an architecture
  revision only, so it asks for something narrower than intended.
* On POWER, -march= does not exist ("unsupported option '-march=' for
  target 'powerpc64le-...'"). FINE_TUNE was excluded from the ENABLE_IBM
  path anyway, silently doing nothing.
* GNU on Apple took an APPLE branch that ignored FINE_TUNE entirely and
  applied -mtune=native unconditionally instead, so FINE_TUNE=ON never
  reached -march=native there.
* MSVC and IntelLLVM (icpx, whose compiler id is not "Intel") matched no
  branch at all, so FINE_TUNE=ON was a silent no-op on those toolchains.
* On Apple Silicon FINE_TUNE=ON put -march=native on the same compile
  line as the -mcpu=native the APPLE block adds below, leaving two
  host-tuning flags with different meanings to fight over the target.

Choose the flag by what the target accepts instead. The architecture
fixes the order the candidates are offered in -- -march=native first on
x86, -mcpu=native first elsewhere, -mtune=native last everywhere -- and
check_cxx_compiler_flag decides which one the toolchain in hand really
takes. Both halves are needed: probing alone would pick -mcpu=native on
x86 GCC, which only warns that the flag is deprecated while setting
nothing but scheduling, and Clang rejects it outright ("unsupported
option '-mcpu=' for target 'x86_64-...'"). A compiler that accepts no
candidate now says so through message(WARNING) rather than dropping the
request without a word, and CMAKE_CROSSCOMPILING warns that `native`
reads the build machine, not the target.

-xhost becomes a FINE_TUNE candidate rather than an unconditional flag.
It is Intel's spelling of exactly what FINE_TUNE asks for -- code for the
highest ISA the compiling host supports -- so applying it with
FINE_TUNE=OFF made every Intel build non-portable against the option's
documented default.

On x86 GCC the emitted flags become -march=native alone, where they were
-march=native -mtune=native. That pair is redundant: `g++ -march=native
-Q --help=target` reports -mtune=cascadelake on this host, identical to
passing -mtune=native explicitly, `-Q --help=target` shows no difference
in any target option between the two, and the two flag sets compile
src/plan.cpp to byte-identical assembly.

Co-Authored-By: Claude <noreply@anthropic.com>
FINE_TUNE defaults to OFF and warns that turning it on produces
non-portable binaries, but two paths tuned for the build machine's CPU
without consulting it.

The Apple Silicon branch appended -mcpu=native. That sets the target ISA,
not just scheduling, and Apple Silicon generations do not share one ISA,
so an HPTT built on an M4 could fault on an M1 -- reached by anyone
building on a Mac, including wheel and package builds, with nothing in
the configuration asking for it. It also collided with FINE_TUNE=ON,
which added a second, differently spelled host-tuning flag to the same
compile line.

The ENABLE_IBM branch appended -mtune=native. -maltivec and -mabi=altivec
stay: AltiVec is a baseline every supported POWER chip has, so those two
remain portable across the architecture, while the -mtune= value is
specific to the chip that ran the build.

FINE_TUNE=ON restores host tuning on both platforms, now through the
single flag selected for the target.

Co-Authored-By: Claude <noreply@anthropic.com>
HPTT is distributed as a binary, so the configuration users actually
receive must not depend on the machine that produced it. A `native`-tuned
artifact targets the ISA of the build host and can fault on a different
CPU of the same architecture, and nothing in CI states that the default
configuration stays clear of that.

Grep build/compile_commands.json for -march/-mcpu/-mtune=native and
-xhost right after the default build, and fail with the offending flag
named. FINE_TUNE=ON is the documented way to ask for host tuning; this
asserts it is the only way to arrive there. The check runs on every
platform, MSVC included, because "no host-tuning flag" is the expected
answer everywhere -- unlike the FINE_TUNE=ON check further down, which
has to skip Windows for want of any such flag to find.

Verified to fail before the flags were gated: on the previous revision
the Apple Silicon branch appended -mcpu=native and the Intel branch
appended -xhost with no reference to FINE_TUNE, so macos-arm64 and any
ICC build would have tripped this step.

Co-Authored-By: Claude <noreply@anthropic.com>
ENABLE_ARM defaulted OFF everywhere, so a default build on aarch64 took
the generic scalar path with REGISTER_BITS=256 -- a 256-bit blocking
factor on a machine whose vector registers are 128 bits wide. The Neon
micro-kernels in src/transpose.cpp were reachable only by passing
-DENABLE_ARM=ON by hand.

Nothing about that default was protecting portability. Advanced SIMD is
mandatory in every ARMv8-A implementation, which is why there is no
`-mfpu` option on those targets to switch it on with: a CPU that can run
the library at all already has the instructions. So ON is correct on
aarch64 even for a redistributed binary, and the option keeps defaulting
OFF on 32-bit ARM, where Neon really is an optional extension.

Apple Silicon already had this: the APPLE branch appends HPTT_ARCH_ARM
regardless of ENABLE_ARM, so macOS arm64 has been using the Neon kernels
while Linux aarch64 and Windows ARM64 used the scalar ones from the same
default configuration. CMake de-duplicates COMPILE_DEFINITIONS, so the
two paths appending it together put it on the compile line once.

Hoist the architecture tests to two predicates above the options, since
the ENABLE_ARM default, the baseline -march and the host-tuning probe
order all key off the same two questions; the -mfpu=neon guard now reads
through HPTT_TARGET_IS_ARM64 rather than repeating the regex.

The ARM CI jobs drop their explicit -DENABLE_ARM=ON, so the downstream
consumer exercises the Neon kernels through the configuration a user
actually gets. A new step asserts HPTT_ARCH_ARM reaches the compile line
on ARM64 runners: scalar HPTT is correct, only slower, so a regression in
this default would otherwise leave every check in the job green.

Co-Authored-By: Claude <noreply@anthropic.com>
benchmark/reference.cpp does not compile. transpose_ref is explicitly
instantiated for float and double, and its conjugating branch reads

    B_[i] = alpha * std::conj(A_[i * strideAinner]);

Since C++11 the std::conj overload taking an arithmetic argument returns
std::complex<T>, and std::complex<double> has no conversion back to
double, so both real instantiations fail:

    error: cannot convert 'std::complex<double>' to 'double' in assignment

Both element types HPTT benchmarks by default are real -- defines.h
typedefs floatType to float -- so the benchmark could not be built at all.

Conjugation is the identity on reals, so dispatch on the element type
through a conj_ref helper that stays inside floatType, overloaded for
std::complex<T>. The complex instantiations keep calling std::conj and
the real ones now compile.

Co-Authored-By: Claude <noreply@anthropic.com>
The three ENABLE_* options each imply an assumption about the target, but
none of them said which, and none checked. ENABLE_ARM=ON on an x86 target
put `-mfpu=neon` and Neon intrinsics on an x86 compile line and failed
deep in the build; ENABLE_AVX=ON on arm64 emitted `-mavx` the same way.

State the floor for each option and enforce it at configure time:

* ENABLE_AVX assumes x86-64-v3 -- AVX, AVX2 and FMA -- and is refused on a
  non-x86 target. The floor is now `-march=x86-64-v3` (GCC 11+/Clang 12+;
  `-mavx -mavx2 -mfma` where the level name is unknown) or `/arch:AVX2`
  under MSVC, replacing the bare `-mavx` that only guaranteed AVX.
  Benchmarked on this tree: v3 plus the AVX kernels reaches 25-31 GiB/s
  against 12-14 for the scalar default on the 2D and 3D-perm210 cases,
  while `-mavx` alone leaves the non-kernel paths unvectorized and
  AVX-512 adds nothing to a 256-bit transpose kernel.
* ENABLE_ARM assumes an ARMv8-A arm64 target and is refused anywhere else,
  32-bit ARM included. Advanced SIMD is mandatory in ARMv8-A, so no flag
  selects it and `-mfpu=neon` disappears: it is a 32-bit-only spelling
  that aarch64 rejects, and 32-bit ARM is no longer in scope for the
  option.
* ENABLE_IBM assumes a POWER target, matched by a new
  HPTT_TARGET_IS_POWER predicate, and is refused elsewhere.

FINE_TUNE moves below these blocks. The compiler honours the last -march
on the command line, so `-march=native` has to be appended after any
baseline -- otherwise ENABLE_AVX's v3 floor would silently downgrade
FINE_TUNE=ON to v3. The x86-64 baseline is skipped when ENABLE_AVX sets a
higher one, leaving one -march and a generic -mtune rather than two.

macOS stops choosing kernels for itself. The APPLE branch appended
HPTT_ARCH_AVX or HPTT_ARCH_ARM regardless of the options, which would now
override an explicit -DENABLE_AVX=OFF. Its arch-independent optimization
flags stay; kernel selection goes through the options, whose defaults
already match Apple hardware: ENABLE_ARM=ON on arm64, and ENABLE_AVX=ON
on an Intel Mac, where the v3 floor is safe because the oldest Intel Mac
any supported macOS runs on is a 2013 Haswell.

CI gains a negative test -- the option that cannot apply to the runner
must fail configure with the diagnostic -- and an ENABLE_AVX=ON build plus
consumer run on the x86 jobs, covering MSVC's /arch:AVX2 spelling that no
job exercised before.

Co-Authored-By: Claude <noreply@anthropic.com>
The architecture checks sat inside the if/elseif chain that picks the
micro-kernels, so only the branch that matched ever ran its check. With
ENABLE_AVX defaulting ON on an Intel Mac, `-DENABLE_ARM=ON` there took the
ENABLE_AVX branch, passed its x86 check, and left ENABLE_ARM accepted and
silently ignored -- the exact case the option is supposed to reject. The
macos-x64 job caught it.

Hoist all three checks above the chain so each option is validated on its
own regardless of which one ends up selecting kernels. Rejecting each
option on the wrong architecture also makes two of them on at once
impossible to configure, since ENABLE_AVX requires x86 and ENABLE_ARM
requires arm64.

Also fix the path the ENABLE_AVX CI step used. It assigned
`AVXDIR=${{ github.workspace }}/build-avx` unquoted, and quote removal
strips the backslashes of `D:\a\hptt\hptt` on Windows, so the step looked
for `D:ahptthptt/build-avx/compile_commands.json` and failed there. The
directory now comes from the job's env block alongside FINE_TUNE_DIR,
which is how the other steps already avoid this.

Co-Authored-By: Claude <noreply@anthropic.com>
CMAKE_SYSTEM_PROCESSOR does not name the target on an MSVC cross build.
Modules/CMakeDetermineSystem.cmake derives it from the compiler only for
WindowsCE; every other configuration copies CMAKE_HOST_SYSTEM_PROCESSOR
into it. So `vcvarsall x64_arm64` with the Ninja generator and no
toolchain file reports AMD64 while cl.exe targets ARM64, and
CMAKE_CROSSCOMPILING does not flag it either, because CMake clears that
unless CMAKE_SYSTEM_NAME was supplied.

The architecture predicates read that variable, so such a build was
classified as x86: the default omitted the Neon kernels, and -DENABLE_ARM=ON
now hit the wrong-architecture FATAL_ERROR even though it configured and
compiled before -- MSVC never received `-mfpu=neon`, being neither GNU nor
Clang, so HPTT_ARCH_ARM alone was enough. That is a regression introduced
with those checks.

Prefer MSVC_CXX_ARCHITECTURE_ID, which CMake fills from the compiler's own
_M_* macros and therefore names the target. It is set only for MSVC and
clang-cl, so CMAKE_SYSTEM_PROCESSOR remains the source everywhere else.

The x86 pattern gains MSVC's `x64` and `X86` spellings, without which a
native Windows x64 build would stop matching once the id becomes the
source. MSVC's `IA64` is Itanium and stays excluded, as does 32-bit `ARM`;
`ARM64EC` matches the 64-bit ARM pattern already. All fourteen spellings
the two sources can produce were checked against the patterns.

The cross build itself is unverified here: no MSVC toolchain is available
in this environment, and CI's windows-arm64 runner is native ARM64 rather
than an x64 host cross-compiling. What CI does cover is that the new
source is correct natively on both Windows jobs -- x64 must keep
satisfying the x86 pattern for the ENABLE_AVX step, and ARM64 must keep
satisfying the arm64 pattern for the Neon-default check.

Co-Authored-By: Claude <noreply@anthropic.com>
The FINE_TUNE consumer died with SIGILL on linux-x64 while ccache reported
32 hits out of 32 calls. ccache keys a compilation on the literal flag
string, and `-march=native` does not record which CPU it resolved to, so an
object compiled on one machine is served as a hit for the same flag on
another. The hosted runner fleet is not homogeneous, so the cache restored
from an earlier run handed the job a libhptt built for a different CPU and
the consumer executed instructions this one does not implement.

That is the failure this branch exists to prevent, arriving through the
build cache instead of through a release artifact, so the fix is to compile
the fine-tuned objects fresh rather than to teach the cache a better lie.
Drop the compiler launchers from that one configure.

Everything else keeps ccache. The default, ENABLE_AVX and static-only
builds name their architecture explicitly -- `-march=x86-64`,
`-march=x86-64-v3`, `/arch:AVX2` -- so an object is valid on any machine
that produced a hit. The consumer keeps it too: HPTT's architecture flags
are PRIVATE, so consumer.cpp is compiled without them and only links the
freshly built library.

Co-Authored-By: Claude <noreply@anthropic.com>
ENABLE_AVX defaulted ON when targeting an Intel Mac and ENABLE_ARM
defaulted ON on any 64-bit ARM target, so what a plain `cmake ..` produced
depended on the platform it ran on: Neon kernels on arm64, AVX kernels over
an x86-64-v3 floor on an Intel Mac, portable scalar everywhere else. That
disagreed with README.md, which lists all three ISA options as "Optionally
one of ...", and with the Makefile targets they come from, where `make avx`,
`make arm` and `make ibm` are each an explicit request.

Default all four OFF on every platform. A default build is the portable one
everywhere, each departure from portability is named by whoever runs the
build, and the target architecture's only remaining say over these options
is to reject one it cannot honour.

The three validations move up to sit directly beneath the option() calls,
making that veto the single place architecture and option meet. The
`NOT ENABLE_IBM` guard on the GNU baseline -march goes with them: it is
redundant beside the HPTT_TARGET_IS_X86 test in the same condition, an x86
target being the one place ENABLE_IBM cannot be on.

The validation diagnostics quote hptt_target_arch rather than
CMAKE_SYSTEM_PROCESSOR. Those two disagree under an MSVC cross build, where
the message would otherwise name the host architecture while rejecting an
option for targeting the wrong one, reading as a contradiction of the error
it reports.

On arm64 a default build now compiles the scalar path with REGISTER_BITS=256,
that macro defaulting to 256 in include/hptt_types.h with only HPTT_ARCH_ARM
lowering it to 128. This is the configuration x86 default builds have always
used; ENABLE_ARM=ON is what selects the Neon kernels and 128 again.

CI keeps pace on both sides:

  - "Check the default build is portable" additionally requires that no
    HPTT_ARCH_* macro reach the compile line, on all six platforms. It has
    to exist because the scalar fallback is correct: an ISA option turning
    itself on changes nothing any other check in the job looks at.

  - Every job builds the one ISA option its architecture accepts and runs
    the downstream consumer against it -- ENABLE_ARM on the ARM64 runners,
    ENABLE_AVX on the x86 ones. Nothing else would compile the Neon kernels
    with the default no longer selecting them, and the two options are
    symmetric enough to share one step.

  - The matrix drops arch_flags, empty on all six entries and with no
    remaining caller.

Verified on x86_64 / GCC 13.3: the default build carries -march=x86-64
-mtune=generic and no HPTT_ARCH_* macro, -DENABLE_AVX=ON carries
-march=x86-64-v3 and HPTT_ARCH_AVX, -DFINE_TUNE=ON carries -march=native.
-DENABLE_ARM=ON, -DENABLE_IBM=ON, and the pairs -DENABLE_AVX=ON
-DENABLE_ARM=ON and -DENABLE_ARM=ON -DENABLE_IBM=ON are each refused at
configure time with a message the CI grep matches. The scalar default and
the ENABLE_AVX build both pass the downstream consumer's correctness checks.

Co-Authored-By: Claude <noreply@anthropic.com>
The CMake section lists the three ENABLE_* variants but not FINE_TUNE, so
reading CMakeLists.txt is the only way to find it. It is also the option
whose consequence a reader most needs warning about: the binary it produces
runs only where the build machine's CPU features are present. That matters
on the way in as much as the way out, the Makefile's default g++ flags
carrying -march=native, so someone moving from `make` to CMake would
otherwise notice neither the loss of host tuning nor how to ask for it back.

Co-Authored-By: Claude <noreply@anthropic.com>
@IvanaGyro
IvanaGyro force-pushed the claude/arm64-fine-tune-flag-rzdq81 branch from c5de6c1 to 4c09d59 Compare July 30, 2026 14:03
@IvanaGyro
IvanaGyro merged commit 98c5de5 into main Jul 30, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant