Conversation
…building To allow multiple (redundant) build hosts per CPU target, verify early in EESSI-install-software.sh that the compilers of all toolchains supported in the EESSI version being built translate the native architecture flag (-march=native on x86_64, -mcpu=native on aarch64, as used by EasyBuild) into exactly the same target flags as recorded in a reference for the CPU target. This asks the GCC/Clang driver directly (via -###) rather than relying on a surrogate such as lscpu. - scripts/native_flags/get_supported_toolchains.py: extract the supported toplevel toolchains for an EESSI version from eb_hooks.py - scripts/native_flags/check_native_flags.sh: check (or --generate) references in scripts/native_flags/references/<subdir>/<compiler>-<version>.txt - references for all non-generic CPU targets built on the AWS build cluster A missing reference is an error, unless $EESSI_NATIVE_FLAGS_ALLOW_MISSING_REFERENCE is set. The check can be skipped entirely with $EESSI_SKIP_NATIVE_FLAGS_CHECK, and is skipped for generic builds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
casparvl
marked this pull request as draft
October 1, 2026 15:29
casparvl
commented
Oct 1, 2026
casparvl
commented
Oct 1, 2026
Co-authored-by: Caspar van Leeuwen <33718780+casparvl@users.noreply.github.com>
…SON file The supported toplevel toolchains per EESSI version are now defined in eessi_supported_toolchains.json rather than in eb_hooks.py itself, so that they can also be used by other scripts without having to parse (or import) the hooks file. - eessi_supported_toolchains.json is located next to eb_hooks.py, both in the repository and when installed in <prefix>/init/easybuild/ by install_scripts.sh. eb_hooks.py locates it relative to its own location. - Toolchains that can only be installed with a recent enough EasyBuild version (lfoss/2025b, rompi/2025a) now specify 'min_easybuild_version' instead of being appended conditionally in eb_hooks.py. - CI also checks that the deployed eessi_supported_toolchains.json is up-to-date, like is done for eb_hooks.py. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Now that the supported toplevel toolchains are defined in eessi_supported_toolchains.json, read them from there instead of extracting them from eb_hooks.py. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
Author
|
TODO's: make sure that we add references for all the existing build architectures, before we merge this PR. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Depends on #312 . That should be merged first, then the diff between this and
mainshould be (a bit) smaller.Check native compiler flags against per-CPU-target references before building
The problem
Every CPU target in EESSI (e.g.
x86_64/intel/icelake) is built on a host of that type, with-march=native(-mcpu=nativeon aarch64). Until now there has been a single build host per CPU target, so all binaries in a given CPU prefix were optimised for exactly the same set of instruction-set extensions.If we want redundancy (i.e. multiple build hosts for the same CPU target, possibly at different sites), we need to ensure that all systems that build for the "same" CPU type expose the same CPU features. In the past, we have seen that this is not always the case: e.g. we have seen different flags being reported by
lscpuon differentzen5issues, and even had binary compatibility issues between binaries built on Grace Hopper nodes at SURF ETP vs Jureca. The fundamental issue is that if two build hosts for the same CPU target translate-march=nativedifferently, the result is an inconsistent software stack in a single CPU prefix, where some binaries don't run on every system of that CPU target.The solution in this PR
Early in
EESSI-install-software.sh(directly after the software subdirectory is verified, before anything is installed), we now check that, on the current build host, the compilers translate the native architecture flag into exactly the same target flags as recorded in a reference for that CPU target. The build stops if they don't.Rather than comparing CPU flags from
lscpuor a library likecpu_features(which would only be a surrogate), we ask the compiler driver itself what it resolves the native flag into, since that is what determines the generated code:gcc <native flag> -### -E - < /dev/null. All-m*options passed tocc1are compared (-march/-mcpu,-mtune, and the full list of-m<ext>/-mno-<ext>).--paramoptions such asl1-cache-sizeare ignored, since they only affect tuning.clang <native flag> -### -c -x c - -o /dev/null. The-target-cpu,-tune-cpu,-target-abiand-target-featureoptions passed toclang -cc1are compared.The native flag is the same one EasyBuild uses (
COMPILER_OPTIMAL_ARCHITECTURE_OPTION):-march=nativeon x86_64, and-mcpu=nativeon aarch64 for both GCC and LLVM.Since the result depends on the compiler version (e.g. on Neoverse-N1, GCC 12.x gives
-mcpu=ares+crypto+ssbs+noprofilewhile GCC 13.2 gives-mcpu=ares+crc+crypto+ssbs), references are keyed on CPU target, compiler and compiler version:scripts/native_flags/references/<EESSI_SOFTWARE_SUBDIR>/<gcc|clang>-<version>.txtThe compilers to check are taken from the toolchains supported in the EESSI version being built:
scripts/native_flags/get_supported_toolchains.pyreads the supported toplevel toolchains for the EESSI version fromeessi_supported_toolchains.json(introduced in Move supported toplevel toolchains from eb_hooks.py into a separate JSON for easier define-once-and-reuse #312 , and also used byeb_hooks.py). It includes toolchains regardless of theirmin_easybuild_version, since that only says which EasyBuild version can install them, and adds site toolchains from$EESSI_SITE_TOP_LEVEL_TOOLCHAINS_<version>.scripts/native_flags/check_native_flags.shloads each toolchain module in a subshell. It takesgccandclangfrom$PATH, keeping only binaries that come from a module installation (an$EBROOT*prefix) and dropping duplicates (e.g. GCC 14.3.0 from bothfoss/2025bandlfoss/2025b). A toolchain whose module is unavailable, or deliberately fails to load (e.g.foss/2022bon zen4 in 2023.06), is skipped with a warning.check_native_flags.sh --generatewrites the references for the current host.Behaviour and overrides:
$EESSI_NATIVE_FLAGS_ALLOW_MISSING_REFERENCEto make it a warning.$EESSI_SKIP_NATIVE_FLAGS_CHECKskips the check entirely. It is always skipped for--genericbuilds.$EESSI_NATIVE_FLAGS_REFERENCE_DIRoverrides the reference location.Included references
References were generated on the current (AWS) build cluster, so that any future redundant build host must match what the existing binaries were built with. They cover EESSI 2023.06, 2025.06 and 2026.06 (GCC 12.2.0 to 15.2.0, Clang 20.1.8 and 21.1.8) for:
aarch64/{neoverse_n1,neoverse_v1,aws/graviton4},x86_64/amd/{zen2,zen3,zen4},x86_64/intel/{haswell,skylake_avx512,cascadelake,icelake,sapphirerapids}Limitations
x86_64/amd/zen5,x86_64/intel/graniterapids,aarch64/nvidia/grace,aarch64/google/axion,aarch64/a64fx. Since a missing reference is an error by default, builds for these targets will fail until someone runsscripts/native_flags/check_native_flags.sh --generateon their current build host (inside the EESSI environment for each EESSI version) and adds the output. Alternatively, setEESSI_NATIVE_FLAGS_ALLOW_MISSING_REFERENCEon those build hosts for now./proc/cpuinfo. All current build nodes run an EL8 kernel (4.18), which does not report pointer authentication (paca/pacg), so GCC resolves to e.g.-mcpu=neoverse-v1+…+nopauth. A redundant build host with a newer kernel (e.g. EL9, 5.14) will likely report pointer authentication, drop+nopauth, and fail the check forneoverse_v1andaws/graviton4. Strictly speaking that is correct, since the flags really differ. In practice the difference is probably harmless: GCC only emits pointer-authentication instructions with-mbranch-protection, and those instructions execute as no-ops on hardware without the feature. We'll need to decide either to keep build hosts on the same kernel generation, or to allow known-harmless differences.+pauth(and+mte,+fpacon Graviton4) even though the kernel does not report those features. Clang appears to start from the default features of the core (identified from the CPU model) and add what/proc/cpuinforeports, rather than removing defaults the kernel doesn't report (not verified in the LLVM source). If so, a host whose kernel fails to report a feature that is a default for its core would not change Clang's output. GCC's would, so on aarch64 the GCC check is the more reliable one. On x86_64 both read CPUID and look sound.LLVM/20.1.7-GCCcore-14.2.0, which also shipsclang) are not checked.rustc -C target-cpu=nativewith rustc's bundled LLVM) is not covered.rompi/2025ais not installed on any of the targets covered here, so it has no references. A reference for ROCmclangwould also be stored asclang-<version>and could clash with an upstream LLVM version.AI disclosure
This PR was developed together with an AI coding assistant (Claude, via Claude Code) in an interactive session that I steered and checked throughout. Concretely:
Approach: the approach was mine: I proposed asking GCC directly via
gcc -march=native -### -E -rather than usinglscpu/cpu_features, and keying the references on CPU model and compiler version. I asked the assistant to review the approach, find an LLVM equivalent, and implement a loop over all GCC and LLVM compilers supported in the EESSI version being built.Initial implementation: the assistant read the build scripts and
eb_hooks.py, usedEESSI_SUPPORTED_TOP_LEVEL_TOOLCHAINSas the list of supported toolchains, and checked in the EasyBuild source that EasyBuild uses-mcpu=native(not-march=native) on aarch64. It wrote the two scripts and the hook. It tested them on an aarch64 (Neoverse-N1) login node against EESSI 2023.06, 2025.06 and 2026.06, covering reference generation, an exact match, deliberately edited references for GCC and Clang (both detected, with a readable diff), and the missing-reference path.Error by default: at my request, it made a missing reference an error by default, with an environment variable to turn it into a warning.
References on the build cluster: I then asked it to generate references on our Slurm build cluster, one partition per CPU target. It ran
check_native_flags.sh --generatein a batch job on each non-generic partition, for all three EESSI versions, and recordedlscpu,/proc/cpuinfoflags, kernel version and archdetect output alongside. Problems it found and fixed along the way:foss/2022bdeliberately fails to load on Zen4, which initially made the check fail. It changed module-load failures to a warning.Cross-check against
/proc/cpuinfo: I asked whether the GCC output matched the CPU flags fromlscpu//proc/cpuinfo. On all x86_64 targets, every extension GCC enables is reported by the kernel, and none it disables is (apart from naming differences such assse3/pni). On aarch64 the GCC output matches the kernel'sFeatures. This comparison surfaced the+nopauthand Clang-on-aarch64 limitations described above.Writing: the assistant wrote the code, the commit message and a draft of this description. I have reviewed the code & commit message in a draft PR and make final changes myself where I think they're needed before marking the PR as ready for review.
🤖 Generated with Claude Code