From def8c6b961f55c6d35bb89d8bf5b979737ab4f2e Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Wed, 17 Jun 2026 12:56:51 +0200 Subject: [PATCH 1/3] Build the support library against oneAPI 2026.0.0. Bump the Intel oneAPI toolkit (DPC++ compiler + oneMKL) that the local support-library build compiles against from 2025.2.0 to 2026.0.0: - deps/build_local.jl: dpcpp_linux-64 / mkl-devel-dpcpp Conda packages - test/onemkl.jl: minimum oneMKL version assertion CI rebuilds liboneapi_support locally (the wrappers postdate the 0.9.2 oneAPI_Support_jll build), so this exercises the new toolkit without needing a registered JLL. The oneAPI_Support_jll / oneAPI_Support_Headers_jll version bumps follow once Yggdrasil has built and registered them. Co-Authored-By: Claude Opus 4.8 (1M context) --- deps/build_local.jl | 2 +- test/onemkl.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/deps/build_local.jl b/deps/build_local.jl index f2d620e0..80e67f51 100644 --- a/deps/build_local.jl +++ b/deps/build_local.jl @@ -62,7 +62,7 @@ if !isfile(joinpath(conda_dir, "condarc-julia.yml")) touch(joinpath(conda_dir, "conda-meta", "history")) end Conda.add_channel("https://software.repos.intel.com/python/conda/", conda_dir) -Conda.add(["dpcpp_linux-64=2025.2.0", "mkl-devel-dpcpp=2025.2.0"], conda_dir) +Conda.add(["dpcpp_linux-64=2026.0.0", "mkl-devel-dpcpp=2026.0.0"], conda_dir) Conda.list(conda_dir) diff --git a/test/onemkl.jl b/test/onemkl.jl index 4a4c7fcd..98774404 100644 --- a/test/onemkl.jl +++ b/test/onemkl.jl @@ -14,7 +14,7 @@ k = 13 @testset "Version" begin version_onemkl = oneMKL.version() - @test version_onemkl ≥ v"2025.2.0" + @test version_onemkl ≥ v"2026.0.0" end ############################################################################################ From b36f8f203505dc027a9332f64584ce71b94a77c8 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Wed, 17 Jun 2026 15:56:59 +0200 Subject: [PATCH 2/3] Adapt the oneMKL DFT wrappers to the oneAPI 2026.0 toolkit. oneMKL 2026.0 changed a few DFT APIs that broke the support-library build: - config_param::INPUT_STRIDES / OUTPUT_STRIDES were removed (deprecated since 2025 in favour of FWD_STRIDES / BWD_STRIDES). Map the legacy parameters onto their successors; the Julia layer only ever sets FWD/BWD strides, so this is behaviour-preserving. - descriptor::get_value() now requires a pointer to the descriptor's real scalar type (float for single precision), so the double* path needs a precision-matched temporary that is widened for the C interface. Also bump the baked-in ONEMKL_VERSION constants to 2026.0.0 so that oneMKL.version() reports the toolkit the library was built against. Co-Authored-By: Claude Opus 4.8 (1M context) --- deps/src/onemkl.h | 4 ++-- deps/src/onemkl_dft.cpp | 33 ++++++++++++++++++++++++++++----- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/deps/src/onemkl.h b/deps/src/onemkl.h index 0029fb2e..1f850bf2 100644 --- a/deps/src/onemkl.h +++ b/deps/src/onemkl.h @@ -140,8 +140,8 @@ typedef struct omatconvert_descr *omatconvert_descr_t; struct omatadd_descr; typedef struct omatadd_descr *omatadd_descr_t; -const int64_t ONEMKL_VERSION_MAJOR = 2025; -const int64_t ONEMKL_VERSION_MINOR = 2; +const int64_t ONEMKL_VERSION_MAJOR = 2026; +const int64_t ONEMKL_VERSION_MINOR = 0; const int64_t ONEMKL_VERSION_PATCH = 0; void onemkl_version(int64_t *major, int64_t *minor, int64_t *patch); diff --git a/deps/src/onemkl_dft.cpp b/deps/src/onemkl_dft.cpp index 8c10ffb7..9f39c127 100644 --- a/deps/src/onemkl_dft.cpp +++ b/deps/src/onemkl_dft.cpp @@ -116,8 +116,10 @@ static inline config_param to_param(onemklDftConfigParam p) { case ONEMKL_DFT_PARAM_NUMBER_OF_TRANSFORMS: return config_param::NUMBER_OF_TRANSFORMS; case ONEMKL_DFT_PARAM_COMPLEX_STORAGE: return config_param::COMPLEX_STORAGE; case ONEMKL_DFT_PARAM_PLACEMENT: return config_param::PLACEMENT; - case ONEMKL_DFT_PARAM_INPUT_STRIDES: return config_param::INPUT_STRIDES; - case ONEMKL_DFT_PARAM_OUTPUT_STRIDES: return config_param::OUTPUT_STRIDES; + // oneMKL >= 2026.0 dropped the deprecated INPUT_STRIDES/OUTPUT_STRIDES; + // map the legacy parameters onto their FWD_STRIDES/BWD_STRIDES successors. + case ONEMKL_DFT_PARAM_INPUT_STRIDES: return config_param::FWD_STRIDES; + case ONEMKL_DFT_PARAM_OUTPUT_STRIDES: return config_param::BWD_STRIDES; case ONEMKL_DFT_PARAM_FWD_DISTANCE: return config_param::FWD_DISTANCE; case ONEMKL_DFT_PARAM_BWD_DISTANCE: return config_param::BWD_DISTANCE; case ONEMKL_DFT_PARAM_WORKSPACE: return config_param::WORKSPACE; @@ -210,7 +212,28 @@ int onemklDftGetValueInt64(onemklDftDescriptor_t desc, onemklDftConfigParam para int onemklDftGetValueDouble(onemklDftDescriptor_t desc, onemklDftConfigParam param, double *value) { if (!desc || !value) return -2; if (!desc->ptr) return -3; - try { ONEMKL_DFT_DISPATCH_CFG(desc->ptr, d->get_value(to_param(param), value)); return 0; } catch (...) { return -1; } + try { + config_param p = to_param(param); + // oneMKL >= 2026.0 requires a pointer to the descriptor's real scalar type + // (float for single precision, double for double); use a matching temporary + // and widen to double for the C interface. + if (desc->prec == precision::SINGLE) { + float tmp; + if (desc->dom == domain::REAL) + static_cast< descriptor* >(desc->ptr)->get_value(p, &tmp); + else + static_cast< descriptor* >(desc->ptr)->get_value(p, &tmp); + *value = static_cast(tmp); + } else { + double tmp; + if (desc->dom == domain::REAL) + static_cast< descriptor* >(desc->ptr)->get_value(p, &tmp); + else + static_cast< descriptor* >(desc->ptr)->get_value(p, &tmp); + *value = tmp; + } + return 0; + } catch (...) { return -1; } } int onemklDftGetValueInt64Array(onemklDftDescriptor_t desc, onemklDftConfigParam param, int64_t *values, int64_t *n) { @@ -443,8 +466,8 @@ int onemklDftQueryParamIndices(int64_t *out, int64_t n) { config_param::NUMBER_OF_TRANSFORMS, config_param::COMPLEX_STORAGE, config_param::PLACEMENT, - config_param::INPUT_STRIDES, - config_param::OUTPUT_STRIDES, + config_param::FWD_STRIDES, // was INPUT_STRIDES (removed in oneMKL 2026.0) + config_param::BWD_STRIDES, // was OUTPUT_STRIDES (removed in oneMKL 2026.0) config_param::FWD_DISTANCE, config_param::BWD_DISTANCE, config_param::WORKSPACE, From 9147cc81bc002cb8035e76182feab6a232a85086 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Wed, 17 Jun 2026 15:56:59 +0200 Subject: [PATCH 3/3] Fix a GC-safety bug in the FFT descriptor setup. _create_descriptor and the multi-dimensional plan constructors passed pointer(lengths) / pointer(strides) to the oneMKL C wrappers without keeping the arrays rooted across the ccall. oneMKL copies the data out of those pointers, but the arrays could be collected first, leaving the descriptor with garbage dimensions/strides. This was latent (it happened to survive for square arrays) but oneMKL 2026.0 surfaces it deterministically: committing a multi-dimensional, non-square descriptor fails with FFT_INVALID_DESCRIPTOR. Wrap the affected ccalls in GC.@preserve. Co-Authored-By: Claude Opus 4.8 (1M context) --- lib/mkl/fft.jl | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/lib/mkl/fft.jl b/lib/mkl/fft.jl index 5f5614b1..745ff65f 100644 --- a/lib/mkl/fft.jl +++ b/lib/mkl/fft.jl @@ -93,9 +93,14 @@ function _create_descriptor(sz::NTuple{N,Int}, T::Type, complex::Bool) where {N} prec = T<:Float64 || T<:ComplexF64 ? ONEMKL_DFT_PRECISION_DOUBLE : ONEMKL_DFT_PRECISION_SINGLE dom = complex ? ONEMKL_DFT_DOMAIN_COMPLEX : ONEMKL_DFT_DOMAIN_REAL desc_ref = Ref{onemklDftDescriptor_t}() - # Create descriptor for the full array dimensions + # Create descriptor for the full array dimensions. `lengths` must stay rooted + # across the ccall: oneMKL copies the dimensions out of `pointer(lengths)`, and + # without GC.@preserve the array can be collected first, leaving the descriptor + # with garbage dimensions (commit then fails with FFT_INVALID_DESCRIPTOR). lengths = collect(Int64, sz) - st = length(lengths) == 1 ? onemklDftCreate1D(desc_ref, prec, dom, lengths[1]) : onemklDftCreateND(desc_ref, prec, dom, length(lengths), pointer(lengths)) + st = GC.@preserve lengths (length(lengths) == 1 ? + onemklDftCreate1D(desc_ref, prec, dom, lengths[1]) : + onemklDftCreateND(desc_ref, prec, dom, length(lengths), pointer(lengths))) st == 0 || error("onemkl DFT create failed (status $st)") desc = desc_ref[] # Do not program descriptor scaling; we'll perform inverse normalization manually. @@ -125,8 +130,10 @@ function plan_fft(X::oneAPI.oneArray{T,N}, region) where {T<:Union{ComplexF32,Co strides[i+1] = prod prod *= size(X,i) end - onemklDftSetValueInt64Array(desc, ONEMKL_DFT_PARAM_FWD_STRIDES, pointer(strides), length(strides)) - onemklDftSetValueInt64Array(desc, ONEMKL_DFT_PARAM_BWD_STRIDES, pointer(strides), length(strides)) + GC.@preserve strides begin + onemklDftSetValueInt64Array(desc, ONEMKL_DFT_PARAM_FWD_STRIDES, pointer(strides), length(strides)) + onemklDftSetValueInt64Array(desc, ONEMKL_DFT_PARAM_BWD_STRIDES, pointer(strides), length(strides)) + end end stc = onemklDftCommit(desc, q); stc == 0 || error("commit failed ($stc)") return cMKLFFTPlan{T,MKLFFT_FORWARD,false,N,R,Nothing}(desc,q,size(X),size(X),false,reg,nothing,nothing) @@ -144,8 +151,10 @@ function plan_bfft(X::oneAPI.oneArray{T,N}, region) where {T<:Union{ComplexF32,C @inbounds for i in 1:N strides[i+1]=prod; prod*=size(X,i) end - onemklDftSetValueInt64Array(desc, ONEMKL_DFT_PARAM_FWD_STRIDES, pointer(strides), length(strides)) - onemklDftSetValueInt64Array(desc, ONEMKL_DFT_PARAM_BWD_STRIDES, pointer(strides), length(strides)) + GC.@preserve strides begin + onemklDftSetValueInt64Array(desc, ONEMKL_DFT_PARAM_FWD_STRIDES, pointer(strides), length(strides)) + onemklDftSetValueInt64Array(desc, ONEMKL_DFT_PARAM_BWD_STRIDES, pointer(strides), length(strides)) + end end stc = onemklDftCommit(desc, q); stc == 0 || error("commit failed ($stc)") return cMKLFFTPlan{T,MKLFFT_INVERSE,false,N,R,Nothing}(desc,q,size(X),size(X),false,reg,nothing,nothing) @@ -165,8 +174,10 @@ function plan_fft!(X::oneAPI.oneArray{T,N}, region) where {T<:Union{ComplexF32,C @inbounds for i in 1:N strides[i+1]=prod; prod*=size(X,i) end - onemklDftSetValueInt64Array(desc, ONEMKL_DFT_PARAM_FWD_STRIDES, pointer(strides), length(strides)) - onemklDftSetValueInt64Array(desc, ONEMKL_DFT_PARAM_BWD_STRIDES, pointer(strides), length(strides)) + GC.@preserve strides begin + onemklDftSetValueInt64Array(desc, ONEMKL_DFT_PARAM_FWD_STRIDES, pointer(strides), length(strides)) + onemklDftSetValueInt64Array(desc, ONEMKL_DFT_PARAM_BWD_STRIDES, pointer(strides), length(strides)) + end end stc = onemklDftCommit(desc, q); stc == 0 || error("commit failed ($stc)") cMKLFFTPlan{T,MKLFFT_FORWARD,true,N,R,Nothing}(desc,q,size(X),size(X),false,reg,nothing,nothing) @@ -184,8 +195,10 @@ function plan_bfft!(X::oneAPI.oneArray{T,N}, region) where {T<:Union{ComplexF32, @inbounds for i in 1:N strides[i+1]=prod; prod*=size(X,i) end - onemklDftSetValueInt64Array(desc, ONEMKL_DFT_PARAM_FWD_STRIDES, pointer(strides), length(strides)) - onemklDftSetValueInt64Array(desc, ONEMKL_DFT_PARAM_BWD_STRIDES, pointer(strides), length(strides)) + GC.@preserve strides begin + onemklDftSetValueInt64Array(desc, ONEMKL_DFT_PARAM_FWD_STRIDES, pointer(strides), length(strides)) + onemklDftSetValueInt64Array(desc, ONEMKL_DFT_PARAM_BWD_STRIDES, pointer(strides), length(strides)) + end end stc = onemklDftCommit(desc, q); stc == 0 || error("commit failed ($stc)") cMKLFFTPlan{T,MKLFFT_INVERSE,true,N,R,Nothing}(desc,q,size(X),size(X),false,reg,nothing,nothing)