From 6569c9ddd9a77f5df9f1e798e0f1d42561fdd2ee Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Fri, 25 Sep 2026 10:13:52 +0200 Subject: [PATCH] Rebuild reshaped and permuted arrays without validation in @Const. Adapt.jl's rules for ReshapedArray and PermutedDimsArray go through constructors whose error paths build strings, which fails to compile when `constify` runs inside a GPU kernel. Reuse the existing fields instead, as adapting only replaces the parent array. Fixes #792 --- src/KernelAbstractions.jl | 11 +++++++++++ test/test.jl | 19 +++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/src/KernelAbstractions.jl b/src/KernelAbstractions.jl index 4fbb7fb1e..e5186cfc0 100644 --- a/src/KernelAbstractions.jl +++ b/src/KernelAbstractions.jl @@ -449,6 +449,17 @@ Adapt.adapt_storage(::Backend, x) constify(arg) = adapt(ConstAdaptor(), arg) +# `constify` runs inside the kernel, where wrappers must be rebuilt without re-validating +# them: Adapt.jl's rules for these wrappers go through constructors whose error paths build +# strings, which does not compile for GPUs. Adapting only replaces the parent array, so the +# existing fields remain valid. +Adapt.adapt_structure(to::ConstAdaptor, A::Base.ReshapedArray) = + Base.ReshapedArray(adapt(to, parent(A)), size(A), A.mi) +@eval function Adapt.adapt_structure(to::ConstAdaptor, A::PermutedDimsArray{T, N, perm, iperm}) where {T, N, perm, iperm} + P = adapt(to, parent(A)) + return $(Expr(:new, :(PermutedDimsArray{eltype(P), N, perm, iperm, typeof(P)}), :P)) +end + include("nditeration.jl") using .NDIteration import .NDIteration: get diff --git a/test/test.jl b/test/test.jl index 06987d37f..199990c50 100644 --- a/test/test.jl +++ b/test/test.jl @@ -313,6 +313,25 @@ function unittest_testsuite(Backend, backend_str, backend_mod, BackendArrayT; sk constarg2d(Backend(), (8, 8))(A, B, ndrange = size(A)) synchronize(Backend()) @test all(Array(A) .== 1.0f0) + + # wrapped arrays are rebuilt around the constified array inside the kernel + host = Float32.(reshape(1:25, 5, 5)) + dev = adapt(Backend(), host) + for (B, ref) in ( + (vec(view(dev, 1:4, 1:4)), vec(view(host, 1:4, 1:4))), + (reshape(view(dev, 1:4, 1:4), 2, 8), reshape(view(host, 1:4, 1:4), 2, 8)), + (reshape(view(dev, :, 2:3), 2, 5), reshape(view(host, :, 2:3), 2, 5)), + (PermutedDimsArray(dev, (2, 1)), PermutedDimsArray(host, (2, 1))), + ) + A = KernelAbstractions.zeros(Backend(), Float32, size(B)) + if ndims(B) == 1 + constarg(Backend(), 8)(A, B, ndrange = size(A)) + else + constarg2d(Backend(), (4, 4))(A, B, ndrange = size(A)) + end + synchronize(Backend()) + @test Array(A) == ref + end end @kernel function kernel_val!(a, ::Val{m}) where {m}