Skip to content

0.3.34 regression (Windows, clang-cl, DYNAMIC_ARCH, NO_AVX512): access violation in dgemm_kernel_ZEN/HASWELL on AMD Zen 4/5 when AVX-512 is exposed; 0.3.33 fine #6013

Description

@bodono

Summary

OpenBLAS 0.3.34 built for Windows x86-64 with clang-cl/flang, DYNAMIC_ARCH=ON, NO_AVX512=1, pthreads (the conda-forge win-64 package libopenblas 0.3.34 pthreads_h877e47f_1) crashes with an access violation inside dgemm_kernel_ZEN / dgemm_kernel_HASWELL on plain dsyrk/dpotrf calls with small matrices (dsyrk('U','T', n=55, k=165) is enough), on AMD Zen 4 and Zen 5 machines when the CPU exposes AVX-512. The same binary works on AMD machines where AVX-512 is not exposed (e.g. Zen 3, or Zen 4 VMs whose hypervisor masks it) and on Intel Xeons that do expose it. 0.3.33, 0.3.31, 0.3.29 and 0.3.27 built by the same recipe (same build-config hash) run the same reproducer fine on the same machines, so this is a regression between the 0.3.33 and 0.3.34 sources.

Reported first at conda-forge/openblas-feedstock#196; the maintainer there does not think it is a packaging problem and asked for an upstream issue.

Details

  • Deterministic on an affected machine; independent of the selected core table (OPENBLAS_CORETYPE=Zen forced: same crash at dgemm_kernel_ZEN+0x6ea6; on a Zen 5 the default fallback is Haswell and it crashes at dgemm_kernel_HASWELL+0x6ea6) and of threading (OPENBLAS_NUM_THREADS=1: still crashes, earlier in the kernel at +0x16e).
  • Under cdb (with _NO_DEBUG_HEAP=1) the fault is on the kernel's ret: the stack pointer and every callee-saved register hold IEEE doubles, i.e. the kernel's own frame was overwritten with matrix data.
  • All runners had the same OS image (GitHub Actions windows-latest, Windows Server 2025, image 20260819.586); AVX-512 exposure was read from numpy's __cpu_features__['AVX512F'] in the same process.
  • The same 0.3.34 sources built by the numpy project (scipy-openblas32 0.3.34.106.0, AVX-512 kernels included) run the reproducer fine on the affected machines, where they select SkylakeX.
  • Between v0.3.33 and v0.3.34 the commits API shows no changes under kernel/x86_64; the changed areas that look relevant to a corrupted kernel frame are the level-3 locking rework (driver/level3/level3_thread_lock.c, 9363452, 298d53c, a2261f9), driver/others/memory.c, the BLAS_LOCK_DEFINED guard in common_x86_64.h, and the build-system changes (Makefile.system, cmake/arch.cmake, cmake/system.cmake, cmake/prebuild.cmake). I have not bisected.
  • Side note: dynamic.c handles AMD extended families 8, 9 and 10 only, so family 0x1A (Zen 5, e.g. AMD EPYC 9V45) returns NULL and the capability fallback selects Haswell under NO_AVX512 (or Cooperlake/SkylakeX otherwise) instead of Zen.

Machines (standalone reproducer, fresh conda environments, 16 GitHub runners)

runner CPU AVX-512 exposed VMs core selected 0.3.34 0.3.33 0.3.31 0.3.29 0.3.27
AMD EPYC 9V74 (Zen 4) yes 2 Zen crash (0xC0000005) 2/2 ok ok ok ok
AMD EPYC 9V74 (Zen 4) no 8 Zen ok ok ok ok ok
AMD EPYC 7763 (Zen 3) no 5 Zen ok ok ok ok ok
Intel Xeon Platinum 8573C yes 1 Haswell ok ok ok ok ok

An AMD EPYC 9V45 (Zen 5) runner crashed the same way with 0.3.34 in an earlier run (only 0.3.34 was tested there; core selected Haswell).

Runs: version sweep, debugger run with the dump, forced core / thread modes.

Build configuration (conda-forge recipe)

