Skip to content

Commit 2140c1f

Browse files
maleadtclaude
andauthored
Store oneArray offset in bytes instead of elements (#570)
The element-based offset was lossy when materializing reinterpret on views with non-aligned offsets (e.g., reinterpreting a view of Int32 as Int64). The byte offset would get truncated by integer division when converting to the new element count. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent bcf9377 commit 2140c1f

2 files changed

Lines changed: 21 additions & 10 deletions

File tree

src/array.jl

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ mutable struct oneArray{T,N,B} <: AbstractGPUArray{T,N}
8585
data::DataRef{B}
8686

8787
maxsize::Int # maximum data size; excluding any selector bytes
88-
offset::Int # offset of the data in the buffer, in number of elements
88+
offset::Int # offset of the data in the buffer, in bytes
8989
dims::Dims{N}
9090

9191
function oneArray{T,N,B}(::UndefInitializer, dims::Dims{N}) where {T,N,B}
@@ -337,11 +337,11 @@ function Base.unsafe_convert(::Type{Ptr{T}}, x::oneArray{T}) where {T}
337337
if is_device(x)
338338
throw(ArgumentError("cannot take the CPU address of a $(typeof(x))"))
339339
end
340-
convert(Ptr{T}, x.data[]) + x.offset*Base.elsize(x)
340+
convert(Ptr{T}, x.data[]) + x.offset
341341
end
342342

343343
function Base.unsafe_convert(::Type{ZePtr{T}}, x::oneArray{T}) where {T}
344-
convert(ZePtr{T}, x.data[]) + x.offset*Base.elsize(x)
344+
convert(ZePtr{T}, x.data[]) + x.offset
345345
end
346346

347347

@@ -363,15 +363,19 @@ end
363363

364364
function Base.unsafe_convert(::Type{oneDeviceArray{T,N,AS.CrossWorkgroup}}, a::oneArray{T,N}) where {T,N}
365365
oneDeviceArray{T,N,AS.CrossWorkgroup}(size(a), reinterpret(LLVMPtr{T,AS.CrossWorkgroup}, pointer(a)),
366-
a.maxsize - a.offset*Base.elsize(a))
366+
a.maxsize - a.offset)
367367
end
368368

369369

370370
## memory copying
371371

372372
typetagdata(a::Array, i=1) = ccall(:jl_array_typetagdata, Ptr{UInt8}, (Any,), a) + i - 1
373-
typetagdata(a::oneArray, i=1) =
374-
convert(ZePtr{UInt8}, a.data[]) + a.maxsize + a.offset + i - 1
373+
function typetagdata(a::oneArray, i=1)
374+
# for zero-size element types (e.g. singleton unions), the byte offset
375+
# is always zero, so the corresponding element offset is also zero
376+
elem_offset = iszero(Base.elsize(a)) ? 0 : a.offset ÷ Base.elsize(a)
377+
return convert(ZePtr{UInt8}, a.data[]) + a.maxsize + elem_offset + i - 1
378+
end
375379

376380
function Base.copyto!(dest::oneArray{T}, doffs::Integer, src::Array{T}, soffs::Integer,
377381
n::Integer) where T
@@ -536,12 +540,10 @@ end
536540
## derived arrays
537541

538542
function GPUArrays.derive(::Type{T}, a::oneArray, dims::Dims{N}, offset::Int) where {T,N}
539-
offset = if sizeof(T) == 0
543+
if sizeof(T) == 0
540544
Base.elsize(a) == 0 || error("Cannot derive a singleton array from non-singleton inputs")
541-
offset
542-
else
543-
(a.offset * Base.elsize(a)) ÷ sizeof(T) + offset
544545
end
546+
offset = a.offset + offset * sizeof(T)
545547
oneArray{T,N}(a.data, dims; a.maxsize, offset)
546548
end
547549

test/array.jl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,15 @@ end
4343
@test Array(xs) == [0,1,0]
4444
end
4545

46+
@testset "reinterpret of view with non-aligned offset" begin
47+
# reinterpreting a view to a larger element type where the byte offset
48+
# is not a multiple of the new element size
49+
a = oneArray(Int32[1,2,3,4,5,6,7,8,9])
50+
v = view(a, 2:7) # offset of 1 Int32 = 4 bytes
51+
r = reinterpret(Int64, v) # Int64 = 8 bytes; 4 is not a multiple of 8
52+
@test Array(r) == reinterpret(Int64, @view Array(a)[2:7])
53+
end
54+
4655
@testset "shared buffers & unsafe_wrap" begin
4756
a = oneVector{Int,oneL0.SharedBuffer}(undef, 2)
4857

0 commit comments

Comments
 (0)