@@ -70,17 +70,159 @@ is_invalid_linear(data, index) = index isa Integer && IndexStyle(data) isa Index
7070# level) are identified by their length, since they keep their dimensions.
7171is_trivial_point (data, index) = isone (length (data)) && index == CartesianIndex ()
7272
73+ # ---------------------------------------------------------------------------
74+ # Flat (constant-folded linear) leaf access.
75+ #
76+ # A point access reads `ncomponents(data)` entries out of the parent buffer. When
77+ # the parent has `IndexLinear` storage, `getindex(data, index)` used to route
78+ # through the Cartesian `get_struct`, which reads `parent[CartesianIndex(...)]`.
79+ # For a 5-D `Array` that generates the full 5-D linear-index arithmetic per tap,
80+ # and for a property-view `SubArray` it generates Base's linear-to-Cartesian
81+ # reindex -- the residual per-access bloat that inflates deeply-inlined stencil
82+ # expression bodies (and, for `SubArray`s, costs `div`/`rem` at runtime).
83+ #
84+ # `flat_mode(data)` picks one of three fully-static leaf-access strategies (the
85+ # `Val` is a pure function of the type, so the branch folds away and only the
86+ # selected `_{get,set}idx_flat` method is compiled per concrete layout):
87+ #
88+ # Val(1) parent is `IndexLinear` and every extent is inferable: index the
89+ # parent directly with a constant-folded affine index. For a plain
90+ # `Array` this is a native `arrayref`; for a fast property-view
91+ # `SubArray` (e.g. `VIJHF`, F trailing) Base lowers `sub[i::Int]` to
92+ # `parent(sub)[offset1 + i]` -- a single load into the root buffer.
93+ # Val(2) parent is an `IndexCartesian` property-view `SubArray` (e.g. a
94+ # `VIJFH` field view, F not trailing) but the layout is shape-linear
95+ # (all logical extents from the F axis onward are 1, the column case):
96+ # reduce to the root `Array` directly with an affine index that is
97+ # fully folded except for one hoistable `first(indices)` load, so no
98+ # Base reindex / `div` / `rem` is instantiated.
99+ # Val(0) everything else (dynamic `Nh`, or a true box `VIJFH` field view with
100+ # `Nh > 1`): the unchanged Cartesian path, which stays correct.
101+ @inline function flat_mode (data:: DataLayout )
102+ has_inferred_size (data) || return Val (0 )
103+ IndexStyle (data) isa IndexLinear && return Val (1 )
104+ F = f_dim (data)
105+ F isa Integer || return Val (0 )
106+ # The root path omits every logical coordinate at or beyond the F axis, so it
107+ # is only correct when those extents are all 1 (the column case). Unlike the
108+ # layout IndexStyle, a `ncomponents <= 1` escape is NOT valid here: a scalar
109+ # field view of a box (Nh > 1) still needs its h-term, which we drop.
110+ all_ones (inferred_size (data)[F: end ]. .. ) &&
111+ reducible_property_view (parent_type (data), Val (F)) && return Val (2 )
112+ return Val (0 )
113+ end
114+
115+ # A property-view SubArray (as built by struct_field_view) of an IndexLinear
116+ # array, whose indices are full `Slice`s except for a `UnitRange` at position F.
117+ # Reducing such a view to its root is a constant-stride affine map, so the flat
118+ # path can bypass Base's linear-to-Cartesian reindex.
119+ @inline reducible_property_view (:: Type , :: Val ) = false
120+ @inline reducible_property_view (
121+ :: Type{<:SubArray{<:Any, N, PA, I}} ,
122+ :: Val{F} ,
123+ ) where {N, PA, I, F} =
124+ F isa Integer && 1 <= F <= N && IndexStyle (PA) isa IndexLinear ?
125+ _pv_indices_ok (I, Val (F), Val (N)) : false
126+ @generated function _pv_indices_ok (:: Type{I} , :: Val{F} , :: Val{N} ) where {I, F, N}
127+ for k in 1 : N
128+ ft = fieldtype (I, k)
129+ ok = k == F ? (ft <: AbstractUnitRange && ! (ft <: Base.Slice )) : (ft <: Base.Slice )
130+ ok || return :(false )
131+ end
132+ return :(true )
133+ end
134+
135+ # Column-major linear offset (0-based) of coordinate tuple `idx` in an array of
136+ # static size `sz`. Tail recursion over tuples inlines to a folded Horner form;
137+ # `sz` entries are type-domain literals, only the coordinates are runtime values.
138+ @inline _col_major_offset (:: Tuple{} , :: Tuple{} ) = 0
139+ @inline _col_major_offset (sz:: Tuple , idx:: Tuple ) =
140+ (idx[1 ] - 1 ) + sz[1 ] * _col_major_offset (Base. tail (sz), Base. tail (idx))
141+
142+ @inline _first_n (t:: Tuple , :: Val{n} ) where {n} = ntuple (k -> t[k], Val (n))
143+
144+ # (base, fstride) for the Val(1) parent path. `base` is the linear position of
145+ # the first component of point `index`; `fstride` is the spacing between
146+ # successive components. For IndexLinear layouts the components form contiguous
147+ # length-`prod(inferred_size)` blocks, so `fstride == prod(inferred_size(data))`
148+ # and a linear point index maps to itself.
149+ @inline _flat_point_offset (data:: DataLayout , index:: Integer ) =
150+ (index, prod (inferred_size (data)))
151+ @inline _flat_point_offset (data:: DataLayout , index:: CartesianIndex ) =
152+ (_col_major_offset (inferred_size (data), Tuple (index)) + 1 , prod (inferred_size (data)))
153+
154+ # (root, base, fstride) for the Val(2) property-view root path. `fstart` (the
155+ # start of the field range) is the only runtime input; everything else folds
156+ # from the static shape. Shape-linearity guarantees every logical coordinate at
157+ # or beyond the F axis is 1, so only the `F - 1` leading dims contribute.
158+ @inline function _flat_root_offset (
159+ data:: DataLayout ,
160+ index:: CartesianIndex ,
161+ fstart,
162+ :: Val{F} ,
163+ ) where {F}
164+ before_sz = _first_n (inferred_size (data), Val (F - 1 ))
165+ before_idx = _first_n (Tuple (index), Val (F - 1 ))
166+ fstride = prod (before_sz) # root stride of the F axis
167+ base = _col_major_offset (before_sz, before_idx) + (fstart - 1 ) * fstride + 1
168+ return (base, fstride)
169+ end
170+ @inline _root_and_fstart (data:: DataLayout , :: Val{F} ) where {F} =
171+ (parent (parent (data)), first (parentindices (parent (data))[F]))
172+
173+ @propagate_inbounds _getidx_flat (:: Val{0} , data, index) =
174+ get_struct (parent (data), eltype (data), index, Val (f_dim (data)))
175+ @propagate_inbounds function _getidx_flat (:: Val{1} , data, index)
176+ (base, fstride) = _flat_point_offset (data, index)
177+ return get_struct_linear (
178+ parent (data),
179+ eltype (data),
180+ base,
181+ fstride,
182+ Val (ncomponents (data)),
183+ )
184+ end
185+ @propagate_inbounds function _getidx_flat (:: Val{2} , data, index)
186+ (root, fstart) = _root_and_fstart (data, Val (f_dim (data)))
187+ (base, fstride) = _flat_root_offset (data, index, fstart, Val (f_dim (data)))
188+ return get_struct_linear (root, eltype (data), base, fstride, Val (ncomponents (data)))
189+ end
190+
191+ @propagate_inbounds _setidx_flat! (:: Val{0} , data, value, index) =
192+ set_struct! (parent (data), convert (eltype (data), value), index, Val (f_dim (data)))
193+ @propagate_inbounds function _setidx_flat! (:: Val{1} , data, value, index)
194+ (base, fstride) = _flat_point_offset (data, index)
195+ return set_struct_linear! (
196+ parent (data),
197+ convert (eltype (data), value),
198+ base,
199+ fstride,
200+ Val (ncomponents (data)),
201+ )
202+ end
203+ @propagate_inbounds function _setidx_flat! (:: Val{2} , data, value, index)
204+ (root, fstart) = _root_and_fstart (data, Val (f_dim (data)))
205+ (base, fstride) = _flat_root_offset (data, index, fstart, Val (f_dim (data)))
206+ return set_struct_linear! (
207+ root,
208+ convert (eltype (data), value),
209+ base,
210+ fstride,
211+ Val (ncomponents (data)),
212+ )
213+ end
214+
73215# Always convert to the element type of a DataLayout when modifying its values.
74216# Represent every single-point DataLayout view using a zero-dimensional DataF.
75217@propagate_inbounds Base. setindex! (data:: DataLayout , value, index:: PointIndex ) =
76218 is_invalid_linear (data, index) ? setindex! (data, value, CartesianIndices (data)[index]) :
77219 is_trivial_point (data, index) ?
78220 set_struct! (parent (data), convert (eltype (data), value)) :
79- set_struct! ( parent (data), convert ( eltype ( data) , value) , index, Val ( f_dim (data)) )
221+ _setidx_flat! ( flat_mode (data), data, value, index)
80222@propagate_inbounds Base. getindex (data:: DataLayout , index:: PointIndex ) =
81223 is_invalid_linear (data, index) ? getindex (data, CartesianIndices (data)[index]) :
82224 is_trivial_point (data, index) ? get_struct (parent (data), eltype (data)) :
83- get_struct ( parent (data), eltype ( data) , index, Val ( f_dim (data)) )
225+ _getidx_flat ( flat_mode (data), data, index)
84226@propagate_inbounds Base. view (data:: DataLayout , index:: PointIndex ) =
85227 is_invalid_linear (data, index) ? view (data, CartesianIndices (data)[index]) :
86228 is_trivial_point (data, index) ? data :
0 commit comments