clang-cl + flang, CMake with -DDYNAMIC_ARCH=ON -DNO_AVX512=1 -DNOFORTRAN=0 -DBUILD_WITHOUT_LAPACK=no -DNUM_THREADS=128 -DBUILD_SHARED_LIBS=on -DUSE_OPENMP=0 (see the feedstock's recipe/bld.bat). 0.3.33 from the same recipe is pthreads_h877e47f_0, 0.3.34 is pthreads_h877e47f_1.

Reproducer

# dsyrk/dpotrf/dpotrs/dgemv via ctypes on the conda-forge openblas.dll; crashes in the first dsyrk on affected machines
import ctypes, glob, os, sys
import numpy as np
binDir = os.path.join(sys.prefix, "Library", "bin")
os.add_dll_directory(binDir)
ob = ctypes.CDLL(os.path.join(binDir, "openblas.dll"))
ob.openblas_get_config.restype = ctypes.c_char_p
ob.openblas_get_corename.restype = ctypes.c_char_p
try:
    from numpy._core._multiarray_umath import __cpu_features__ as feat
except ImportError:
    from numpy.core._multiarray_umath import __cpu_features__ as feat
print("CONFIG:", ob.openblas_get_config().decode(), "| CORE:", ob.openblas_get_corename().decode(),
      "| threads:", ob.openblas_get_num_threads(), "| AVX512F exposed:", feat.get("AVX512F"), flush=True)
def by(v): return ctypes.byref(ctypes.c_int(v))
D = ctypes.POINTER(ctypes.c_double)
for (m, n) in [(165, 55), (1200, 400)]:
    rng = np.random.RandomState(0)
    A = np.asfortranarray(rng.randn(m, n)); G = np.zeros((n, n), order="F")
    b = rng.randn(n); y = rng.randn(m); info = ctypes.c_int(0)
    one, zero = ctypes.c_double(1.0), ctypes.c_double(0.0)
    ob.dsyrk_(b"U", b"T", by(n), by(m), ctypes.byref(one), A.ctypes.data_as(D), by(m), ctypes.byref(zero), G.ctypes.data_as(D), by(n))
    G[np.diag_indices(n)] += 1.0 + m
    ob.dpotrf_(b"U", by(n), G.ctypes.data_as(D), by(n), ctypes.byref(info)); assert info.value == 0, info.value
    ob.dpotrs_(b"U", by(n), by(1), G.ctypes.data_as(D), by(n), b.ctypes.data_as(D), by(n), ctypes.byref(info)); assert info.value == 0
    ob.dgemv_(b"T", by(m), by(n), ctypes.byref(one), A.ctypes.data_as(D), by(m), y.ctypes.data_as(D), by(1), ctypes.byref(one), b.ctypes.data_as(D), by(1))
    ob.dgemv_(b"N", by(m), by(n), ctypes.byref(one), A.ctypes.data_as(D), by(m), b.ctypes.data_as(D), by(1), ctypes.byref(zero), y.ctypes.data_as(D), by(1))
    print(f"ok m={m} n={n}", flush=True)
print("REPRO OK")
conda create -p .\ob -c conda-forge "libopenblas=0.3.34=*pthreads*" openblas=0.3.34 python=3.12 numpy
.\ob\python.exe repro.py

Crash dump (AMD EPYC 9V45, default core selection = Haswell, 4 threads)

rax=000001e9d8cdc200 rbx=3ff699ece033200f rcx=000001e9f7520000
rdx=0000000000000000 rsi=bfd0d1ca60b6f99e rdi=bff543da6fd24b01
rip=00007ff80b441166 rsp=000000c06efe9c38 rbp=3fd1f264f736fd5a
 r8=000001e9e7727bc0  r9=000001e9d8cdc8c0 r10=00000000000001b8
r11=0000000000000080 r12=bffaea7f2b139a9b r13=bfdb26aecf319480
r14=3fe52314bfed9253 r15=bfb0447583b403d1
openblas_115cad90a43485aceeae12031d62cd82!dgemm_kernel_HASWELL+0x6ea6:
Child-SP          RetAddr               : Args to Child                                                           : Call Site
000000c0`6efe9c38 3fe22753`b6c6084e     : bfd8660e`50759a89 3fc44d32`199d116a bfe7c85b`6fbfafdc c0026722`7d37cd7a : openblas_115cad90a43485aceeae12031d62cd82!dgemm_kernel_HASWELL+0x6ea6

Happy to run patches or a bisect build on the affected runner class; the sweep over GitHub's runner pool is automated on our side.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions