Broken build on macOS (Universal / Intel): cpuid instruction not available

First seen: 2026-05-07 11:41:11+00:00 · Messages: 24 · Participants: 8

Latest Update

2026-06-04 · claude-opus-4-6

Monthly Summary: Broken macOS Universal/Intel Builds — cpuid Instruction Not Available (May 2026)

Overview

A build regression introduced by the centralization of x86 CPU feature detection in PostgreSQL 19 was reported, diagnosed, and resolved within the first week of May. The fix — making pg_cpuid() fail soft by returning zeros when no cpuid intrinsic is available — was committed on 2026-05-08, restoring universal build support with the expected caveat that x86 optimizations are silently disabled.

Root Cause

Two commits created the problem:

The old code used defensive #ifdef guards that ensured cpuid-dependent paths were only compiled when configure detected a working intrinsic. The centralized replacement dropped those guards, changing the failure mode from silent performance degradation to hard compile error when cpuid intrinsics are unavailable — which happens in macOS universal builds (-arch arm64 -arch x86_64) because configure runs a single probe pass that can only succeed for one architecture.

Resolution

John Naylor posted, tested, and committed a patch making pg_cpuid() return zeros when neither HAVE__GET_CPUID nor HAVE__CPUID is defined — mirroring the existing pg_cpuid_subleaf() behavior. All consumers (popcount, AVX2 checksums, TSC timing) already have scalar fallback paths that activate when no features are advertised.

Tobias Bussmann verified the fix resolves the universal build regression and make check passes for both arm64 and x86_64 slices.

Performance Implications for Universal Builds

Post-fix inspection of configure output revealed that universal builds also lose hardware CRC-32C on both slices (falling back to slicing-by-8) because the arch-specific -march flags needed for CRC probes are incompatible with simultaneous multi-arch compilation. The full list of lost optimizations:

Tom Lane noted this matches pre-v19 behavior — universal builds were always silently degraded — and the project will not invest in per-arch configure passes to recover these optimizations.

Policy Decisions

  1. Universal builds are not formally supported but will compile and run correctly with degraded performance.
  2. No per-arch configure machinery will be built — proper fat-binary support would require two independent probe passes, which nobody wants to maintain given Apple's trajectory away from x86.
  3. Buildfarm coverage is the path to keeping edge cases working long-term — Tobias offered to host macOS VMs.
  4. Meson transition was flagged as the appropriate venue for any future multi-arch probe work (not autotools).

Secondary Issue: Xcode 26.2 Under Rosetta

A separate report of x86_feature_available being undeclared on Intel was traced to a specific Xcode 26.2 toolchain quirk when invoked via Rosetta — switching to Command Line Tools 26.4 resolved it. Native Intel builds (buildfarm animal longfin, Daniel Gustafsson's Intel MBP) are unaffected.

History (1 prior analysis)
2026-06-04 · claude-opus-4-6

New Developments: Cross-Compilation Build Failure and Proposed Fix

A New Regression Discovered: x86_64 → arm64 Cross-Compilation Breaks

Tobias Bussmann returned with a comprehensive comparison of configure outputs across six different build configurations (native, cross-compiled, and universal, on both arm64 and x86_64). The key new finding is that cross-compiling from x86_64 to arm64 (or universal) also fails with the same x86_feature_available / PG_AVX2 undeclared errors seen earlier. This is distinct from the universal-build-on-arm64 case fixed in the prior round.

The root cause: configure's test for "AVX2 target attribute support" (USE_AVX2_WITH_RUNTIME_CHECK) passes even when cross-compiling to arm64, because the ARM compiler only emits a warning ("'+avx2' is not a recognized feature for this target (ignoring feature)") rather than a hard error. Configure interprets this as success. But USE_SSE2 is not set (since __x86_64__ isn't defined on the ARM side), so pg_cpu.h never provides x86_feature_available() — resulting in the build failure.

Sandeep Thakkar confirmed on the packagers list that this also affects native x86_64 hosts cross-compiling to arm64, making it a practical issue for EDB's beta1 packaging.

Tom Lane's Diagnostic Deep-Dive

Tom replicated the issue on buildfarm animal longfin's host (x86_64 Mac mini) and identified two distinct problems:

  1. pg_cpu.h guards are wrong: The file uses #if defined(USE_SSE2) || defined(__i386__) to gate x86-specific code, but macOS never defines __i386__ (only __x86_64__). On single-arch x86 builds this is masked because USE_SSE2 gets set. On multi-arch builds where USE_SSE2 doesn't get set for the ARM slice, the x86 API disappears.

  2. USE_AVX2_WITH_RUNTIME_CHECK leaks across architectures: Configure sets it based on $host_cpu = x86_64 AND whether the compiler accepts __attribute__((target("avx2"))). In multi-arch builds, the ARM compiler doesn't reject the attribute (just warns), so the symbol gets defined globally even though x86 APIs aren't available on the ARM side.

Tom later corrected himself: USE_SSE2 is set via #if defined(__x86_64__) in c.h, so his initial pg_cpu.h analysis was a no-op. The real issue is purely the leaked AVX2/AVX512 symbols.

Evolution of Tom's Patch (v1 → v2)

v1 (quick hack): Two changes — (a) fix pg_cpu.h guards (later determined to be a no-op), and (b) guard checksum.c's call to x86_feature_available() with an additional #ifdef __x86_64__ check.

v1.5 (cleaner approach): Make x86_feature_available() exist on all platforms but return constant false on non-x86. This means callers like checksum.c compile cleanly everywhere without per-callsite guards. The ARM compiler still emits a "not a recognized feature" warning for the target("avx2") attribute, but the build succeeds.

v2 (global fix, 2026-06-03): Rather than fixing individual call sites, #undef USE_AVX2_WITH_RUNTIME_CHECK and the USE_AVX512* symbols in c.h when __x86_64__ is not defined. This undoes configure's mistake at the earliest possible point, preventing any current or future file from seeing these symbols on non-x86 targets. Tom explicitly notes this approach is forward-compatible with Munro's pending patch that may add more AVX2 consumers.

Practical Urgency: Beta1 Packaging

Andrew Dunslane noted that Sandeep (EDB packager) would look at applying Tom's fix the next day, indicating this is blocking beta1 universal binary production. Tom suggested EDB apply the quick-hack for beta1 while the cleaner v2 is reviewed for commit.

Tobias's "Two Builds + lipo" Idea

Tobias floated doing two independent single-arch builds and combining them with lipo to get a properly-optimized universal binary. Tom acknowledged this could work but dismissed it as too much effort for a dying platform combination.

Minor Side Note: _M_AMD64

Tom raised a question about whether c.h's check for _M_AMD64 (MSVC's x86_64 macro) is necessary, noting inconsistency — if it's needed in c.h, why not in all other places that check __x86_64__? This is unresolved but flagged for future cleanup.