Skip to content

Commit 0bd80d1

Browse files
AntonOrestenclaude
authored andcommitted
Handle 0-dim broadcasts, reject host-side leaves
0-dim destinations previously died with an IR error; they now run as a 1-element 1-D broadcast (0-dim leaves are reshaped to 1-element vectors, since the kernel path assumes rank >= 1). CPU Array leaves previously compiled and crashed with an illegal memory access on the device (poisoning the CUDA context), and ranges failed with a cryptic pointer MethodError; both now throw a clear ArgumentError on the host before launch. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 5526e6a commit 0bd80d1

2 files changed

Lines changed: 42 additions & 0 deletions

File tree

src/broadcast.jl

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,24 @@ struct BroadcastLeaf{P, A}
5656
end
5757
BroadcastLeaf{P}(arr::A) where {P, A} = BroadcastLeaf{P, A}(arr)
5858

59+
# Reject leaves the kernel cannot address: a host Array would compile fine but
60+
# dereference a CPU pointer on device (illegal memory access, poisoning the
61+
# CUDA context), and ranges have no pointer at all.
62+
function _check_device_leaf(arr::AbstractArray)
63+
root = arr
64+
while (p = parent(root)) !== root
65+
root = p
66+
end
67+
if root isa Array || root isa AbstractRange
68+
throw(ArgumentError("cuTile broadcast requires device arrays, got $(typeof(arr))"))
69+
end
70+
end
71+
5972
_to_tiled_bc(t::Tiled, dsize) = _to_tiled_bc(parent(t), dsize)
73+
# 0-dim leaves become 1-element vectors; the kernel path assumes rank >= 1.
74+
_to_tiled_bc(arr::AbstractArray{<:Any,0}, dsize::Dims) = _to_tiled_bc(reshape(arr, 1), dsize)
6075
function _to_tiled_bc(arr::AbstractArray, dsize::Dims{N}) where N
76+
_check_device_leaf(arr)
6177
ta = TileArray(arr)
6278
ndims(arr) == N && size(arr) == dsize && return ta
6379
P = ntuple(d -> size(arr, d) == 1 && dsize[d] > 1, N)
@@ -70,12 +86,20 @@ function _to_tiled_bc(bc::Broadcasted, dsize)
7086
Broadcasted{Nothing}(bc.f, new_args, nothing)
7187
end
7288

89+
# 0-dim destinations: the kernel path needs at least one dimension, so run as
90+
# a 1-element 1-D broadcast (0-dim leaves load rank-0 tiles and expand).
91+
function _tiled_broadcast!(dest::AbstractArray{T,0}, bc::Broadcasted) where T
92+
Base.Broadcast.check_broadcast_axes(axes(dest), bc.args...)
93+
_tiled_broadcast!(reshape(dest, 1), bc)
94+
end
95+
7396
function _tiled_broadcast!(dest::AbstractArray{T,N}, bc::Broadcasted) where {T, N}
7497
# Reject shapes Base broadcasting would reject (throws DimensionMismatch);
7598
# size-1 dims are legal and expanded per-leaf in the kernel.
7699
Base.Broadcast.check_broadcast_axes(axes(dest), bc.args...)
77100
isempty(dest) && return
78101

102+
_check_device_leaf(dest)
79103
dest_ta = TileArray(dest)
80104
tiled_bc = _to_tiled_bc(bc, size(dest))
81105

test/host/broadcast.jl

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,24 @@ using CUDA
196196
@test isempty(Array(D))
197197
end
198198

199+
@testset "0-dim arrays" begin
200+
C = CUDA.zeros(Float32)
201+
ct.Tiled(C) .= 1.0f0
202+
@test Array(C)[] == 1.0f0
203+
204+
A = CUDA.fill(2.0f0)
205+
ct.Tiled(C) .= ct.Tiled(A) .+ 1.0f0
206+
@test Array(C)[] == 3.0f0
207+
end
208+
209+
@testset "host-side leaves rejected" begin
210+
C = CUDA.zeros(Float32, 16)
211+
A_cpu = rand(Float32, 16)
212+
@test_throws ArgumentError ct.Tiled(C) .= ct.Tiled(A_cpu) .* 2.0f0
213+
@test_throws ArgumentError ct.Tiled(C) .= ct.Tiled(C) .+ (1:16)
214+
@test_throws ArgumentError ct.Tiled(A_cpu) .= 0 # CPU destination
215+
end
216+
199217
@testset "ct.@. returns destination array" begin
200218
n = 256
201219
A = CUDA.rand(Float32, n)

0 commit comments

Comments
 (0)