From 3b1a769014271ca6291115f7fbec0b9d87ccd21d Mon Sep 17 00:00:00 2001 From: Sheehan Olver Date: Mon, 20 Apr 2026 14:56:49 +0100 Subject: [PATCH 1/5] Restore mac tests but on ARM --- .github/workflows/ci.yml | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4991c31d..53182a8c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,7 +4,7 @@ on: - pull_request jobs: test: - name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} + name: Julia ${{ matrix.version }} - ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: fail-fast: false @@ -14,13 +14,7 @@ jobs: - '1' os: - ubuntu-latest -# - macOS-latest - arch: - - x86 - - x64 - exclude: - - os: macOS-latest - arch: x86 + - macOS-latest steps: - uses: actions/checkout@v4 - uses: julia-actions/setup-julia@v2 From a070ad3c5d2ce4082acc7e0ea147a3a55a371079 Mon Sep 17 00:00:00 2001 From: Sheehan Olver Date: Mon, 20 Apr 2026 14:57:56 +0100 Subject: [PATCH 2/5] Update ci.yml --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 53182a8c..67e693e9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: - macOS-latest steps: - uses: actions/checkout@v4 - - uses: julia-actions/setup-julia@v2 + - uses: julia-actions/setup-julia@v3 with: version: ${{ matrix.version }} arch: ${{ matrix.arch }} From e3d2dcddf29774dcad2dde69658650c2a15b2dab Mon Sep 17 00:00:00 2001 From: Sheehan Olver Date: Mon, 20 Apr 2026 15:00:57 +0100 Subject: [PATCH 3/5] Update ci.yml --- .github/workflows/ci.yml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 67e693e9..530d7c39 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,11 +16,10 @@ jobs: - ubuntu-latest - macOS-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - uses: julia-actions/setup-julia@v3 with: version: ${{ matrix.version }} - arch: ${{ matrix.arch }} show-versioninfo: true - uses: actions/cache@v3 env: @@ -32,10 +31,10 @@ jobs: ${{ runner.os }}-test-${{ env.cache-name }}- ${{ runner.os }}-test- ${{ runner.os }}- - - uses: julia-actions/julia-buildpkg@latest - - uses: julia-actions/julia-runtest@latest + - uses: julia-actions/julia-buildpkg@v1 + - uses: julia-actions/julia-runtest@v1 - uses: julia-actions/julia-processcoverage@v1 - - uses: codecov/codecov-action@v4 + - uses: codecov/codecov-action@v6 with: token: ${{ secrets.CODECOV_TOKEN }} file: lcov.info From 640c8a4af9a06a61dcbd9b36f6c34790e792bd95 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 9 Jul 2026 17:00:04 +0000 Subject: [PATCH 4/5] Fix BigFloat Julia 1.12 compatibility: use Memory-based mpfr_t pointers In Julia 1.12, BigFloat changed from a mutable struct (layout-compatible with mpfr_t) to an immutable struct wrapping Memory{Limb}. This broke the ccall to ft_mpfr_trmv_ptr and related functions that pass BigFloat arrays as mpfr_t** to the C library, causing a segfault (signal 11). Fix by: - Adding `_mpfr_ptr(b::BigFloat)` that extracts the correct mpfr_t*: - Julia >= 1.12: pointer(getfield(b, :d)) (start of the Memory) - Julia < 1.12: pointer_from_objref(b) (mutable BigFloat fields) - Adding `_mpfr_ptrs` for StridedVector and StridedMatrix that build a Vector{Ptr{mpfr_t}} for the C mpfr_t** argument - Updating all BigFloat ccalls (trmv/trsv/trmm/trsm _ptr variants) to use Ptr{Ptr{mpfr_t}} with GC.@preserve to keep BigFloat Memory alive --- src/libfasttransforms.jl | 64 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 58 insertions(+), 6 deletions(-) diff --git a/src/libfasttransforms.jl b/src/libfasttransforms.jl index 3ce492d9..dd694b15 100644 --- a/src/libfasttransforms.jl +++ b/src/libfasttransforms.jl @@ -49,6 +49,34 @@ function renew!(x::AbstractArray{BigFloat}) return x end +# Obtain a raw C pointer to the mpfr_t struct underlying a BigFloat. +# In Julia < 1.12, BigFloat was a mutable struct whose fields matched mpfr_t directly, +# so pointer_from_objref gives us the mpfr_t*. +# In Julia >= 1.12, BigFloat is an immutable struct wrapping Memory{Limb}, and the +# mpfr_t struct is stored at the start of that Memory. +@static if VERSION >= v"1.12" + _mpfr_ptr(b::BigFloat) = Ptr{mpfr_t}(pointer(getfield(b, :d))) +else + _mpfr_ptr(b::BigFloat) = Ptr{mpfr_t}(pointer_from_objref(b)) +end + +function _mpfr_ptrs(x::StridedVector{BigFloat}) + return [_mpfr_ptr(xi) for xi in x] +end + +function _mpfr_ptrs(x::StridedMatrix{BigFloat}) + m, n = size(x) + s = stride(x, 2) + len = n > 0 ? s * (n - 1) + m : 0 + ptrs = Vector{Ptr{mpfr_t}}(undef, len) + for j in 0:n-1 + for i in 0:m-1 + @inbounds ptrs[i + j*s + 1] = _mpfr_ptr(x[i+1, j+1]) + end + end + return ptrs +end + function horner!(f::Vector{Float64}, c::StridedVector{Float64}, x::Vector{Float64}) @assert length(x) == length(f) ccall((:ft_horner, libfasttransforms), Cvoid, (Cint, Ptr{Float64}, Cint, Cint, Ptr{Float64}, Ptr{Float64}), length(c), c, stride(c, 1), length(x), x, f) @@ -983,19 +1011,31 @@ for (fJ, fC) in ((:lmul!, :ft_mpfr_trmv_ptr), function $fJ(p::FTPlan{BigFloat, 1}, x::StridedVector{BigFloat}) checksize(p, x) checkstride(p, x) - ccall(($(string(fC)), libfasttransforms), Cvoid, (Cint, Cint, Ptr{mpfr_t}, Cint, Ptr{BigFloat}, Int32), 'N', p.n, p, p.n, renew!(x), Base.MPFR.ROUNDING_MODE[]) + renew!(x) + GC.@preserve x begin + ptrs = _mpfr_ptrs(x) + ccall(($(string(fC)), libfasttransforms), Cvoid, (Cint, Cint, Ptr{mpfr_t}, Cint, Ptr{Ptr{mpfr_t}}, Int32), 'N', p.n, p, p.n, ptrs, Base.MPFR.ROUNDING_MODE[]) + end return x end function $fJ(p::AdjointFTPlan{BigFloat, FTPlan{BigFloat, 1, K}}, x::StridedVector{BigFloat}) where K checksize(p, x) checkstride(p, x) - ccall(($(string(fC)), libfasttransforms), Cvoid, (Cint, Cint, Ptr{mpfr_t}, Cint, Ptr{BigFloat}, Int32), 'T', p.parent.n, p, p.parent.n, renew!(x), Base.MPFR.ROUNDING_MODE[]) + renew!(x) + GC.@preserve x begin + ptrs = _mpfr_ptrs(x) + ccall(($(string(fC)), libfasttransforms), Cvoid, (Cint, Cint, Ptr{mpfr_t}, Cint, Ptr{Ptr{mpfr_t}}, Int32), 'T', p.parent.n, p, p.parent.n, ptrs, Base.MPFR.ROUNDING_MODE[]) + end return x end function $fJ(p::TransposeFTPlan{BigFloat, FTPlan{BigFloat, 1, K}}, x::StridedVector{BigFloat}) where K checksize(p, x) checkstride(p, x) - ccall(($(string(fC)), libfasttransforms), Cvoid, (Cint, Cint, Ptr{mpfr_t}, Cint, Ptr{BigFloat}, Int32), 'T', p.parent.n, p, p.parent.n, renew!(x), Base.MPFR.ROUNDING_MODE[]) + renew!(x) + GC.@preserve x begin + ptrs = _mpfr_ptrs(x) + ccall(($(string(fC)), libfasttransforms), Cvoid, (Cint, Cint, Ptr{mpfr_t}, Cint, Ptr{Ptr{mpfr_t}}, Int32), 'T', p.parent.n, p, p.parent.n, ptrs, Base.MPFR.ROUNDING_MODE[]) + end return x end end @@ -1083,19 +1123,31 @@ for (fJ, fC) in ((:lmul!, :ft_mpfr_trmm_ptr), function $fJ(p::FTPlan{BigFloat, 1}, x::StridedMatrix{BigFloat}) checksize(p, x) checkstride(p, x) - ccall(($(string(fC)), libfasttransforms), Cvoid, (Cint, Cint, Ptr{mpfr_t}, Cint, Ptr{BigFloat}, Cint, Cint, Int32), 'N', p.n, p, p.n, renew!(x), stride(x, 2), size(x, 2), Base.MPFR.ROUNDING_MODE[]) + renew!(x) + GC.@preserve x begin + ptrs = _mpfr_ptrs(x) + ccall(($(string(fC)), libfasttransforms), Cvoid, (Cint, Cint, Ptr{mpfr_t}, Cint, Ptr{Ptr{mpfr_t}}, Cint, Cint, Int32), 'N', p.n, p, p.n, ptrs, stride(x, 2), size(x, 2), Base.MPFR.ROUNDING_MODE[]) + end return x end function $fJ(p::AdjointFTPlan{BigFloat, FTPlan{BigFloat, 1, K}}, x::StridedMatrix{BigFloat}) where K checksize(p, x) checkstride(p, x) - ccall(($(string(fC)), libfasttransforms), Cvoid, (Cint, Cint, Ptr{mpfr_t}, Cint, Ptr{BigFloat}, Cint, Cint, Int32), 'T', p.parent.n, p, p.parent.n, renew!(x), stride(x, 2), size(x, 2), Base.MPFR.ROUNDING_MODE[]) + renew!(x) + GC.@preserve x begin + ptrs = _mpfr_ptrs(x) + ccall(($(string(fC)), libfasttransforms), Cvoid, (Cint, Cint, Ptr{mpfr_t}, Cint, Ptr{Ptr{mpfr_t}}, Cint, Cint, Int32), 'T', p.parent.n, p, p.parent.n, ptrs, stride(x, 2), size(x, 2), Base.MPFR.ROUNDING_MODE[]) + end return x end function $fJ(p::TransposeFTPlan{BigFloat, FTPlan{BigFloat, 1, K}}, x::StridedMatrix{BigFloat}) where K checksize(p, x) checkstride(p, x) - ccall(($(string(fC)), libfasttransforms), Cvoid, (Cint, Cint, Ptr{mpfr_t}, Cint, Ptr{BigFloat}, Cint, Cint, Int32), 'T', p.parent.n, p, p.parent.n, renew!(x), stride(x, 2), size(x, 2), Base.MPFR.ROUNDING_MODE[]) + renew!(x) + GC.@preserve x begin + ptrs = _mpfr_ptrs(x) + ccall(($(string(fC)), libfasttransforms), Cvoid, (Cint, Cint, Ptr{mpfr_t}, Cint, Ptr{Ptr{mpfr_t}}, Cint, Cint, Int32), 'T', p.parent.n, p, p.parent.n, ptrs, stride(x, 2), size(x, 2), Base.MPFR.ROUNDING_MODE[]) + end return x end end From 84139eb343660a1d4bab1e6a510114cc8202eb75 Mon Sep 17 00:00:00 2001 From: Sheehan Olver Date: Fri, 10 Jul 2026 14:10:05 +0200 Subject: [PATCH 5/5] remove unnecessary wrappers --- src/libfasttransforms.jl | 28 ++++++---------------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/src/libfasttransforms.jl b/src/libfasttransforms.jl index dd694b15..320b9ee8 100644 --- a/src/libfasttransforms.jl +++ b/src/libfasttransforms.jl @@ -60,22 +60,6 @@ else _mpfr_ptr(b::BigFloat) = Ptr{mpfr_t}(pointer_from_objref(b)) end -function _mpfr_ptrs(x::StridedVector{BigFloat}) - return [_mpfr_ptr(xi) for xi in x] -end - -function _mpfr_ptrs(x::StridedMatrix{BigFloat}) - m, n = size(x) - s = stride(x, 2) - len = n > 0 ? s * (n - 1) + m : 0 - ptrs = Vector{Ptr{mpfr_t}}(undef, len) - for j in 0:n-1 - for i in 0:m-1 - @inbounds ptrs[i + j*s + 1] = _mpfr_ptr(x[i+1, j+1]) - end - end - return ptrs -end function horner!(f::Vector{Float64}, c::StridedVector{Float64}, x::Vector{Float64}) @assert length(x) == length(f) @@ -1013,7 +997,7 @@ for (fJ, fC) in ((:lmul!, :ft_mpfr_trmv_ptr), checkstride(p, x) renew!(x) GC.@preserve x begin - ptrs = _mpfr_ptrs(x) + ptrs = map(_mpfr_ptr, x) ccall(($(string(fC)), libfasttransforms), Cvoid, (Cint, Cint, Ptr{mpfr_t}, Cint, Ptr{Ptr{mpfr_t}}, Int32), 'N', p.n, p, p.n, ptrs, Base.MPFR.ROUNDING_MODE[]) end return x @@ -1023,7 +1007,7 @@ for (fJ, fC) in ((:lmul!, :ft_mpfr_trmv_ptr), checkstride(p, x) renew!(x) GC.@preserve x begin - ptrs = _mpfr_ptrs(x) + ptrs = map(_mpfr_ptr, x) ccall(($(string(fC)), libfasttransforms), Cvoid, (Cint, Cint, Ptr{mpfr_t}, Cint, Ptr{Ptr{mpfr_t}}, Int32), 'T', p.parent.n, p, p.parent.n, ptrs, Base.MPFR.ROUNDING_MODE[]) end return x @@ -1033,7 +1017,7 @@ for (fJ, fC) in ((:lmul!, :ft_mpfr_trmv_ptr), checkstride(p, x) renew!(x) GC.@preserve x begin - ptrs = _mpfr_ptrs(x) + ptrs = map(_mpfr_ptr, x) ccall(($(string(fC)), libfasttransforms), Cvoid, (Cint, Cint, Ptr{mpfr_t}, Cint, Ptr{Ptr{mpfr_t}}, Int32), 'T', p.parent.n, p, p.parent.n, ptrs, Base.MPFR.ROUNDING_MODE[]) end return x @@ -1125,7 +1109,7 @@ for (fJ, fC) in ((:lmul!, :ft_mpfr_trmm_ptr), checkstride(p, x) renew!(x) GC.@preserve x begin - ptrs = _mpfr_ptrs(x) + ptrs = map(_mpfr_ptr, x) ccall(($(string(fC)), libfasttransforms), Cvoid, (Cint, Cint, Ptr{mpfr_t}, Cint, Ptr{Ptr{mpfr_t}}, Cint, Cint, Int32), 'N', p.n, p, p.n, ptrs, stride(x, 2), size(x, 2), Base.MPFR.ROUNDING_MODE[]) end return x @@ -1135,7 +1119,7 @@ for (fJ, fC) in ((:lmul!, :ft_mpfr_trmm_ptr), checkstride(p, x) renew!(x) GC.@preserve x begin - ptrs = _mpfr_ptrs(x) + ptrs = map(_mpfr_ptr, x) ccall(($(string(fC)), libfasttransforms), Cvoid, (Cint, Cint, Ptr{mpfr_t}, Cint, Ptr{Ptr{mpfr_t}}, Cint, Cint, Int32), 'T', p.parent.n, p, p.parent.n, ptrs, stride(x, 2), size(x, 2), Base.MPFR.ROUNDING_MODE[]) end return x @@ -1145,7 +1129,7 @@ for (fJ, fC) in ((:lmul!, :ft_mpfr_trmm_ptr), checkstride(p, x) renew!(x) GC.@preserve x begin - ptrs = _mpfr_ptrs(x) + ptrs = map(_mpfr_ptr, x) ccall(($(string(fC)), libfasttransforms), Cvoid, (Cint, Cint, Ptr{mpfr_t}, Cint, Ptr{Ptr{mpfr_t}}, Cint, Cint, Int32), 'T', p.parent.n, p, p.parent.n, ptrs, stride(x, 2), size(x, 2), Base.MPFR.ROUNDING_MODE[]) end return x