carray 2.0.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.yardopts +5 -25
- data/CHANGELOG.md +16 -0
- data/LICENSE +1 -1
- data/NEWS.md +3 -0
- data/README.md +128 -44
- data/carray.gemspec +22 -24
- data/ext/ca_array_pool.c +91 -0
- data/ext/ca_axis_descriptor.h +186 -0
- data/ext/ca_axis_dispatch.c +924 -0
- data/ext/ca_axis_group.c +1208 -0
- data/ext/ca_bincmp_dispatch.c +76 -0
- data/ext/ca_bincmp_dispatch.h +85 -0
- data/ext/ca_binop_dispatch.c +125 -0
- data/ext/ca_binop_dispatch.h +159 -0
- data/ext/ca_categorical_iterator.c +1375 -0
- data/ext/ca_compare.c +94 -0
- data/ext/ca_compare.h +26 -0
- data/ext/ca_composite_dispatch.c +414 -0
- data/ext/ca_composite_dispatch.h +116 -0
- data/ext/ca_for_buffer.h +96 -0
- data/ext/ca_for_each_element.h +241 -0
- data/ext/ca_group_iter.c +304 -0
- data/ext/ca_iter_substrate.h +325 -0
- data/ext/ca_kernel_iterator.c +4321 -0
- data/ext/ca_kernel_iterator.h +2603 -0
- data/ext/ca_moncmp_dispatch.c +37 -0
- data/ext/ca_moncmp_dispatch.h +62 -0
- data/ext/ca_monop_dispatch.c +200 -0
- data/ext/ca_monop_dispatch.h +235 -0
- data/ext/ca_obj_array.c +355 -359
- data/ext/ca_obj_bincmp.c +809 -0
- data/ext/ca_obj_binop.c +892 -0
- data/ext/ca_obj_bitarray.c +369 -164
- data/ext/ca_obj_bitfield.c +294 -234
- data/ext/ca_obj_block.c +189 -711
- data/ext/ca_obj_byte_swap.c +766 -0
- data/ext/ca_obj_const_string.c +965 -0
- data/ext/ca_obj_face.c +670 -0
- data/ext/ca_obj_face.h +247 -0
- data/ext/ca_obj_fake.c +228 -100
- data/ext/ca_obj_farray.c +54 -441
- data/ext/ca_obj_field.c +82 -529
- data/ext/ca_obj_fixlen_string.c +306 -0
- data/ext/ca_obj_grid.c +858 -440
- data/ext/ca_obj_meld.c +1034 -0
- data/ext/ca_obj_moncmp.c +569 -0
- data/ext/ca_obj_monop.c +1111 -0
- data/ext/ca_obj_object.c +774 -298
- data/ext/ca_obj_record.c +468 -0
- data/ext/ca_obj_reduce.c +97 -82
- data/ext/ca_obj_refer.c +569 -459
- data/ext/ca_obj_remap.c +475 -0
- data/ext/ca_obj_repeat.c +92 -477
- data/ext/ca_obj_roll.c +616 -0
- data/ext/ca_obj_select.c +344 -296
- data/ext/ca_obj_select_axis.c +1296 -0
- data/ext/ca_obj_shift.c +230 -792
- data/ext/ca_obj_source.c +78 -0
- data/ext/ca_obj_stack.c +1173 -0
- data/ext/ca_obj_stride.c +2501 -0
- data/ext/ca_obj_string.c +268 -0
- data/ext/ca_obj_tile.c +614 -0
- data/ext/ca_obj_time.c +546 -0
- data/ext/ca_obj_timedelta.c +435 -0
- data/ext/ca_obj_transpose.c +62 -516
- data/ext/ca_obj_triop.c +746 -0
- data/ext/ca_obj_unbound_repeat.c +208 -241
- data/ext/ca_obj_window.c +1131 -563
- data/ext/ca_op_byte_swap.c +175 -0
- data/ext/ca_op_ipower.c +319 -0
- data/ext/ca_op_powi.h +88 -0
- data/ext/ca_sort_kernels.h +132 -0
- data/ext/ca_sweep_engine.c +430 -0
- data/ext/ca_sweep_engine.h +157 -0
- data/ext/ca_transform_common.c +228 -0
- data/ext/ca_triop_dispatch.c +55 -0
- data/ext/ca_triop_dispatch.h +62 -0
- data/ext/carray.h +795 -402
- data/ext/carray_access.c +831 -711
- data/ext/carray_attribute.c +98 -330
- data/ext/carray_bincount.c +255 -0
- data/ext/carray_broadcast.c +283 -0
- data/ext/carray_call_cfunc.c +1360 -828
- data/ext/carray_call_cfunc.h +160 -0
- data/ext/carray_cast.c +1212 -301
- data/ext/carray_cast_func.rb +81 -40
- data/ext/carray_class.c +53 -63
- data/ext/carray_config.h +28 -0
- data/ext/carray_conversion.c +350 -346
- data/ext/carray_copy.c +156 -268
- data/ext/carray_core.c +1342 -199
- data/ext/carray_count.c +312 -0
- data/ext/carray_data_type.c +43 -19
- data/ext/carray_element.c +585 -213
- data/ext/carray_factorize.c +2542 -0
- data/ext/carray_generate.c +230 -559
- data/ext/carray_histogram.c +490 -0
- data/ext/carray_hold.c +228 -0
- data/ext/carray_index_classifier.c +1035 -0
- data/ext/carray_index_classifier.h +27 -0
- data/ext/carray_internal.h +120 -0
- data/ext/carray_kernels_bincmp.c +4445 -0
- data/ext/carray_kernels_binop.c +10979 -0
- data/ext/carray_kernels_init.c +36 -0
- data/ext/carray_kernels_map.c +3466 -0
- data/ext/carray_kernels_moncmp.c +2096 -0
- data/ext/carray_kernels_monop.c +18312 -0
- data/ext/carray_kernels_reduce_aggregate.c +25836 -0
- data/ext/carray_kernels_reduce_boolean.c +329 -0
- data/ext/carray_kernels_reduce_cumulative.c +14592 -0
- data/ext/carray_kernels_reduce_extreme.c +16947 -0
- data/ext/carray_kernels_reduce_variance.c +3909 -0
- data/ext/carray_kernels_scan.c +3692 -0
- data/ext/carray_kernels_search.c +32137 -0
- data/ext/carray_kernels_sort.c +10625 -0
- data/ext/carray_kernels_triop.c +1391 -0
- data/ext/carray_lazy.c +567 -0
- data/ext/carray_loop.c +88 -200
- data/ext/carray_mask.c +848 -154
- data/ext/carray_math_kernel.h +120 -0
- data/ext/carray_mathfunc.c +10 -241
- data/ext/carray_median_percentile.c +1257 -0
- data/ext/carray_memory_view.c +1625 -0
- data/ext/carray_operator.c +1526 -318
- data/ext/carray_order.c +664 -1394
- data/ext/carray_partition.c +416 -0
- data/ext/carray_random.c +518 -0
- data/ext/carray_scatter.c +357 -0
- data/ext/carray_slab.c +1219 -0
- data/ext/carray_slab.h +84 -0
- data/ext/carray_sort.c +829 -0
- data/ext/carray_sort_kernel.c +620 -0
- data/ext/carray_struct.c +695 -0
- data/ext/carray_test.c +343 -229
- data/ext/carray_undef.c +34 -17
- data/ext/carray_utils.c +175 -74
- data/ext/extconf.rb +216 -55
- data/ext/mk_call_cfunc.rb +480 -0
- data/ext/mkkernel.rb +8842 -0
- data/ext/ruby_carray.c +202 -101
- data/ext/version.h +4 -14
- data/ext/version.rb +5 -13
- data/lib/carray/arrow_tensor.rb +401 -0
- data/lib/carray/attribute.rb +166 -0
- data/lib/carray/autoload_carray.rb +220 -0
- data/lib/carray/autoload_method_extension.rb +44 -0
- data/lib/carray/axis_group.rb +711 -0
- data/lib/carray/basics.rb +481 -0
- data/lib/carray/bincount_nd.rb +358 -0
- data/lib/carray/block_iterator.rb +604 -0
- data/lib/carray/boolean_reduce.rb +109 -0
- data/lib/carray/categorical.rb +561 -0
- data/lib/carray/categorical_iterator.rb +1062 -0
- data/lib/carray/complex.rb +150 -0
- data/lib/carray/conditional.rb +216 -0
- data/lib/carray/const_string.rb +228 -0
- data/lib/carray/construct.rb +139 -328
- data/lib/carray/core_extensions.rb +240 -0
- data/lib/carray/data_type_extension.rb +233 -0
- data/lib/carray/fixlen_string.rb +95 -0
- data/lib/carray/frame/concat.rb +132 -0
- data/lib/carray/frame/convert.rb +95 -0
- data/lib/carray/frame/csv_parser.rb +211 -0
- data/lib/carray/frame/frame.rb +649 -0
- data/lib/carray/frame/group.rb +186 -0
- data/lib/carray/frame/io.rb +164 -0
- data/lib/carray/frame/join.rb +248 -0
- data/lib/carray/frame/records.rb +99 -0
- data/lib/carray/frame/sort.rb +113 -0
- data/lib/carray/frame/verbs.rb +299 -0
- data/lib/carray/frame.rb +16 -0
- data/lib/carray/histogram.rb +512 -0
- data/lib/carray/inspect.rb +37 -20
- data/lib/carray/iterator.rb +57 -349
- data/lib/carray/lazy.rb +889 -0
- data/lib/carray/mask_gap_fill.rb +200 -0
- data/lib/carray/math.rb +78 -342
- data/lib/carray/meld_reduce.rb +289 -0
- data/lib/carray/methods/align_addr.rb +116 -0
- data/lib/carray/methods/bin.rb +128 -0
- data/lib/carray/methods/bincount.rb +87 -0
- data/lib/carray/methods/bit_string.rb +92 -0
- data/lib/carray/methods/broadcast.rb +63 -0
- data/lib/carray/methods/choose.rb +39 -0
- data/lib/carray/methods/composition.rb +280 -0
- data/lib/carray/methods/gather_nd.rb +206 -0
- data/lib/carray/methods/index.rb +39 -0
- data/lib/carray/methods/insert_block.rb +99 -0
- data/lib/carray/methods/is_in.rb +141 -0
- data/lib/carray/methods/join.rb +90 -0
- data/lib/carray/methods/locate_addr.rb +47 -0
- data/lib/carray/methods/mask_duplicates.rb +41 -0
- data/lib/carray/methods/meshgrid.rb +91 -0
- data/lib/carray/methods/mode.rb +126 -0
- data/lib/carray/methods/nunique.rb +46 -0
- data/lib/carray/methods/resize.rb +56 -0
- data/lib/carray/methods/snap.rb +156 -0
- data/lib/carray/methods/string_format.rb +57 -0
- data/lib/carray/methods/unique.rb +47 -0
- data/lib/carray/methods/value_counts.rb +71 -0
- data/lib/carray/mkmf.rb +124 -101
- data/lib/carray/runtime.rb +108 -0
- data/lib/carray/serialize.rb +478 -167
- data/lib/carray/slab_iterator.rb +292 -0
- data/lib/carray/stack.rb +291 -0
- data/lib/carray/string.rb +56 -180
- data/lib/carray/string_operation_extension.rb +289 -0
- data/lib/carray/struct.rb +335 -323
- data/lib/carray/struct_builder.rb +697 -0
- data/lib/carray/table.rb +41 -2
- data/lib/carray/time.rb +2255 -38
- data/lib/carray/window_iterator.rb +655 -0
- data/lib/carray.rb +55 -57
- metadata +163 -130
- data/Rakefile +0 -51
- data/TODO.md +0 -18
- data/ext/ca_iter_block.c +0 -257
- data/ext/ca_iter_dimension.c +0 -299
- data/ext/ca_iter_window.c +0 -214
- data/ext/ca_obj_mapping.c +0 -644
- data/ext/carray_iterator.c +0 -641
- data/ext/carray_math.rb +0 -850
- data/ext/carray_numeric.c +0 -259
- data/ext/carray_sort_addr.c +0 -254
- data/ext/carray_stat.c +0 -2100
- data/ext/carray_stat_proc.rb +0 -1999
- data/ext/mkmath.rb +0 -741
- data/ext/ruby_ccomplex.c +0 -509
- data/ext/ruby_float_func.c +0 -86
- data/lib/carray/array.rb +0 -8
- data/lib/carray/autoload/autoload_base.rb +0 -19
- data/lib/carray/autoload/autoload_gem_cairo.rb +0 -9
- data/lib/carray/autoload/autoload_gem_ffi.rb +0 -9
- data/lib/carray/autoload/autoload_gem_gnuplot.rb +0 -2
- data/lib/carray/autoload/autoload_gem_io_csv.rb +0 -14
- data/lib/carray/autoload/autoload_gem_io_pg.rb +0 -6
- data/lib/carray/autoload/autoload_gem_io_sqlite3.rb +0 -12
- data/lib/carray/autoload/autoload_gem_narray.rb +0 -10
- data/lib/carray/autoload/autoload_gem_numo_narray.rb +0 -15
- data/lib/carray/autoload/autoload_gem_opencv.rb +0 -16
- data/lib/carray/autoload/autoload_gem_random.rb +0 -8
- data/lib/carray/autoload/autoload_gem_rmagick.rb +0 -23
- data/lib/carray/autoload/autoload_gem_zimg.rb +0 -3
- data/lib/carray/autoload/autoload_io_imagemagick.rb +0 -6
- data/lib/carray/autoload/autoload_math_histogram.rb +0 -5
- data/lib/carray/autoload/autoload_math_recurrence.rb +0 -6
- data/lib/carray/autoload/autoload_object_iterator.rb +0 -1
- data/lib/carray/autoload/autoload_object_link.rb +0 -1
- data/lib/carray/autoload/autoload_object_pack.rb +0 -2
- data/lib/carray/autoload.rb +0 -141
- data/lib/carray/basic.rb +0 -191
- data/lib/carray/broadcast.rb +0 -101
- data/lib/carray/compose.rb +0 -315
- data/lib/carray/convert.rb +0 -115
- data/lib/carray/info.rb +0 -110
- data/lib/carray/io/imagemagick.rb +0 -235
- data/lib/carray/mask.rb +0 -102
- data/lib/carray/math/histogram.rb +0 -177
- data/lib/carray/math/recurrence.rb +0 -93
- data/lib/carray/object/ca_obj_iterator.rb +0 -50
- data/lib/carray/object/ca_obj_link.rb +0 -50
- data/lib/carray/object/ca_obj_pack.rb +0 -99
- data/lib/carray/obsolete.rb +0 -256
- data/lib/carray/ordering.rb +0 -181
- data/lib/carray/testing.rb +0 -51
- data/lib/carray/transform.rb +0 -109
- data/misc/Methods.ja.md +0 -182
- data/misc/NOTE +0 -51
- data/spec/Classes/CABitfield_spec.rb +0 -58
- data/spec/Classes/CABlockIterator_spec.rb +0 -114
- data/spec/Classes/CABlock_spec.rb +0 -205
- data/spec/Classes/CAField_spec.rb +0 -39
- data/spec/Classes/CAGrid_spec.rb +0 -75
- data/spec/Classes/CAMap_spec.rb +0 -0
- data/spec/Classes/CAMapping_spec.rb +0 -105
- data/spec/Classes/CAObject_attribute_spec.rb +0 -33
- data/spec/Classes/CAObject_spec.rb +0 -33
- data/spec/Classes/CARefer_spec.rb +0 -93
- data/spec/Classes/CARepeat_spec.rb +0 -65
- data/spec/Classes/CASelect_spec.rb +0 -22
- data/spec/Classes/CAShift_spec.rb +0 -16
- data/spec/Classes/CAStruct_spec.rb +0 -71
- data/spec/Classes/CATranspose_spec.rb +0 -60
- data/spec/Classes/CAUnboudRepeat_spec.rb +0 -102
- data/spec/Classes/CAWindow_spec.rb +0 -54
- data/spec/Classes/CAWrap_spec.rb +0 -8
- data/spec/Classes/CArray_spec.rb +0 -184
- data/spec/Classes/CScalar_spec.rb +0 -55
- data/spec/Classes/ex1.rb +0 -46
- data/spec/Features/feature_130_spec.rb +0 -19
- data/spec/Features/feature_attributes_spec.rb +0 -280
- data/spec/Features/feature_boolean_spec.rb +0 -98
- data/spec/Features/feature_broadcast.rb +0 -116
- data/spec/Features/feature_cast_function.rb +0 -19
- data/spec/Features/feature_cast_spec.rb +0 -33
- data/spec/Features/feature_class_spec.rb +0 -84
- data/spec/Features/feature_complex_spec.rb +0 -42
- data/spec/Features/feature_composite_spec.rb +0 -124
- data/spec/Features/feature_convert_spec.rb +0 -46
- data/spec/Features/feature_copy_spec.rb +0 -123
- data/spec/Features/feature_creation_spec.rb +0 -84
- data/spec/Features/feature_element_spec.rb +0 -144
- data/spec/Features/feature_extream_spec.rb +0 -54
- data/spec/Features/feature_generate_spec.rb +0 -74
- data/spec/Features/feature_index_spec.rb +0 -69
- data/spec/Features/feature_mask_spec.rb +0 -580
- data/spec/Features/feature_math_spec.rb +0 -97
- data/spec/Features/feature_order_spec.rb +0 -146
- data/spec/Features/feature_ref_store_spec.rb +0 -209
- data/spec/Features/feature_serialization_spec.rb +0 -125
- data/spec/Features/feature_stat_spec.rb +0 -397
- data/spec/Features/feature_virtual_spec.rb +0 -48
- data/spec/Features/method_eq_spec.rb +0 -81
- data/spec/Features/method_is_nan_spec.rb +0 -12
- data/spec/Features/method_map_spec.rb +0 -54
- data/spec/Features/method_max_with.rb +0 -20
- data/spec/Features/method_min_with.rb +0 -19
- data/spec/Features/method_ne_spec.rb +0 -18
- data/spec/Features/method_project_spec.rb +0 -188
- data/spec/Features/method_ref_spec.rb +0 -27
- data/spec/Features/method_round_spec.rb +0 -11
- data/spec/Features/method_s_linspace_spec.rb +0 -48
- data/spec/Features/method_s_span_spec.rb +0 -14
- data/spec/Features/method_seq_spec.rb +0 -47
- data/spec/Features/method_sort_with.rb +0 -43
- data/spec/Features/method_sorted_with.rb +0 -29
- data/spec/Features/method_span_spec.rb +0 -42
- data/spec/Features/method_wrap_readonly_spec.rb +0 -43
- data/spec/UnitTest/test_CAVirtual.rb +0 -214
- data/spec/spec_all.rb +0 -10
- data/utils/ca_ase.rb +0 -21
- data/utils/ca_methods.rb +0 -15
- data/utils/cast_checker.rb +0 -30
- data/utils/convert_test.rb +0 -73
- data/utils/extract_yard.rb +0 -22
- data/utils/guess_shape.rb +0 -76
- data/utils/monkey_patch_methods.rb +0 -62
- data/utils/remove_resource_fork.sh +0 -5
|
@@ -0,0 +1,924 @@
|
|
|
1
|
+
/* ---------------------------------------------------------------------------
|
|
2
|
+
|
|
3
|
+
Per-axis descriptor common engine. Provides gather / scatter /
|
|
4
|
+
fill_value / for_each_slab primitives shared by CSA, CAGrid, and
|
|
5
|
+
other axis-descriptor-emitting views.
|
|
6
|
+
|
|
7
|
+
Single-loop algorithm (shared by gather and scatter):
|
|
8
|
+
0. Merge adjacent contig-mergeable STRIDE descriptor axes in
|
|
9
|
+
place. This reduces N-d strided iteration patterns that are
|
|
10
|
+
internally contiguous to a single STRIDE axis spanning the
|
|
11
|
+
merged span, before the slab detector or prefix iterator run.
|
|
12
|
+
1. Identify the innermost run of consecutive STRIDE-step1 axes
|
|
13
|
+
that form a contiguous sub-rectangle in parent row-major.
|
|
14
|
+
That run is the "slab" — one memcpy per outer iteration.
|
|
15
|
+
2. Iterate the remaining (prefix) axes row-major. Each axis may
|
|
16
|
+
be STRIDE or INDEX; both contribute to the parent offset
|
|
17
|
+
uniformly.
|
|
18
|
+
3. Output / input buffer is contiguous row-major over the view
|
|
19
|
+
shape; the slab span is transferred per iteration.
|
|
20
|
+
|
|
21
|
+
Slab degenerates naturally:
|
|
22
|
+
- slab covers all axes -> single memcpy from a single offset
|
|
23
|
+
- slab covers no axes -> per-cell memcpy (slab_bytes = bytes)
|
|
24
|
+
- mixed -> per-iteration slab memcpy
|
|
25
|
+
|
|
26
|
+
Contig criterion for the slab run: walking innermost -> outward
|
|
27
|
+
through the run, once we see an axis with count < mdim[k], every
|
|
28
|
+
outer axis in the run must have count == 1. This guarantees the
|
|
29
|
+
slab span is a tight prefix of a parent row. (`mdim[k]` is the
|
|
30
|
+
*effective* parent dim per axis after axis-merge — equal to
|
|
31
|
+
parent->dim[k] for unmerged axes.)
|
|
32
|
+
|
|
33
|
+
Scatter semantics:
|
|
34
|
+
- Iteration order = output row-major
|
|
35
|
+
(= axes[k].count outer-to-inner).
|
|
36
|
+
- Duplicate INDEX values write the same parent cell multiple
|
|
37
|
+
times; last-write-wins is the natural memcpy-based result.
|
|
38
|
+
The engine does not check or assume uniqueness.
|
|
39
|
+
|
|
40
|
+
---------------------------------------------------------------------------- */
|
|
41
|
+
|
|
42
|
+
#include "carray.h"
|
|
43
|
+
#include "ca_iter_substrate.h"
|
|
44
|
+
#include "ca_composite_dispatch.h"
|
|
45
|
+
|
|
46
|
+
#include <string.h>
|
|
47
|
+
|
|
48
|
+
/* Compute parent row-major byte strides from a dimension array. */
|
|
49
|
+
static void
|
|
50
|
+
ca_axis_dispatch_build_pstrides (ca_size_t *pstrides,
|
|
51
|
+
const ca_size_t *dim,
|
|
52
|
+
int8_t ndim,
|
|
53
|
+
ca_size_t bytes)
|
|
54
|
+
{
|
|
55
|
+
ca_size_t s = bytes;
|
|
56
|
+
int8_t k;
|
|
57
|
+
for ( k = ndim - 1; k >= 0; k-- ) {
|
|
58
|
+
pstrides[k] = s;
|
|
59
|
+
s *= dim[k];
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/* In-place merge of adjacent contig-mergeable STRIDE descriptor
|
|
64
|
+
axes.
|
|
65
|
+
|
|
66
|
+
Inputs (all arrays sized CA_RANK_MAX):
|
|
67
|
+
axes - descriptor array
|
|
68
|
+
pstrides - parent byte stride per axis (derived from mdim/bytes)
|
|
69
|
+
mdim - effective parent dim per axis (parent->dim initially)
|
|
70
|
+
ndim_inout - read and written
|
|
71
|
+
|
|
72
|
+
Merge condition for adjacent k, k+1 (both must be STRIDE):
|
|
73
|
+
step[k] * pstrides[k] == count[k+1] * step[k+1] * pstrides[k+1]
|
|
74
|
+
i.e. one full traversal of axis k+1's `count` cells at its effective
|
|
75
|
+
byte stride exactly equals one step of axis k. Under this condition
|
|
76
|
+
the two axes describe a single uniform-stride traversal of
|
|
77
|
+
count[k]*count[k+1] cells representable as one STRIDE axis.
|
|
78
|
+
|
|
79
|
+
Post-merge values for the surviving axis (placed at position k):
|
|
80
|
+
start = start[k] * mdim[k+1] + start[k+1]
|
|
81
|
+
count = count[k] * count[k+1]
|
|
82
|
+
step = step[k+1]
|
|
83
|
+
pstride = pstrides[k+1]
|
|
84
|
+
mdim = mdim[k] * mdim[k+1]
|
|
85
|
+
|
|
86
|
+
INDEX axes act as fences (merge is not attempted across them).
|
|
87
|
+
Sign-agnostic: works for negative steps as long as the equation
|
|
88
|
+
holds. Iterates to fixpoint. */
|
|
89
|
+
void
|
|
90
|
+
ca_axis_dispatch_merge (ca_axis_desc_t *axes,
|
|
91
|
+
ca_size_t *pstrides,
|
|
92
|
+
ca_size_t *mdim,
|
|
93
|
+
int8_t *ndim_inout)
|
|
94
|
+
{
|
|
95
|
+
int8_t ndim = *ndim_inout;
|
|
96
|
+
int8_t k, j;
|
|
97
|
+
int changed;
|
|
98
|
+
|
|
99
|
+
do {
|
|
100
|
+
changed = 0;
|
|
101
|
+
for ( k = 0; k + 1 < ndim; k++ ) {
|
|
102
|
+
if ( axes[k].kind != CA_AXIS_KIND_STRIDE ) continue;
|
|
103
|
+
if ( axes[k+1].kind != CA_AXIS_KIND_STRIDE ) continue;
|
|
104
|
+
|
|
105
|
+
ca_size_t ebs_k = axes[k].step * pstrides[k];
|
|
106
|
+
ca_size_t ebs_kp1 = axes[k+1].step * pstrides[k+1];
|
|
107
|
+
|
|
108
|
+
/* Defensive: a zero effective inner stride would make the
|
|
109
|
+
equation degenerate; skip. (Genuine stride-0 / CARepeat
|
|
110
|
+
axes don't reach this engine - they go through CAStride.) */
|
|
111
|
+
if ( ebs_kp1 == 0 ) continue;
|
|
112
|
+
|
|
113
|
+
if ( ebs_k != axes[k+1].count * ebs_kp1 ) continue;
|
|
114
|
+
|
|
115
|
+
/* Merge k and k+1 into a single STRIDE axis at position k. */
|
|
116
|
+
axes[k].start = axes[k].start * mdim[k+1] + axes[k+1].start;
|
|
117
|
+
axes[k].count = axes[k].count * axes[k+1].count;
|
|
118
|
+
axes[k].step = axes[k+1].step;
|
|
119
|
+
axes[k].indices = NULL;
|
|
120
|
+
pstrides[k] = pstrides[k+1];
|
|
121
|
+
mdim[k] = mdim[k] * mdim[k+1];
|
|
122
|
+
|
|
123
|
+
/* Shift remaining left. */
|
|
124
|
+
for ( j = k + 1; j + 1 < ndim; j++ ) {
|
|
125
|
+
axes[j] = axes[j+1];
|
|
126
|
+
pstrides[j] = pstrides[j+1];
|
|
127
|
+
mdim[j] = mdim[j+1];
|
|
128
|
+
}
|
|
129
|
+
ndim--;
|
|
130
|
+
changed = 1;
|
|
131
|
+
break; /* restart scan from index 0 */
|
|
132
|
+
}
|
|
133
|
+
} while ( changed );
|
|
134
|
+
|
|
135
|
+
*ndim_inout = ndim;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/* Slab detection: identify the innermost STRIDE-step1 contig run.
|
|
139
|
+
Operates on the (possibly merged) axes and the matching `mdim`
|
|
140
|
+
(effective parent dim per axis).
|
|
141
|
+
|
|
142
|
+
On return:
|
|
143
|
+
*slab_start = innermost prefix axis index (exclusive); ndim if
|
|
144
|
+
no slab axis exists
|
|
145
|
+
*slab_bytes = product of slab axes' counts * bytes
|
|
146
|
+
*slab_base = sum of slab axes' (start * pstrides) - INDEX axes
|
|
147
|
+
never enter the slab, so this is well-defined */
|
|
148
|
+
void
|
|
149
|
+
ca_axis_dispatch_layout (ca_axis_desc_t *axes,
|
|
150
|
+
const ca_size_t *pstrides,
|
|
151
|
+
const ca_size_t *mdim,
|
|
152
|
+
int8_t ndim,
|
|
153
|
+
ca_size_t bytes,
|
|
154
|
+
int8_t *slab_start,
|
|
155
|
+
ca_size_t *slab_bytes,
|
|
156
|
+
ca_size_t *slab_base)
|
|
157
|
+
{
|
|
158
|
+
int8_t k;
|
|
159
|
+
|
|
160
|
+
/* Identify innermost slab run of STRIDE-step1 axes that's contig in
|
|
161
|
+
parent. saw_partial: once a partial axis (count < mdim[k]) is
|
|
162
|
+
seen, every outer axis in the run must have count == 1. */
|
|
163
|
+
int8_t sstart = ndim;
|
|
164
|
+
ca_size_t sbytes = bytes;
|
|
165
|
+
{
|
|
166
|
+
int saw_partial = 0;
|
|
167
|
+
for ( k = ndim - 1; k >= 0; k-- ) {
|
|
168
|
+
if ( axes[k].kind != CA_AXIS_KIND_STRIDE ) break;
|
|
169
|
+
if ( axes[k].step != 1 ) break;
|
|
170
|
+
if ( saw_partial ) {
|
|
171
|
+
if ( axes[k].count != 1 ) break;
|
|
172
|
+
} else if ( axes[k].count != mdim[k] ) {
|
|
173
|
+
saw_partial = 1;
|
|
174
|
+
}
|
|
175
|
+
sstart = k;
|
|
176
|
+
sbytes *= axes[k].count;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
*slab_start = sstart;
|
|
180
|
+
*slab_bytes = sbytes;
|
|
181
|
+
|
|
182
|
+
/* Slab base offset in parent (sum of axes[k].start * pstrides[k]
|
|
183
|
+
for axes in the slab run; INDEX axes never enter the slab). */
|
|
184
|
+
ca_size_t base = 0;
|
|
185
|
+
for ( k = sstart; k < ndim; k++ ) {
|
|
186
|
+
base += axes[k].start * pstrides[k];
|
|
187
|
+
}
|
|
188
|
+
*slab_base = base;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/* Build merged layout from caller-provided axes/ndim/parent_axis_dims.
|
|
192
|
+
Copies into the caller's out_* arrays (each must be sized
|
|
193
|
+
CA_RANK_MAX), builds pstrides, runs the axis-merge pass to fixpoint,
|
|
194
|
+
and writes the merged ndim into *out_ndim. Subsequent code uses
|
|
195
|
+
only the out_* arrays - parent->dim is intentionally NOT referenced;
|
|
196
|
+
the producer's `parent_axis_dims[]` carries the effective per-axis
|
|
197
|
+
dim. This is what allows flat-index views to claim parent.ndim = 1
|
|
198
|
+
even though parent->ndim > 1 physically. */
|
|
199
|
+
void
|
|
200
|
+
ca_axis_dispatch_prepare (const ca_size_t *parent_axis_dims,
|
|
201
|
+
const ca_axis_desc_t *axes,
|
|
202
|
+
int8_t ndim,
|
|
203
|
+
ca_size_t bytes,
|
|
204
|
+
ca_axis_desc_t *out_axes,
|
|
205
|
+
ca_size_t *out_pstrides,
|
|
206
|
+
ca_size_t *out_mdim,
|
|
207
|
+
int8_t *out_ndim)
|
|
208
|
+
{
|
|
209
|
+
int8_t k;
|
|
210
|
+
for ( k = 0; k < ndim; k++ ) {
|
|
211
|
+
out_axes[k] = axes[k];
|
|
212
|
+
out_mdim[k] = parent_axis_dims[k];
|
|
213
|
+
}
|
|
214
|
+
ca_axis_dispatch_build_pstrides(out_pstrides, out_mdim, ndim, bytes);
|
|
215
|
+
*out_ndim = ndim;
|
|
216
|
+
ca_axis_dispatch_merge(out_axes, out_pstrides, out_mdim, out_ndim);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/* The prefix-axis typedefs (ca_op_axis_kind_t, ca_op_prefix_axis_t)
|
|
220
|
+
and the inline offset helper (ca_axis_dispatch_prefix_offset) live
|
|
221
|
+
in ca_iter_substrate.h. See that header for declarations. */
|
|
222
|
+
|
|
223
|
+
/* Build pre-classified prefix axis array from the (post-merge, post-
|
|
224
|
+
layout) axes/pstrides for indices [0..slab_start-1]. Algebraically
|
|
225
|
+
equivalent to the raw inline computation; precomputes byte-unit
|
|
226
|
+
start/step so the inner loop does one multiply per STRIDE axis and
|
|
227
|
+
one indirect load + one multiply per INDEX axis (vs. two multiplies
|
|
228
|
+
per STRIDE axis in the raw form). SHIFT axes keep start/step in
|
|
229
|
+
element units (for ca_bounds_normalize_index) and carry size0 /
|
|
230
|
+
policy from the descriptor. */
|
|
231
|
+
void
|
|
232
|
+
ca_axis_dispatch_classify_prefix (const ca_axis_desc_t *axes,
|
|
233
|
+
const ca_size_t *pstrides,
|
|
234
|
+
int8_t slab_start,
|
|
235
|
+
ca_op_prefix_axis_t *prefix)
|
|
236
|
+
{
|
|
237
|
+
int8_t k;
|
|
238
|
+
for ( k = 0; k < slab_start; k++ ) {
|
|
239
|
+
prefix[k].count = axes[k].count;
|
|
240
|
+
if ( axes[k].kind == CA_AXIS_KIND_STRIDE ) {
|
|
241
|
+
prefix[k].kind = CA_OP_AXIS_STRIDE;
|
|
242
|
+
prefix[k].byte_start = axes[k].start * pstrides[k];
|
|
243
|
+
prefix[k].byte_step = axes[k].step * pstrides[k];
|
|
244
|
+
prefix[k].indices = NULL;
|
|
245
|
+
prefix[k].byte_pstride = 0;
|
|
246
|
+
} else if ( axes[k].kind == CA_AXIS_KIND_INDEX ) {
|
|
247
|
+
prefix[k].kind = CA_OP_AXIS_INDEX;
|
|
248
|
+
prefix[k].byte_start = 0;
|
|
249
|
+
prefix[k].byte_step = 0;
|
|
250
|
+
prefix[k].indices = axes[k].indices;
|
|
251
|
+
prefix[k].byte_pstride = pstrides[k];
|
|
252
|
+
} else { /* CA_AXIS_KIND_SHIFT */
|
|
253
|
+
prefix[k].kind = CA_OP_AXIS_SHIFT;
|
|
254
|
+
prefix[k].byte_start = 0;
|
|
255
|
+
prefix[k].byte_step = 0;
|
|
256
|
+
prefix[k].indices = NULL;
|
|
257
|
+
prefix[k].byte_pstride = pstrides[k];
|
|
258
|
+
prefix[k].shift_start = axes[k].start;
|
|
259
|
+
prefix[k].shift_step = axes[k].step;
|
|
260
|
+
prefix[k].size0 = axes[k].size0;
|
|
261
|
+
prefix[k].policy = axes[k].policy;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/* ca_axis_dispatch_prefix_offset moved to ca_iter_substrate.h
|
|
267
|
+
(static inline so it inlines into _xfer / _fill_value below and into
|
|
268
|
+
other consumer TUs such as ca_kernel_iterator.c). */
|
|
269
|
+
|
|
270
|
+
/* Fill every selected parent cell with `val` (bytes-wide). Shares
|
|
271
|
+
the slab layout helper with gather/scatter; the inner write is a
|
|
272
|
+
per-element memcpy from `val` (the slab might be wider than one
|
|
273
|
+
element, e.g. when consecutive STRIDE axes form a contig run, so we
|
|
274
|
+
iterate within the slab).
|
|
275
|
+
|
|
276
|
+
Used by func_fill_data (e.g. `view[nil] = scalar` / `view.fill(x)`)
|
|
277
|
+
and by the per-slab callbacks below.
|
|
278
|
+
|
|
279
|
+
Delegates to `ca_fill_typed` for SIMD-friendly typed-store loops
|
|
280
|
+
(4-byte / 8-byte typed stores that the compiler autovectorises on
|
|
281
|
+
NEON / SSE2), avoiding a per-element memcpy loop. */
|
|
282
|
+
static void
|
|
283
|
+
ca_axis_dispatch_fill_slab (char *dst, const void *val,
|
|
284
|
+
ca_size_t bytes, ca_size_t slab_bytes)
|
|
285
|
+
{
|
|
286
|
+
ca_size_t n = slab_bytes / bytes;
|
|
287
|
+
ca_fill_typed(dst, (const char *) val, bytes, n);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/* Generic per-slab driver (`ca_axis_dispatch_for_each_slab`) plus
|
|
291
|
+
thin gather / scatter / fill_value callbacks around it. The
|
|
292
|
+
kernel_iterator descriptor path hooks into the driver for
|
|
293
|
+
descriptor-routed slab walks.
|
|
294
|
+
|
|
295
|
+
The all-slab fast path (slab_start == 0) is preserved as a single
|
|
296
|
+
cb invocation — no special-casing; the same cb decides what to do
|
|
297
|
+
with the slab. */
|
|
298
|
+
|
|
299
|
+
/* Callback context for gather / scatter (they share the layout - both
|
|
300
|
+
carry a buf+off cursor and gather additionally carries bound_fill). */
|
|
301
|
+
typedef struct {
|
|
302
|
+
char *buf;
|
|
303
|
+
ca_size_t off;
|
|
304
|
+
const void *bound_fill;
|
|
305
|
+
ca_size_t bytes;
|
|
306
|
+
char *parent_ptr;
|
|
307
|
+
} ca_axis_xfer_ctx_t;
|
|
308
|
+
|
|
309
|
+
static void
|
|
310
|
+
ca_axis_dispatch_gather_cb (ca_size_t off, int oob, ca_size_t n, void *vctx)
|
|
311
|
+
{
|
|
312
|
+
ca_axis_xfer_ctx_t *c = (ca_axis_xfer_ctx_t *) vctx;
|
|
313
|
+
char *p = c->parent_ptr + off;
|
|
314
|
+
if ( oob ) {
|
|
315
|
+
/* gather + OOB: write bound_fill across the slab (if provided);
|
|
316
|
+
otherwise leave the output cells untouched. */
|
|
317
|
+
if ( c->bound_fill ) {
|
|
318
|
+
ca_axis_dispatch_fill_slab(c->buf + c->off, c->bound_fill,
|
|
319
|
+
c->bytes, n);
|
|
320
|
+
}
|
|
321
|
+
} else {
|
|
322
|
+
memcpy(c->buf + c->off, p, n);
|
|
323
|
+
}
|
|
324
|
+
c->off += n;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
static void
|
|
328
|
+
ca_axis_dispatch_scatter_cb (ca_size_t off, int oob, ca_size_t n, void *vctx)
|
|
329
|
+
{
|
|
330
|
+
ca_axis_xfer_ctx_t *c = (ca_axis_xfer_ctx_t *) vctx;
|
|
331
|
+
char *p = c->parent_ptr + off;
|
|
332
|
+
/* scatter + OOB: skip parent write entirely. */
|
|
333
|
+
if ( ! oob ) {
|
|
334
|
+
memcpy(p, c->buf + c->off, n);
|
|
335
|
+
}
|
|
336
|
+
c->off += n;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/* Callback context for fill_value (broadcast a single value). parent is set
|
|
340
|
+
when the value goes through the parent's own fill_stride instead of a
|
|
341
|
+
buffer this side has attached. */
|
|
342
|
+
typedef struct {
|
|
343
|
+
const void *val;
|
|
344
|
+
ca_size_t bytes;
|
|
345
|
+
char *parent_ptr;
|
|
346
|
+
CArray *parent;
|
|
347
|
+
} ca_axis_fill_ctx_t;
|
|
348
|
+
|
|
349
|
+
static void
|
|
350
|
+
ca_axis_dispatch_fill_value_cb (ca_size_t off, int oob, ca_size_t n, void *vctx)
|
|
351
|
+
{
|
|
352
|
+
ca_axis_fill_ctx_t *c = (ca_axis_fill_ctx_t *) vctx;
|
|
353
|
+
if ( oob ) return;
|
|
354
|
+
if ( c->parent ) {
|
|
355
|
+
/* One slab is one contiguous run of parent cells, which is a region the
|
|
356
|
+
parent can fill for itself -- no borrowed buffer, so nothing outside
|
|
357
|
+
the run is read or written. */
|
|
358
|
+
ca_size_t count = n / c->bytes;
|
|
359
|
+
ca_size_t step = 1;
|
|
360
|
+
ca_fill_stride(c->parent, off / c->bytes, 1, &count, &step,
|
|
361
|
+
(void *) c->val);
|
|
362
|
+
}
|
|
363
|
+
else {
|
|
364
|
+
ca_axis_dispatch_fill_slab(c->parent_ptr + off, c->val, c->bytes, n);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/* Generic per-slab driver. Walks the (post-merge / post-layout)
|
|
369
|
+
prefix axes row-major and invokes `cb` once per slab iteration.
|
|
370
|
+
The all-slab case degenerates to a single `cb` invocation with
|
|
371
|
+
oob = 0. Empty views (total_elements == 0) yield no callbacks.
|
|
372
|
+
|
|
373
|
+
The callback advances its own buf/value cursor via `ctx` -- the
|
|
374
|
+
driver carries no per-slab cursor state. */
|
|
375
|
+
void
|
|
376
|
+
ca_axis_dispatch_for_each_slab (CArray *parent,
|
|
377
|
+
const ca_size_t *parent_axis_dims,
|
|
378
|
+
ca_axis_desc_t *axes,
|
|
379
|
+
int8_t ndim,
|
|
380
|
+
ca_size_t bytes,
|
|
381
|
+
ca_size_t total_elements,
|
|
382
|
+
ca_slab_cb_t cb,
|
|
383
|
+
void *ctx)
|
|
384
|
+
{
|
|
385
|
+
ca_axis_desc_t laxes[CA_RANK_MAX];
|
|
386
|
+
ca_size_t pstrides[CA_RANK_MAX];
|
|
387
|
+
ca_size_t mdim[CA_RANK_MAX];
|
|
388
|
+
ca_size_t idx[CA_RANK_MAX];
|
|
389
|
+
ca_op_prefix_axis_t prefix[CA_RANK_MAX];
|
|
390
|
+
int8_t lndim;
|
|
391
|
+
int8_t slab_start;
|
|
392
|
+
ca_size_t slab_bytes, slab_base;
|
|
393
|
+
int8_t k;
|
|
394
|
+
|
|
395
|
+
if ( total_elements == 0 ) {
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
ca_axis_dispatch_prepare(parent_axis_dims, axes, ndim, bytes,
|
|
400
|
+
laxes, pstrides, mdim, &lndim);
|
|
401
|
+
ca_axis_dispatch_layout(laxes, pstrides, mdim, lndim, bytes,
|
|
402
|
+
&slab_start, &slab_bytes, &slab_base);
|
|
403
|
+
|
|
404
|
+
/* All-slab fast path: single cb invocation. Slab detection breaks
|
|
405
|
+
on kind != STRIDE, so SHIFT axes never enter this branch and
|
|
406
|
+
oob = 0 is guaranteed. */
|
|
407
|
+
if ( slab_start == 0 ) {
|
|
408
|
+
cb(slab_base, 0, slab_bytes, ctx);
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/* Pre-classify prefix axes once. Inner loop becomes branchless
|
|
413
|
+
modulo a per-axis kind tag whose value is constant across
|
|
414
|
+
iters. */
|
|
415
|
+
ca_axis_dispatch_classify_prefix(laxes, pstrides, slab_start, prefix);
|
|
416
|
+
|
|
417
|
+
/* General path: iterate prefix axes [0..slab_start-1] row-major,
|
|
418
|
+
invoke cb per iteration. */
|
|
419
|
+
for ( k = 0; k < slab_start; k++ ) idx[k] = 0;
|
|
420
|
+
|
|
421
|
+
ca_size_t n_iters = total_elements / (slab_bytes / bytes);
|
|
422
|
+
ca_size_t n;
|
|
423
|
+
for ( n = 0; n < n_iters; n++ ) {
|
|
424
|
+
int oob;
|
|
425
|
+
ca_size_t poff = slab_base
|
|
426
|
+
+ ca_axis_dispatch_prefix_offset(prefix, idx,
|
|
427
|
+
slab_start, &oob);
|
|
428
|
+
cb(poff, oob, slab_bytes, ctx);
|
|
429
|
+
|
|
430
|
+
/* Advance idx row-major over prefix axes. */
|
|
431
|
+
for ( k = slab_start - 1; k >= 0; k-- ) {
|
|
432
|
+
if ( ++idx[k] < prefix[k].count ) break;
|
|
433
|
+
idx[k] = 0;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/* Per-cell gather/scatter fast path.
|
|
439
|
+
|
|
440
|
+
When `ca_axis_dispatch_layout` cannot extend the innermost slab
|
|
441
|
+
run (innermost axis is INDEX, or STRIDE with step != 1)
|
|
442
|
+
`slab_bytes` degenerates to `bytes`. The generic for_each_slab
|
|
443
|
+
loop would then run `total_elements` callback invocations, each
|
|
444
|
+
doing a runtime-size memcpy of one cell — the function-pointer cb
|
|
445
|
+
blocks inlining and the runtime size blocks typed-store
|
|
446
|
+
specialisation.
|
|
447
|
+
|
|
448
|
+
When in per-cell mode AND the inner axis is STRIDE/INDEX, bypass
|
|
449
|
+
the cb and hoist the innermost axis as a kind-specialised,
|
|
450
|
+
bytes-typed inner loop. Outer axes still ride the prefix
|
|
451
|
+
odometer. SHIFT inner falls back to for_each_slab (its OOB
|
|
452
|
+
semantics live in the cb). `bytes` not in {1,2,4,8} falls back to
|
|
453
|
+
memcpy in the inner loop, still winning from removing cb dispatch
|
|
454
|
+
and the outer-only odometer. */
|
|
455
|
+
|
|
456
|
+
static void
|
|
457
|
+
ca_axis_dispatch_percell_gather (CArray *parent,
|
|
458
|
+
ca_axis_desc_t *laxes,
|
|
459
|
+
const ca_size_t *pstrides,
|
|
460
|
+
int8_t lndim,
|
|
461
|
+
ca_size_t bytes,
|
|
462
|
+
ca_size_t slab_base,
|
|
463
|
+
const void *bound_fill,
|
|
464
|
+
char *out_buf)
|
|
465
|
+
{
|
|
466
|
+
int8_t inner = lndim - 1;
|
|
467
|
+
int8_t outers_n = inner;
|
|
468
|
+
ca_op_prefix_axis_t outer_prefix[CA_RANK_MAX];
|
|
469
|
+
ca_size_t outer_idx[CA_RANK_MAX];
|
|
470
|
+
ca_size_t n_outer = 1;
|
|
471
|
+
int8_t k;
|
|
472
|
+
|
|
473
|
+
if ( outers_n > 0 ) {
|
|
474
|
+
ca_axis_dispatch_classify_prefix(laxes, pstrides, outers_n, outer_prefix);
|
|
475
|
+
for ( k = 0; k < outers_n; k++ ) {
|
|
476
|
+
outer_idx[k] = 0;
|
|
477
|
+
n_outer *= outer_prefix[k].count;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
ca_axis_kind_t inner_kind = laxes[inner].kind;
|
|
482
|
+
ca_size_t inner_count = laxes[inner].count;
|
|
483
|
+
ca_size_t inner_pstride = pstrides[inner];
|
|
484
|
+
ca_size_t inner_base = laxes[inner].start * inner_pstride;
|
|
485
|
+
ca_size_t inner_step = laxes[inner].step * inner_pstride;
|
|
486
|
+
const ca_size_t *inner_indices = laxes[inner].indices;
|
|
487
|
+
ca_size_t row_dst_bytes = inner_count * bytes;
|
|
488
|
+
|
|
489
|
+
ca_size_t no;
|
|
490
|
+
for ( no = 0; no < n_outer; no++ ) {
|
|
491
|
+
int oob = 0;
|
|
492
|
+
ca_size_t outer_off = 0;
|
|
493
|
+
if ( outers_n > 0 ) {
|
|
494
|
+
outer_off = ca_axis_dispatch_prefix_offset(outer_prefix, outer_idx,
|
|
495
|
+
outers_n, &oob);
|
|
496
|
+
}
|
|
497
|
+
char *row_dst = out_buf + no * row_dst_bytes;
|
|
498
|
+
|
|
499
|
+
if ( oob ) {
|
|
500
|
+
if ( bound_fill ) {
|
|
501
|
+
ca_axis_dispatch_fill_slab(row_dst, bound_fill, bytes, row_dst_bytes);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
else {
|
|
505
|
+
char *row_src_base = parent->ptr + slab_base + outer_off;
|
|
506
|
+
ca_size_t j;
|
|
507
|
+
if ( inner_kind == CA_AXIS_KIND_STRIDE ) {
|
|
508
|
+
char *src = row_src_base + inner_base;
|
|
509
|
+
switch ( bytes ) {
|
|
510
|
+
case 1:
|
|
511
|
+
for (j = 0; j < inner_count; j++)
|
|
512
|
+
*(uint8_t *)(row_dst + j) = *(uint8_t *)(src + j*inner_step);
|
|
513
|
+
break;
|
|
514
|
+
case 2:
|
|
515
|
+
for (j = 0; j < inner_count; j++)
|
|
516
|
+
*(uint16_t *)(row_dst + j*2) = *(uint16_t *)(src + j*inner_step);
|
|
517
|
+
break;
|
|
518
|
+
case 4:
|
|
519
|
+
for (j = 0; j < inner_count; j++)
|
|
520
|
+
*(uint32_t *)(row_dst + j*4) = *(uint32_t *)(src + j*inner_step);
|
|
521
|
+
break;
|
|
522
|
+
case 8:
|
|
523
|
+
for (j = 0; j < inner_count; j++)
|
|
524
|
+
*(uint64_t *)(row_dst + j*8) = *(uint64_t *)(src + j*inner_step);
|
|
525
|
+
break;
|
|
526
|
+
default:
|
|
527
|
+
for (j = 0; j < inner_count; j++)
|
|
528
|
+
memcpy(row_dst + j*bytes, src + j*inner_step, bytes);
|
|
529
|
+
break;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
else { /* CA_AXIS_KIND_INDEX */
|
|
533
|
+
switch ( bytes ) {
|
|
534
|
+
case 1:
|
|
535
|
+
for (j = 0; j < inner_count; j++)
|
|
536
|
+
*(uint8_t *)(row_dst + j) =
|
|
537
|
+
*(uint8_t *)(row_src_base + inner_indices[j]*inner_pstride);
|
|
538
|
+
break;
|
|
539
|
+
case 2:
|
|
540
|
+
for (j = 0; j < inner_count; j++)
|
|
541
|
+
*(uint16_t *)(row_dst + j*2) =
|
|
542
|
+
*(uint16_t *)(row_src_base + inner_indices[j]*inner_pstride);
|
|
543
|
+
break;
|
|
544
|
+
case 4:
|
|
545
|
+
for (j = 0; j < inner_count; j++)
|
|
546
|
+
*(uint32_t *)(row_dst + j*4) =
|
|
547
|
+
*(uint32_t *)(row_src_base + inner_indices[j]*inner_pstride);
|
|
548
|
+
break;
|
|
549
|
+
case 8:
|
|
550
|
+
for (j = 0; j < inner_count; j++)
|
|
551
|
+
*(uint64_t *)(row_dst + j*8) =
|
|
552
|
+
*(uint64_t *)(row_src_base + inner_indices[j]*inner_pstride);
|
|
553
|
+
break;
|
|
554
|
+
default:
|
|
555
|
+
for (j = 0; j < inner_count; j++)
|
|
556
|
+
memcpy(row_dst + j*bytes,
|
|
557
|
+
row_src_base + inner_indices[j]*inner_pstride, bytes);
|
|
558
|
+
break;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
if ( outers_n > 0 ) {
|
|
564
|
+
for ( k = outers_n - 1; k >= 0; k-- ) {
|
|
565
|
+
if ( ++outer_idx[k] < outer_prefix[k].count ) break;
|
|
566
|
+
outer_idx[k] = 0;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
/* Gather engine: fill a caller-provided buffer with the view's
|
|
573
|
+
data. Caller must have parent->ptr valid (parent attached) and
|
|
574
|
+
`out_buf` large enough to hold total_elements * bytes.
|
|
575
|
+
|
|
576
|
+
Generic path is a thin wrapper around `for_each_slab`; per-cell
|
|
577
|
+
mode (slab_bytes == bytes, inner ∈ {STRIDE, INDEX}) dispatches to
|
|
578
|
+
the specialised inner-axis-hoisted loop above. */
|
|
579
|
+
void
|
|
580
|
+
ca_axis_dispatch_gather (CArray *parent,
|
|
581
|
+
const ca_size_t *parent_axis_dims,
|
|
582
|
+
ca_axis_desc_t *axes,
|
|
583
|
+
int8_t ndim,
|
|
584
|
+
ca_size_t bytes,
|
|
585
|
+
ca_size_t total_elements,
|
|
586
|
+
const void *bound_fill,
|
|
587
|
+
char *out_buf)
|
|
588
|
+
{
|
|
589
|
+
ca_axis_desc_t laxes[CA_RANK_MAX];
|
|
590
|
+
ca_size_t pstrides[CA_RANK_MAX];
|
|
591
|
+
ca_size_t mdim[CA_RANK_MAX];
|
|
592
|
+
int8_t lndim, sstart;
|
|
593
|
+
ca_size_t sbytes, sbase;
|
|
594
|
+
|
|
595
|
+
if ( total_elements == 0 ) return;
|
|
596
|
+
|
|
597
|
+
ca_axis_dispatch_prepare(parent_axis_dims, axes, ndim, bytes,
|
|
598
|
+
laxes, pstrides, mdim, &lndim);
|
|
599
|
+
ca_axis_dispatch_layout(laxes, pstrides, mdim, lndim, bytes,
|
|
600
|
+
&sstart, &sbytes, &sbase);
|
|
601
|
+
|
|
602
|
+
if ( sstart == lndim && lndim >= 1
|
|
603
|
+
&& (laxes[lndim-1].kind == CA_AXIS_KIND_STRIDE
|
|
604
|
+
|| laxes[lndim-1].kind == CA_AXIS_KIND_INDEX) ) {
|
|
605
|
+
ca_axis_dispatch_percell_gather(parent, laxes, pstrides, lndim, bytes,
|
|
606
|
+
sbase, bound_fill, out_buf);
|
|
607
|
+
return;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
/* Row-select fast path: outer = single INDEX axis, slab inner
|
|
611
|
+
promoted (row-major boolean/fancy select + materialise). An
|
|
612
|
+
inlined tight loop replaces `for_each_slab`'s function-pointer
|
|
613
|
+
cb dispatch on the dataframe-like row-select idiom.
|
|
614
|
+
Eligibility:
|
|
615
|
+
- sstart == 1 (exactly one prefix axis outside the slab)
|
|
616
|
+
- the prefix axis is INDEX (= no OOB, no SHIFT)
|
|
617
|
+
- parent->ptr attached (cold path stays on for_each_slab). */
|
|
618
|
+
if ( sstart == 1
|
|
619
|
+
&& laxes[0].kind == CA_AXIS_KIND_INDEX
|
|
620
|
+
&& parent->ptr != NULL ) {
|
|
621
|
+
const ca_size_t *indices = laxes[0].indices;
|
|
622
|
+
ca_size_t n = laxes[0].count;
|
|
623
|
+
ca_size_t pstride = pstrides[0];
|
|
624
|
+
char *base = parent->ptr + sbase;
|
|
625
|
+
ca_size_t i;
|
|
626
|
+
/* Hot bytes specializations using __builtin_memcpy with literal size
|
|
627
|
+
so clang lowers to NEON ldp/stp directly (no libc memcpy call). */
|
|
628
|
+
if ( sbytes == 8 ) {
|
|
629
|
+
for ( i = 0; i < n; i++ ) {
|
|
630
|
+
*(uint64_t *)(out_buf + i*8) =
|
|
631
|
+
*(uint64_t *)(base + indices[i] * pstride);
|
|
632
|
+
}
|
|
633
|
+
} else if ( sbytes == 16 ) {
|
|
634
|
+
for ( i = 0; i < n; i++ ) {
|
|
635
|
+
__builtin_memcpy(out_buf + i*16, base + indices[i] * pstride, 16);
|
|
636
|
+
}
|
|
637
|
+
} else if ( sbytes == 32 ) {
|
|
638
|
+
for ( i = 0; i < n; i++ ) {
|
|
639
|
+
__builtin_memcpy(out_buf + i*32, base + indices[i] * pstride, 32);
|
|
640
|
+
}
|
|
641
|
+
} else if ( sbytes == 64 ) {
|
|
642
|
+
for ( i = 0; i < n; i++ ) {
|
|
643
|
+
__builtin_memcpy(out_buf + i*64, base + indices[i] * pstride, 64);
|
|
644
|
+
}
|
|
645
|
+
} else if ( sbytes == 128 ) {
|
|
646
|
+
for ( i = 0; i < n; i++ ) {
|
|
647
|
+
__builtin_memcpy(out_buf + i*128, base + indices[i] * pstride, 128);
|
|
648
|
+
}
|
|
649
|
+
} else if ( sbytes == 256 ) {
|
|
650
|
+
for ( i = 0; i < n; i++ ) {
|
|
651
|
+
__builtin_memcpy(out_buf + i*256, base + indices[i] * pstride, 256);
|
|
652
|
+
}
|
|
653
|
+
} else {
|
|
654
|
+
for ( i = 0; i < n; i++ ) {
|
|
655
|
+
memcpy(out_buf + i * sbytes, base + indices[i] * pstride, sbytes);
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
{
|
|
662
|
+
ca_axis_xfer_ctx_t ctx = {
|
|
663
|
+
.buf = out_buf, .off = 0, .bound_fill = bound_fill, .bytes = bytes,
|
|
664
|
+
.parent_ptr = parent->ptr
|
|
665
|
+
};
|
|
666
|
+
ca_axis_dispatch_for_each_slab(parent, parent_axis_dims, axes, ndim,
|
|
667
|
+
bytes, total_elements,
|
|
668
|
+
ca_axis_dispatch_gather_cb, &ctx);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
/* Allocate-and-gather wrapper. Returns a malloced buffer of size
|
|
673
|
+
total_elements * bytes (or a 1-byte placeholder when total_elements
|
|
674
|
+
== 0; avoids malloc(0) implementation variance). Caller must xfree. */
|
|
675
|
+
char *
|
|
676
|
+
ca_axis_dispatch_attach (CArray *parent,
|
|
677
|
+
const ca_size_t *parent_axis_dims,
|
|
678
|
+
ca_axis_desc_t *axes,
|
|
679
|
+
int8_t ndim,
|
|
680
|
+
ca_size_t bytes,
|
|
681
|
+
ca_size_t total_elements,
|
|
682
|
+
const void *bound_fill)
|
|
683
|
+
{
|
|
684
|
+
ca_size_t out_len = total_elements * bytes;
|
|
685
|
+
char *out = xmalloc(out_len > 0 ? out_len : 1);
|
|
686
|
+
ca_axis_dispatch_gather(parent, parent_axis_dims, axes, ndim, bytes,
|
|
687
|
+
total_elements, bound_fill, out);
|
|
688
|
+
return out;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
/* Scatter engine: write the caller-provided buffer back into
|
|
692
|
+
parent. Caller must have parent->ptr valid (parent attached) and
|
|
693
|
+
`in_buf` sized total_elements * bytes.
|
|
694
|
+
|
|
695
|
+
Semantics: output row-major iteration order; duplicate INDEX
|
|
696
|
+
values produce last-write-wins. Thin wrapper around for_each_slab
|
|
697
|
+
+ scatter_cb; per-cell mode dispatches to
|
|
698
|
+
`ca_axis_dispatch_percell_scatter` below. */
|
|
699
|
+
/* Mirror of percell_gather: per-cell PUT path used when slab_bytes ==
|
|
700
|
+
bytes and inner ∈ {STRIDE, INDEX}. OOB outer rows are skipped (scatter
|
|
701
|
+
semantics). */
|
|
702
|
+
static void
|
|
703
|
+
ca_axis_dispatch_percell_scatter (CArray *parent,
|
|
704
|
+
ca_axis_desc_t *laxes,
|
|
705
|
+
const ca_size_t *pstrides,
|
|
706
|
+
int8_t lndim,
|
|
707
|
+
ca_size_t bytes,
|
|
708
|
+
ca_size_t slab_base,
|
|
709
|
+
const char *in_buf)
|
|
710
|
+
{
|
|
711
|
+
int8_t inner = lndim - 1;
|
|
712
|
+
int8_t outers_n = inner;
|
|
713
|
+
ca_op_prefix_axis_t outer_prefix[CA_RANK_MAX];
|
|
714
|
+
ca_size_t outer_idx[CA_RANK_MAX];
|
|
715
|
+
ca_size_t n_outer = 1;
|
|
716
|
+
int8_t k;
|
|
717
|
+
|
|
718
|
+
if ( outers_n > 0 ) {
|
|
719
|
+
ca_axis_dispatch_classify_prefix(laxes, pstrides, outers_n, outer_prefix);
|
|
720
|
+
for ( k = 0; k < outers_n; k++ ) {
|
|
721
|
+
outer_idx[k] = 0;
|
|
722
|
+
n_outer *= outer_prefix[k].count;
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
ca_axis_kind_t inner_kind = laxes[inner].kind;
|
|
727
|
+
ca_size_t inner_count = laxes[inner].count;
|
|
728
|
+
ca_size_t inner_pstride = pstrides[inner];
|
|
729
|
+
ca_size_t inner_base = laxes[inner].start * inner_pstride;
|
|
730
|
+
ca_size_t inner_step = laxes[inner].step * inner_pstride;
|
|
731
|
+
const ca_size_t *inner_indices = laxes[inner].indices;
|
|
732
|
+
ca_size_t row_src_bytes = inner_count * bytes;
|
|
733
|
+
|
|
734
|
+
ca_size_t no;
|
|
735
|
+
for ( no = 0; no < n_outer; no++ ) {
|
|
736
|
+
int oob = 0;
|
|
737
|
+
ca_size_t outer_off = 0;
|
|
738
|
+
if ( outers_n > 0 ) {
|
|
739
|
+
outer_off = ca_axis_dispatch_prefix_offset(outer_prefix, outer_idx,
|
|
740
|
+
outers_n, &oob);
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
if ( !oob ) {
|
|
744
|
+
const char *row_src = in_buf + no * row_src_bytes;
|
|
745
|
+
char *row_dst_base = parent->ptr + slab_base + outer_off;
|
|
746
|
+
ca_size_t j;
|
|
747
|
+
if ( inner_kind == CA_AXIS_KIND_STRIDE ) {
|
|
748
|
+
char *dst = row_dst_base + inner_base;
|
|
749
|
+
switch ( bytes ) {
|
|
750
|
+
case 1:
|
|
751
|
+
for (j = 0; j < inner_count; j++)
|
|
752
|
+
*(uint8_t *)(dst + j*inner_step) = *(uint8_t *)(row_src + j);
|
|
753
|
+
break;
|
|
754
|
+
case 2:
|
|
755
|
+
for (j = 0; j < inner_count; j++)
|
|
756
|
+
*(uint16_t *)(dst + j*inner_step) = *(uint16_t *)(row_src + j*2);
|
|
757
|
+
break;
|
|
758
|
+
case 4:
|
|
759
|
+
for (j = 0; j < inner_count; j++)
|
|
760
|
+
*(uint32_t *)(dst + j*inner_step) = *(uint32_t *)(row_src + j*4);
|
|
761
|
+
break;
|
|
762
|
+
case 8:
|
|
763
|
+
for (j = 0; j < inner_count; j++)
|
|
764
|
+
*(uint64_t *)(dst + j*inner_step) = *(uint64_t *)(row_src + j*8);
|
|
765
|
+
break;
|
|
766
|
+
default:
|
|
767
|
+
for (j = 0; j < inner_count; j++)
|
|
768
|
+
memcpy(dst + j*inner_step, row_src + j*bytes, bytes);
|
|
769
|
+
break;
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
else { /* CA_AXIS_KIND_INDEX */
|
|
773
|
+
switch ( bytes ) {
|
|
774
|
+
case 1:
|
|
775
|
+
for (j = 0; j < inner_count; j++)
|
|
776
|
+
*(uint8_t *)(row_dst_base + inner_indices[j]*inner_pstride) =
|
|
777
|
+
*(uint8_t *)(row_src + j);
|
|
778
|
+
break;
|
|
779
|
+
case 2:
|
|
780
|
+
for (j = 0; j < inner_count; j++)
|
|
781
|
+
*(uint16_t *)(row_dst_base + inner_indices[j]*inner_pstride) =
|
|
782
|
+
*(uint16_t *)(row_src + j*2);
|
|
783
|
+
break;
|
|
784
|
+
case 4:
|
|
785
|
+
for (j = 0; j < inner_count; j++)
|
|
786
|
+
*(uint32_t *)(row_dst_base + inner_indices[j]*inner_pstride) =
|
|
787
|
+
*(uint32_t *)(row_src + j*4);
|
|
788
|
+
break;
|
|
789
|
+
case 8:
|
|
790
|
+
for (j = 0; j < inner_count; j++)
|
|
791
|
+
*(uint64_t *)(row_dst_base + inner_indices[j]*inner_pstride) =
|
|
792
|
+
*(uint64_t *)(row_src + j*8);
|
|
793
|
+
break;
|
|
794
|
+
default:
|
|
795
|
+
for (j = 0; j < inner_count; j++)
|
|
796
|
+
memcpy(row_dst_base + inner_indices[j]*inner_pstride,
|
|
797
|
+
row_src + j*bytes, bytes);
|
|
798
|
+
break;
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
if ( outers_n > 0 ) {
|
|
804
|
+
for ( k = outers_n - 1; k >= 0; k-- ) {
|
|
805
|
+
if ( ++outer_idx[k] < outer_prefix[k].count ) break;
|
|
806
|
+
outer_idx[k] = 0;
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
void
|
|
813
|
+
ca_axis_dispatch_scatter (CArray *parent,
|
|
814
|
+
const ca_size_t *parent_axis_dims,
|
|
815
|
+
ca_axis_desc_t *axes,
|
|
816
|
+
int8_t ndim,
|
|
817
|
+
ca_size_t bytes,
|
|
818
|
+
ca_size_t total_elements,
|
|
819
|
+
const char *in_buf)
|
|
820
|
+
{
|
|
821
|
+
ca_axis_desc_t laxes[CA_RANK_MAX];
|
|
822
|
+
ca_size_t pstrides[CA_RANK_MAX];
|
|
823
|
+
ca_size_t mdim[CA_RANK_MAX];
|
|
824
|
+
int8_t lndim, sstart;
|
|
825
|
+
ca_size_t sbytes, sbase;
|
|
826
|
+
|
|
827
|
+
if ( total_elements == 0 ) return;
|
|
828
|
+
|
|
829
|
+
ca_axis_dispatch_prepare(parent_axis_dims, axes, ndim, bytes,
|
|
830
|
+
laxes, pstrides, mdim, &lndim);
|
|
831
|
+
ca_axis_dispatch_layout(laxes, pstrides, mdim, lndim, bytes,
|
|
832
|
+
&sstart, &sbytes, &sbase);
|
|
833
|
+
|
|
834
|
+
if ( sstart == lndim && lndim >= 1
|
|
835
|
+
&& (laxes[lndim-1].kind == CA_AXIS_KIND_STRIDE
|
|
836
|
+
|| laxes[lndim-1].kind == CA_AXIS_KIND_INDEX) ) {
|
|
837
|
+
ca_axis_dispatch_percell_scatter(parent, laxes, pstrides, lndim, bytes,
|
|
838
|
+
sbase, in_buf);
|
|
839
|
+
return;
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
{
|
|
843
|
+
/* scatter_cb only reads from buf, so the const_cast is safe.
|
|
844
|
+
bound_fill is irrelevant on scatter (OOB cells are skipped). */
|
|
845
|
+
ca_axis_xfer_ctx_t ctx = {
|
|
846
|
+
.buf = (char *) in_buf, .off = 0, .bound_fill = NULL, .bytes = bytes,
|
|
847
|
+
.parent_ptr = parent->ptr
|
|
848
|
+
};
|
|
849
|
+
ca_axis_dispatch_for_each_slab(parent, parent_axis_dims, axes, ndim,
|
|
850
|
+
bytes, total_elements,
|
|
851
|
+
ca_axis_dispatch_scatter_cb, &ctx);
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
/* Broadcast-fill engine: write the single value `val` (bytes wide)
|
|
856
|
+
into every selected parent cell. Caller must have parent->ptr
|
|
857
|
+
valid.
|
|
858
|
+
|
|
859
|
+
Semantics: same iteration as scatter (output row-major). For
|
|
860
|
+
duplicate INDEX values the same value is written N times — the
|
|
861
|
+
end-state is identical to a single write, so last-write-wins is
|
|
862
|
+
trivially satisfied. Thin wrapper around for_each_slab +
|
|
863
|
+
fill_value_cb. */
|
|
864
|
+
void
|
|
865
|
+
ca_axis_dispatch_fill_value (CArray *parent,
|
|
866
|
+
const ca_size_t *parent_axis_dims,
|
|
867
|
+
ca_axis_desc_t *axes,
|
|
868
|
+
int8_t ndim,
|
|
869
|
+
ca_size_t bytes,
|
|
870
|
+
ca_size_t total_elements,
|
|
871
|
+
const void *val)
|
|
872
|
+
{
|
|
873
|
+
ca_axis_fill_ctx_t ctx = { .val = val, .bytes = bytes,
|
|
874
|
+
.parent_ptr = parent->ptr, .parent = NULL };
|
|
875
|
+
ca_axis_dispatch_for_each_slab(parent, parent_axis_dims, axes, ndim,
|
|
876
|
+
bytes, total_elements,
|
|
877
|
+
ca_axis_dispatch_fill_value_cb, &ctx);
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
/* Same walk, but each slab is handed to the parent as a region of its own
|
|
881
|
+
rather than written through a pointer into it. A gather view whose parent
|
|
882
|
+
has to be materialised to be addressed can then write the cells it selected
|
|
883
|
+
without the parent being pulled in whole and pushed back. */
|
|
884
|
+
void
|
|
885
|
+
ca_axis_dispatch_fill_value_via_parent (CArray *parent,
|
|
886
|
+
const ca_size_t *parent_axis_dims,
|
|
887
|
+
ca_axis_desc_t *axes,
|
|
888
|
+
int8_t ndim,
|
|
889
|
+
ca_size_t bytes,
|
|
890
|
+
ca_size_t total_elements,
|
|
891
|
+
const void *val)
|
|
892
|
+
{
|
|
893
|
+
ca_axis_fill_ctx_t ctx = { .val = val, .bytes = bytes,
|
|
894
|
+
.parent_ptr = NULL, .parent = parent };
|
|
895
|
+
ca_axis_dispatch_for_each_slab(parent, parent_axis_dims, axes, ndim,
|
|
896
|
+
bytes, total_elements,
|
|
897
|
+
ca_axis_dispatch_fill_value_cb, &ctx);
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
/* ==========================================================================
|
|
901
|
+
Innermost-STRIDE L2 alias helpers for kernel_iterator descriptor
|
|
902
|
+
source routing. Called from ca_kernel_iterator.c to decide
|
|
903
|
+
whether the descriptor source can alias into an L2-strided walk.
|
|
904
|
+
========================================================================== */
|
|
905
|
+
|
|
906
|
+
int
|
|
907
|
+
ca_axis_dispatch_is_innermost_stride (const ca_axis_desc_t *descs,
|
|
908
|
+
int8_t ndim)
|
|
909
|
+
{
|
|
910
|
+
if ( ndim <= 0 ) return 0;
|
|
911
|
+
return descs[ndim - 1].kind == CA_AXIS_KIND_STRIDE;
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
int
|
|
915
|
+
ca_axis_dispatch_outer_has_shift (const ca_axis_desc_t *descs,
|
|
916
|
+
int8_t ndim)
|
|
917
|
+
{
|
|
918
|
+
int8_t k;
|
|
919
|
+
if ( ndim <= 1 ) return 0;
|
|
920
|
+
for ( k = 0; k < ndim - 1; k++ ) {
|
|
921
|
+
if ( descs[k].kind == CA_AXIS_KIND_SHIFT ) return 1;
|
|
922
|
+
}
|
|
923
|
+
return 0;
|
|
924
|
+
}
|