carray 3.0.2 → 3.0.4
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/CHANGELOG.md +568 -0
- data/README.md +6 -6
- data/carray.gemspec +1 -1
- data/ext/ca_axis_descriptor.h +27 -24
- data/ext/ca_axis_dispatch.c +220 -0
- data/ext/ca_axis_group.c +201 -123
- data/ext/ca_binop_dispatch.c +46 -52
- data/ext/ca_binop_dispatch.h +1 -1
- data/ext/ca_categorical_iterator.c +249 -183
- data/ext/ca_compare.c +7 -12
- data/ext/ca_for_buffer.h +21 -68
- data/ext/ca_group_iter.c +38 -23
- data/ext/ca_kernel_iterator.c +1765 -2076
- data/ext/ca_kernel_iterator.h +584 -566
- data/ext/ca_monop_dispatch.c +86 -63
- data/ext/ca_monop_dispatch.h +5 -0
- data/ext/ca_obj_array.c +223 -56
- data/ext/ca_obj_bincmp.c +172 -138
- data/ext/ca_obj_binop.c +116 -230
- data/ext/ca_obj_bitarray.c +40 -26
- data/ext/ca_obj_bitfield.c +100 -68
- data/ext/ca_obj_block.c +19 -9
- data/ext/ca_obj_byte_swap.c +45 -46
- data/ext/ca_obj_const_string.c +269 -44
- data/ext/ca_obj_face.c +612 -89
- data/ext/ca_obj_face.h +111 -47
- data/ext/ca_obj_fake.c +36 -79
- data/ext/ca_obj_farray.c +7 -3
- data/ext/ca_obj_field.c +33 -8
- data/ext/ca_obj_fixlen_string.c +23 -24
- data/ext/ca_obj_grid.c +541 -210
- data/ext/ca_obj_meld.c +331 -111
- data/ext/ca_obj_moncmp.c +80 -65
- data/ext/ca_obj_monop.c +177 -149
- data/ext/ca_obj_object.c +664 -248
- data/ext/ca_obj_record.c +63 -23
- data/ext/ca_obj_reduce.c +18 -16
- data/ext/ca_obj_refer.c +140 -58
- data/ext/ca_obj_remap.c +85 -24
- data/ext/ca_obj_repeat.c +24 -9
- data/ext/ca_obj_roll.c +121 -16
- data/ext/ca_obj_select.c +61 -58
- data/ext/ca_obj_select_axis.c +73 -107
- data/ext/ca_obj_shift.c +32 -102
- data/ext/ca_obj_stack.c +372 -122
- data/ext/ca_obj_stride.c +348 -140
- data/ext/ca_obj_string.c +88 -22
- data/ext/ca_obj_tile.c +38 -21
- data/ext/ca_obj_time.c +45 -31
- data/ext/ca_obj_timedelta.c +43 -29
- data/ext/ca_obj_transpose.c +17 -6
- data/ext/ca_obj_triop.c +125 -141
- data/ext/ca_obj_window.c +330 -44
- data/ext/ca_op_ipower.c +24 -11
- data/ext/ca_op_powi.h +57 -30
- data/ext/ca_pow5_table.h +656 -0
- data/ext/ca_sort_kernels.h +70 -0
- data/ext/ca_sweep_engine.c +104 -81
- data/ext/ca_sweep_engine.h +7 -20
- data/ext/ca_triop_dispatch.c +4 -1
- data/ext/ca_triop_dispatch.h +5 -2
- data/ext/caframe_csv_reader.c +542 -0
- data/ext/carray.h +241 -92
- data/ext/carray_access.c +608 -276
- data/ext/carray_address_basis.c +494 -97
- data/ext/carray_attribute.c +129 -4
- data/ext/carray_bincount.c +39 -7
- data/ext/carray_broadcast.c +56 -19
- data/ext/carray_build_flags.h +1 -1
- data/ext/carray_call_cfunc.c +108 -28
- data/ext/carray_cast.c +1576 -223
- data/ext/carray_cast_func.rb +77 -24
- data/ext/carray_class.c +4 -3
- data/ext/carray_conversion.c +208 -75
- data/ext/carray_copy.c +82 -24
- data/ext/carray_core.c +646 -162
- data/ext/carray_count.c +171 -88
- data/ext/carray_element.c +94 -60
- data/ext/carray_factorize.c +670 -324
- data/ext/carray_generate.c +260 -83
- data/ext/carray_histogram.c +209 -106
- data/ext/carray_hold.c +14 -16
- data/ext/carray_index_classifier.c +422 -243
- data/ext/carray_internal.h +692 -3
- data/ext/carray_kernels_bincmp.c +7 -0
- data/ext/carray_kernels_binop.c +487 -120
- data/ext/carray_kernels_init.c +653 -50
- data/ext/carray_kernels_moncmp.c +15 -8
- data/ext/carray_kernels_monop.c +99 -43
- data/ext/carray_kernels_reduce_aggregate.c +3925 -6112
- data/ext/carray_kernels_reduce_boolean.c +112 -36
- data/ext/carray_kernels_reduce_cumulative.c +2127 -6903
- data/ext/carray_kernels_reduce_extreme.c +3032 -2892
- data/ext/carray_kernels_reduce_variance.c +439 -500
- data/ext/carray_kernels_scan.c +1097 -488
- data/ext/carray_kernels_search.c +3787 -2176
- data/ext/carray_kernels_sort.c +1200 -920
- data/ext/carray_kernels_triop.c +479 -7
- data/ext/carray_key_windows.c +216 -0
- data/ext/carray_lazy.c +967 -153
- data/ext/carray_loop.c +81 -65
- data/ext/carray_mask.c +910 -324
- data/ext/carray_math_kernel.h +1 -0
- data/ext/carray_median_percentile.c +299 -100
- data/ext/carray_memory_view.c +872 -758
- data/ext/carray_operator.c +1171 -364
- data/ext/carray_order.c +505 -133
- data/ext/carray_partition.c +112 -65
- data/ext/carray_random.c +327 -239
- data/ext/carray_round.c +273 -0
- data/ext/carray_scatter.c +346 -78
- data/ext/carray_segment.c +257 -0
- data/ext/carray_slab.c +249 -86
- data/ext/carray_sort.c +224 -227
- data/ext/carray_sort_kernel.c +8 -1
- data/ext/carray_struct.c +118 -39
- data/ext/carray_test.c +193 -69
- data/ext/carray_time_text.c +1182 -0
- data/ext/carray_undef.c +4 -0
- data/ext/carray_utils.c +579 -113
- data/ext/extconf.rb +27 -2
- data/ext/mk_call_cfunc.rb +19 -4
- data/ext/mk_pow5_table.rb +35 -0
- data/ext/mkkernel.rb +1202 -1014
- data/ext/ruby_carray.c +17 -5
- data/ext/version.h +4 -4
- data/ext/version.rb +4 -2
- data/lib/carray/attribute.rb +160 -17
- data/lib/carray/autoload_carray.rb +23 -27
- data/lib/carray/autoload_method_extension.rb +20 -5
- data/lib/carray/axis_group.rb +360 -63
- data/lib/carray/basics.rb +162 -28
- data/lib/carray/bincount_nd.rb +18 -2
- data/lib/carray/block_iterator.rb +231 -58
- data/lib/carray/boolean_reduce.rb +34 -42
- data/lib/carray/categorical.rb +242 -52
- data/lib/carray/categorical_iterator.rb +437 -827
- data/lib/carray/complex.rb +41 -34
- data/lib/carray/conditional.rb +150 -14
- data/lib/carray/const_string.rb +59 -4
- data/lib/carray/construct.rb +15 -11
- data/lib/carray/data_type_extension.rb +1 -0
- data/lib/carray/distance_limit.rb +77 -0
- data/lib/carray/fixlen_string.rb +7 -1
- data/lib/carray/frame/column_verbs.rb +70 -0
- data/lib/carray/frame/concat.rb +157 -6
- data/lib/carray/frame/convert.rb +82 -7
- data/lib/carray/frame/csv_parser.rb +688 -87
- data/lib/carray/frame/describe.rb +80 -0
- data/lib/carray/frame/frame.rb +369 -45
- data/lib/carray/frame/group.rb +631 -90
- data/lib/carray/frame/io.rb +506 -79
- data/lib/carray/frame/join.rb +411 -62
- data/lib/carray/frame/records.rb +116 -19
- data/lib/carray/frame/reshape.rb +455 -0
- data/lib/carray/frame/sort.rb +30 -18
- data/lib/carray/frame/verbs.rb +868 -61
- data/lib/carray/frame/windows.rb +117 -0
- data/lib/carray/frame.rb +4 -0
- data/lib/carray/fuse_source.rb +118 -2
- data/lib/carray/fusion.rb +257 -42
- data/lib/carray/histogram.rb +6 -1
- data/lib/carray/inspect.rb +54 -13
- data/lib/carray/iterator.rb +99 -9
- data/lib/carray/key_windows.rb +620 -0
- data/lib/carray/lazy.rb +216 -193
- data/lib/carray/mask_gap_fill.rb +112 -92
- data/lib/carray/math.rb +32 -19
- data/lib/carray/meld_reduce.rb +52 -21
- data/lib/carray/methods/align_addr.rb +8 -4
- data/lib/carray/methods/bin.rb +26 -18
- data/lib/carray/methods/bincount.rb +18 -2
- data/lib/carray/methods/bit_string.rb +15 -4
- data/lib/carray/methods/choose.rb +16 -3
- data/lib/carray/methods/composition.rb +18 -10
- data/lib/carray/methods/factorize.rb +2 -2
- data/lib/carray/methods/gather_nd.rb +25 -5
- data/lib/carray/methods/insert_block.rb +9 -14
- data/lib/carray/methods/is_between.rb +59 -0
- data/lib/carray/methods/is_in.rb +140 -10
- data/lib/carray/methods/join.rb +2 -7
- data/lib/carray/methods/lag.rb +49 -0
- data/lib/carray/methods/locate_addr.rb +182 -45
- data/lib/carray/methods/mode.rb +18 -1
- data/lib/carray/methods/nunique.rb +11 -6
- data/lib/carray/methods/repeat.rb +1 -20
- data/lib/carray/methods/resize.rb +21 -23
- data/lib/carray/methods/snap.rb +34 -12
- data/lib/carray/methods/unique.rb +11 -2
- data/lib/carray/methods/value_counts.rb +4 -11
- data/lib/carray/record_order.rb +208 -0
- data/lib/carray/rng.rb +3 -0
- data/lib/carray/runtime.rb +65 -17
- data/lib/carray/segment_iterator.rb +1221 -0
- data/lib/carray/serialize.rb +214 -30
- data/lib/carray/slab_iterator.rb +101 -27
- data/lib/carray/stack.rb +10 -4
- data/lib/carray/string.rb +105 -5
- data/lib/carray/string_operation_extension.rb +12 -4
- data/lib/carray/struct.rb +137 -58
- data/lib/carray/struct_builder.rb +124 -17
- data/lib/carray/time.rb +1222 -186
- data/lib/carray/window_iterator.rb +242 -81
- data/lib/carray.rb +9 -2
- data/yard-stubs/ca_obj_array.rb +4 -4
- data/yard-stubs/ca_obj_bitarray.rb +7 -0
- data/yard-stubs/ca_obj_bitfield.rb +23 -12
- data/yard-stubs/ca_obj_fake.rb +1 -1
- data/yard-stubs/ca_obj_field.rb +5 -1
- data/yard-stubs/ca_obj_grid.rb +21 -10
- data/yard-stubs/ca_obj_refer.rb +5 -4
- data/yard-stubs/ca_obj_roll.rb +2 -3
- data/yard-stubs/ca_obj_shift.rb +5 -1
- data/yard-stubs/ca_obj_stride.rb +10 -7
- data/yard-stubs/ca_obj_tile.rb +1 -1
- data/yard-stubs/ca_obj_transpose.rb +1 -1
- data/yard-stubs/ca_obj_window.rb +9 -5
- data/yard-stubs/carray_access.rb +11 -5
- data/yard-stubs/carray_attribute.rb +0 -5
- data/yard-stubs/carray_broadcast.rb +1 -1
- data/yard-stubs/carray_cast.rb +88 -43
- data/yard-stubs/carray_conversion.rb +15 -2
- data/yard-stubs/carray_copy.rb +12 -5
- data/yard-stubs/carray_core.rb +3 -3
- data/yard-stubs/carray_count.rb +9 -0
- data/yard-stubs/carray_element.rb +10 -4
- data/yard-stubs/carray_generate.rb +21 -1
- data/yard-stubs/carray_lazy.rb +5 -1
- data/yard-stubs/carray_loop.rb +4 -1
- data/yard-stubs/carray_mask.rb +60 -12
- data/yard-stubs/carray_math.rb +52 -14
- data/yard-stubs/carray_median_percentile.rb +15 -8
- data/yard-stubs/carray_memory_view.rb +40 -23
- data/yard-stubs/carray_order.rb +61 -29
- data/yard-stubs/carray_random.rb +13 -4
- data/yard-stubs/carray_scatter.rb +21 -9
- data/yard-stubs/carray_segment.rb +72 -0
- data/yard-stubs/carray_slab.rb +10 -2
- data/yard-stubs/carray_sort.rb +49 -8
- data/yard-stubs/carray_test.rb +15 -3
- data/yard-stubs/carray_utils.rb +0 -26
- metadata +19 -5
- data/ext/ca_for_each_element.h +0 -239
- data/ext/carray_kernels_map.c +0 -3467
- data/lib/carray/arrow_tensor.rb +0 -401
- data/lib/carray/table.rb +0 -108
data/ext/ca_sort_kernels.h
CHANGED
|
@@ -129,4 +129,74 @@ void ca_sort_merge_pair_u64 (ca_pair_u64 *a, ca_pair_u64 *aux, ca_size_t n);
|
|
|
129
129
|
void ca_sort_merge_pair_f32 (ca_pair_f32 *a, ca_pair_f32 *aux, ca_size_t n);
|
|
130
130
|
void ca_sort_merge_pair_f64 (ca_pair_f64 *a, ca_pair_f64 *aux, ca_size_t n);
|
|
131
131
|
|
|
132
|
+
/* Runs of this many elements are insertion-sorted before the merge passes. */
|
|
133
|
+
#define CA_SORT_INSERTION_THRESHOLD 16
|
|
134
|
+
|
|
135
|
+
/* The body of a stable bottom-up mergesort over TYPE, ordered by cmp alone
|
|
136
|
+
(the element type has no `<`), for a function taking
|
|
137
|
+
(TYPE *a, TYPE *aux, ca_size_t n, int (*cmp)(const void *, const void *))
|
|
138
|
+
aux holds n elements. cmp may raise -- an object comparator calls <=> --
|
|
139
|
+
and the raise unwinds through this code's frames, which hold nothing;
|
|
140
|
+
libc's qsort / mergesort would have to be unwound instead.
|
|
141
|
+
|
|
142
|
+
A comparison is costly here (a call into Ruby) and a move is cheap, so
|
|
143
|
+
the runs are insertion-sorted with each insertion point found by binary
|
|
144
|
+
search: log2(k) comparisons where a linear walk takes k/2. Merge passes
|
|
145
|
+
ping-pong between a and aux and skip a pair of runs already in order.
|
|
146
|
+
Stable: an element lands after every one that does not compare greater,
|
|
147
|
+
and a merge takes from the right run only on a strictly smaller one. */
|
|
148
|
+
#define CA_SORT_MERGE_BY_CMP(TYPE) \
|
|
149
|
+
{ \
|
|
150
|
+
if ( n <= 1 ) return; \
|
|
151
|
+
for ( ca_size_t lo = 0; lo < n; lo += CA_SORT_INSERTION_THRESHOLD ) { \
|
|
152
|
+
ca_size_t hi = lo + CA_SORT_INSERTION_THRESHOLD; \
|
|
153
|
+
if ( hi > n ) hi = n; \
|
|
154
|
+
for ( ca_size_t k = lo + 1; k < hi; k++ ) { \
|
|
155
|
+
TYPE v = a[k]; \
|
|
156
|
+
ca_size_t l = lo, r = k; \
|
|
157
|
+
while ( l < r ) { \
|
|
158
|
+
ca_size_t mid = l + (r - l) / 2; \
|
|
159
|
+
if ( cmp(&a[mid], &v) > 0 ) r = mid; \
|
|
160
|
+
else l = mid + 1; \
|
|
161
|
+
} \
|
|
162
|
+
if ( l < k ) { \
|
|
163
|
+
memmove(a + l + 1, a + l, (size_t)(k - l) * sizeof(TYPE)); \
|
|
164
|
+
a[l] = v; \
|
|
165
|
+
} \
|
|
166
|
+
} \
|
|
167
|
+
} \
|
|
168
|
+
TYPE *cur = a; \
|
|
169
|
+
TYPE *next = aux; \
|
|
170
|
+
for ( ca_size_t width = CA_SORT_INSERTION_THRESHOLD; width < n; \
|
|
171
|
+
width <<= 1 ) { \
|
|
172
|
+
for ( ca_size_t lo = 0; lo < n; lo += (width << 1) ) { \
|
|
173
|
+
ca_size_t mid = lo + width; \
|
|
174
|
+
ca_size_t hi = lo + (width << 1); \
|
|
175
|
+
if ( mid > n ) mid = n; \
|
|
176
|
+
if ( hi > n ) hi = n; \
|
|
177
|
+
if ( mid >= hi || cmp(&cur[mid - 1], &cur[mid]) <= 0 ) { \
|
|
178
|
+
memcpy(next + lo, cur + lo, (size_t)(hi - lo) * sizeof(TYPE)); \
|
|
179
|
+
continue; \
|
|
180
|
+
} \
|
|
181
|
+
ca_size_t i = lo, j = mid, k = lo; \
|
|
182
|
+
while ( i < mid && j < hi ) { \
|
|
183
|
+
if ( cmp(&cur[j], &cur[i]) < 0 ) next[k++] = cur[j++]; \
|
|
184
|
+
else next[k++] = cur[i++]; \
|
|
185
|
+
} \
|
|
186
|
+
while ( i < mid ) next[k++] = cur[i++]; \
|
|
187
|
+
while ( j < hi ) next[k++] = cur[j++]; \
|
|
188
|
+
} \
|
|
189
|
+
{ TYPE *t = cur; cur = next; next = t; } \
|
|
190
|
+
} \
|
|
191
|
+
if ( cur != a ) { \
|
|
192
|
+
memcpy(a, cur, (size_t) n * sizeof(TYPE)); \
|
|
193
|
+
} \
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/* (VALUE, index) pairs, as the object lane of the sort kernels holds them. */
|
|
197
|
+
typedef struct ca_pair_object { VALUE v; ca_size_t i; } ca_pair_object;
|
|
198
|
+
void ca_sort_merge_pair_object (ca_pair_object *a, ca_pair_object *aux,
|
|
199
|
+
ca_size_t n,
|
|
200
|
+
int (*cmp)(const void *, const void *));
|
|
201
|
+
|
|
132
202
|
#endif /* CA_SORT_KERNELS_H */
|
data/ext/ca_sweep_engine.c
CHANGED
|
@@ -42,9 +42,9 @@ ca_sweep_refuse_shapes (CArray *a, CArray *b)
|
|
|
42
42
|
volatile VALUE sa = ca_sweep_shape_str(a);
|
|
43
43
|
volatile VALUE sb = ca_sweep_shape_str(b);
|
|
44
44
|
rb_raise(rb_eArgError,
|
|
45
|
-
"shape mismatch between operands (%s and %s); a scalar pairs "
|
|
45
|
+
"%s: shape mismatch between operands (%s and %s); a scalar pairs "
|
|
46
46
|
"with any array, two arrays only when their shapes agree",
|
|
47
|
-
StringValueCStr(sa), StringValueCStr(sb));
|
|
47
|
+
ca_calling_method_name(), StringValueCStr(sa), StringValueCStr(sb));
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
int
|
|
@@ -85,6 +85,26 @@ ca_sweep_pair_operands (ca_sweep_state_t *st)
|
|
|
85
85
|
return donor;
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
+
/* Non-zero when INPUT k_op lives in the same root as an OUTPUT other
|
|
89
|
+
* than itself. Read in place, or chunk by chunk after earlier chunks
|
|
90
|
+
* were written, it would show the kernel cells already written; such an
|
|
91
|
+
* input is read whole before the walk. An input that is the output
|
|
92
|
+
* itself is read in place: each cell is read before it is written. */
|
|
93
|
+
static int
|
|
94
|
+
ca_sweep_input_overlaps_output (ca_sweep_state_t *st, int k_op)
|
|
95
|
+
{
|
|
96
|
+
int j;
|
|
97
|
+
CArray *ca = st->cx[k_op];
|
|
98
|
+
if (st->fsync[k_op] != '0' || ca_is_scalar(ca)) return 0;
|
|
99
|
+
for (j = 0; j < st->n_ops; j++) {
|
|
100
|
+
if (st->fsync[j] == '1' && st->cx[j] != ca &&
|
|
101
|
+
ca_shares_root(ca, st->cx[j])) {
|
|
102
|
+
return 1;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return 0;
|
|
106
|
+
}
|
|
107
|
+
|
|
88
108
|
/* Non-zero when any INPUT operand carries a mask. Asks, allocates
|
|
89
109
|
* nothing. */
|
|
90
110
|
static int
|
|
@@ -97,18 +117,6 @@ ca_sweep_any_input_mask (ca_sweep_state_t *st)
|
|
|
97
117
|
return 0;
|
|
98
118
|
}
|
|
99
119
|
|
|
100
|
-
NORETURN(static void ca_sweep_refuse_masked_input (ca_sweep_state_t *st,
|
|
101
|
-
const char *fallback));
|
|
102
|
-
|
|
103
|
-
static void
|
|
104
|
-
ca_sweep_refuse_masked_input (ca_sweep_state_t *st, const char *fallback)
|
|
105
|
-
{
|
|
106
|
-
rb_raise(rb_eRuntimeError,
|
|
107
|
-
"%s: masked INPUT not allowed in NO_MASK form "
|
|
108
|
-
"(use the *_MASKED form to handle masked cells explicitly)",
|
|
109
|
-
st->src_label ? st->src_label : fallback);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
120
|
/* Give back everything acquire has taken so far, without syncing: detach
|
|
113
121
|
* attached operands, free owned scratch (xmalloc on the whole-buffer path,
|
|
114
122
|
* arena on the chunked one) and the mask buffers. Called when a read
|
|
@@ -158,12 +166,14 @@ ca_sweep_acquire_body (VALUE arg)
|
|
|
158
166
|
/* Per-operand acquire:
|
|
159
167
|
* OUTPUT (fsync == '1') -> ca_attach + base = ca->ptr
|
|
160
168
|
* INPUT alias -> ca_attach + base = ca->ptr
|
|
161
|
-
* INPUT non-alias non-scalar
|
|
169
|
+
* INPUT non-alias non-scalar,
|
|
170
|
+
* or sharing an OUTPUT's root -> xmalloc scratch + ca_xfer_all
|
|
162
171
|
* INPUT non-alias never attaches the operand itself; the scratch
|
|
163
172
|
* copy carries the values into the kernel. */
|
|
164
173
|
for (k_op = 0; k_op < st->n_ops; k_op++) {
|
|
165
174
|
CArray *ca = st->cx[k_op];
|
|
166
|
-
if (st->fsync[k_op] == '1' ||
|
|
175
|
+
if (st->fsync[k_op] == '1' ||
|
|
176
|
+
(ca_attach_is_alias(ca) && ! ca_sweep_input_overlaps_output(st, k_op))) {
|
|
167
177
|
ca_attach(ca);
|
|
168
178
|
st->attached[k_op] = 1;
|
|
169
179
|
st->base[k_op] = (char *) ca->ptr;
|
|
@@ -204,8 +214,7 @@ ca_sweep_acquire_body (VALUE arg)
|
|
|
204
214
|
for (k_op = 0; k_op < st->n_ops; k_op++) {
|
|
205
215
|
CArray *ca = st->cx[k_op];
|
|
206
216
|
if (st->fsync[k_op] != '1') continue;
|
|
207
|
-
|
|
208
|
-
if (!ca->mask) ca_create_mask(ca);
|
|
217
|
+
ca_create_mask(ca);
|
|
209
218
|
memcpy(ca->mask->ptr, st->m0, st->n_kernel);
|
|
210
219
|
}
|
|
211
220
|
}
|
|
@@ -232,17 +241,12 @@ ca_sweep_acquire (ca_sweep_state_t *st)
|
|
|
232
241
|
st->attached[k_op] = 0;
|
|
233
242
|
}
|
|
234
243
|
st->m0 = NULL;
|
|
235
|
-
/* CAREFUL: do not reset st->no_mask here — caller sets it before
|
|
236
|
-
* acquire and the NO_MASK guard below consumes it. */
|
|
237
244
|
|
|
238
245
|
/* Refusals come first, while nothing is held. */
|
|
239
246
|
ca_sweep_pair_operands(st);
|
|
240
247
|
ctx.st = st;
|
|
241
248
|
ctx.ms = NULL;
|
|
242
249
|
ctx.any_input_mask = ca_sweep_any_input_mask(st);
|
|
243
|
-
if (ctx.any_input_mask && st->no_mask) {
|
|
244
|
-
ca_sweep_refuse_masked_input(st, "ca_sweep_acquire");
|
|
245
|
-
}
|
|
246
250
|
|
|
247
251
|
rb_protect(ca_sweep_acquire_body, (VALUE) &ctx, &tag);
|
|
248
252
|
if (ctx.ms) xfree(ctx.ms);
|
|
@@ -280,14 +284,60 @@ ca_sweep_run_chunked (ca_sweep_state_t *st, VALUE (*walk)(VALUE), VALUE arg)
|
|
|
280
284
|
ca_sweep_release_chunked(st);
|
|
281
285
|
}
|
|
282
286
|
|
|
287
|
+
static VALUE
|
|
288
|
+
ca_sweep_sync_one (VALUE arg)
|
|
289
|
+
{
|
|
290
|
+
ca_sync((CArray *) arg);
|
|
291
|
+
return Qnil;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/* Sync the OUTPUTs (reverse order). Writing back through a view's slots
|
|
295
|
+
* can raise (a CAObject's store hook); the raise is held so that release
|
|
296
|
+
* still detaches and frees everything, and ca_sweep_reraise propagates
|
|
297
|
+
* it afterwards. An entity's sync does nothing that can raise, so it is
|
|
298
|
+
* called directly: the outputs this library allocates itself pay nothing. */
|
|
299
|
+
static int
|
|
300
|
+
ca_sweep_sync_outputs (ca_sweep_state_t *st, volatile VALUE *error)
|
|
301
|
+
{
|
|
302
|
+
int k_op, first = 0;
|
|
303
|
+
for (k_op = st->n_ops - 1; k_op >= 0; k_op--) {
|
|
304
|
+
CArray *ca = st->cx[k_op];
|
|
305
|
+
if (st->fsync[k_op] != '1') {
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
308
|
+
if (ca_is_entity(ca)) {
|
|
309
|
+
ca_sync(ca);
|
|
310
|
+
}
|
|
311
|
+
else {
|
|
312
|
+
int tag = 0;
|
|
313
|
+
rb_protect(ca_sweep_sync_one, (VALUE) ca, &tag);
|
|
314
|
+
if (tag) {
|
|
315
|
+
if (! first) {
|
|
316
|
+
first = tag;
|
|
317
|
+
*error = rb_errinfo();
|
|
318
|
+
}
|
|
319
|
+
rb_set_errinfo(Qnil);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return first;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
static void
|
|
327
|
+
ca_sweep_reraise (int tag, VALUE error)
|
|
328
|
+
{
|
|
329
|
+
if (tag) {
|
|
330
|
+
rb_set_errinfo(error);
|
|
331
|
+
rb_jump_tag(tag);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
283
335
|
void
|
|
284
336
|
ca_sweep_release (ca_sweep_state_t *st)
|
|
285
337
|
{
|
|
286
338
|
int k_op;
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
if (st->fsync[k_op] == '1') ca_sync(st->cx[k_op]);
|
|
290
|
-
}
|
|
339
|
+
volatile VALUE error = Qnil;
|
|
340
|
+
int tag = ca_sweep_sync_outputs(st, &error);
|
|
291
341
|
/* detach attached / xfree owned buffers (reverse order) */
|
|
292
342
|
for (k_op = st->n_ops - 1; k_op >= 0; k_op--) {
|
|
293
343
|
if (st->attached[k_op]) {
|
|
@@ -301,6 +351,7 @@ ca_sweep_release (ca_sweep_state_t *st)
|
|
|
301
351
|
xfree(st->m0);
|
|
302
352
|
st->m0 = NULL;
|
|
303
353
|
}
|
|
354
|
+
ca_sweep_reraise(tag, error);
|
|
304
355
|
}
|
|
305
356
|
|
|
306
357
|
/* ===== Chunked path implementation ===== */
|
|
@@ -315,13 +366,14 @@ ca_sweep_acquire_chunked_body (VALUE arg)
|
|
|
315
366
|
/* per-operand acquire:
|
|
316
367
|
* OUTPUT (fsync == '1') : ca_attach + base_orig = ca->ptr (legitimate)
|
|
317
368
|
* INPUT alias : ca_attach + base_orig = ca->ptr (zero-copy)
|
|
318
|
-
* INPUT non-alias non-scalar
|
|
369
|
+
* INPUT non-alias non-scalar,
|
|
370
|
+
* or sharing an OUTPUT's root : arena chunk scratch of chunk_n_max bytes
|
|
319
371
|
* INPUT scalar : ca_attach + base_orig = ca->ptr (1 cell)
|
|
320
372
|
*/
|
|
321
373
|
for (k_op = 0; k_op < st->n_ops; k_op++) {
|
|
322
374
|
CArray *ca = st->cx[k_op];
|
|
323
375
|
if (st->fsync[k_op] == '1' || ca_is_scalar(ca) ||
|
|
324
|
-
ca_attach_is_alias(ca)) {
|
|
376
|
+
(ca_attach_is_alias(ca) && ! ca_sweep_input_overlaps_output(st, k_op))) {
|
|
325
377
|
ca_attach(ca);
|
|
326
378
|
st->attached[k_op] = 1;
|
|
327
379
|
st->base_orig[k_op] = (char *) ca->ptr;
|
|
@@ -352,8 +404,7 @@ ca_sweep_acquire_chunked_body (VALUE arg)
|
|
|
352
404
|
for (k_op = 0; k_op < st->n_ops; k_op++) {
|
|
353
405
|
CArray *ca = st->cx[k_op];
|
|
354
406
|
if (st->fsync[k_op] != '1') continue;
|
|
355
|
-
|
|
356
|
-
if (!ca->mask) ca_create_mask(ca);
|
|
407
|
+
ca_create_mask(ca);
|
|
357
408
|
}
|
|
358
409
|
}
|
|
359
410
|
|
|
@@ -410,13 +461,20 @@ ca_sweep_acquire_chunked (ca_sweep_state_t *st)
|
|
|
410
461
|
st->chunk_n_max = 1;
|
|
411
462
|
}
|
|
412
463
|
|
|
464
|
+
/* An INPUT sharing an OUTPUT's root is read whole before anything is
|
|
465
|
+
written: the walk becomes a single chunk, so its data and its mask
|
|
466
|
+
are gathered before the first flush. */
|
|
467
|
+
for (k_op = 0; k_op < st->n_ops; k_op++) {
|
|
468
|
+
if (ca_sweep_input_overlaps_output(st, k_op) && st->n_kernel > 0) {
|
|
469
|
+
st->chunk_n_max = st->n_kernel;
|
|
470
|
+
break;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
413
474
|
/* Refusals come first, while nothing is held. */
|
|
414
475
|
ctx.st = st;
|
|
415
476
|
ctx.ms = NULL;
|
|
416
477
|
ctx.any_input_mask = ca_sweep_any_input_mask(st);
|
|
417
|
-
if (ctx.any_input_mask && st->no_mask) {
|
|
418
|
-
ca_sweep_refuse_masked_input(st, "ca_sweep_acquire_chunked");
|
|
419
|
-
}
|
|
420
478
|
|
|
421
479
|
rb_protect(ca_sweep_acquire_chunked_body, (VALUE) &ctx, &tag);
|
|
422
480
|
if (tag) {
|
|
@@ -454,9 +512,7 @@ ca_sweep_gather_chunk_mask (ca_sweep_state_t *st, ca_size_t off, ca_size_t n)
|
|
|
454
512
|
}
|
|
455
513
|
}
|
|
456
514
|
|
|
457
|
-
/* Copy the chunk just finished out to every OUTPUT operand's mask.
|
|
458
|
-
* after the author's loop over that chunk, so m_out writes into m0 are
|
|
459
|
-
* carried through. */
|
|
515
|
+
/* Copy the chunk just finished out to every OUTPUT operand's mask. */
|
|
460
516
|
static void
|
|
461
517
|
ca_sweep_flush_chunk_mask (ca_sweep_state_t *st)
|
|
462
518
|
{
|
|
@@ -510,7 +566,6 @@ ca_sweep_next_chunk (ca_sweep_state_t *st)
|
|
|
510
566
|
st->chunk_off = 0;
|
|
511
567
|
st->chunked_state = 1;
|
|
512
568
|
} else {
|
|
513
|
-
/* the chunk that just finished is the author's last word on its mask */
|
|
514
569
|
ca_sweep_flush_chunk_mask(st);
|
|
515
570
|
/* advance */
|
|
516
571
|
st->chunk_off += st->chunk_n;
|
|
@@ -543,16 +598,15 @@ void
|
|
|
543
598
|
ca_sweep_release_chunked (ca_sweep_state_t *st)
|
|
544
599
|
{
|
|
545
600
|
int k_op;
|
|
601
|
+
volatile VALUE error = Qnil;
|
|
602
|
+
int tag;
|
|
546
603
|
/* a walk that raised has already given everything back */
|
|
547
604
|
if (st->chunked_state == 3) return;
|
|
548
605
|
/* The final chunk has no next_chunk call to flush it, so it is flushed
|
|
549
|
-
* here.
|
|
550
|
-
* per-cell m_out writes over that last chunk. */
|
|
606
|
+
* here. */
|
|
551
607
|
ca_sweep_flush_chunk_mask(st);
|
|
552
|
-
/* sync OUTPUTs (
|
|
553
|
-
|
|
554
|
-
if (st->fsync[k_op] == '1') ca_sync(st->cx[k_op]);
|
|
555
|
-
}
|
|
608
|
+
/* sync OUTPUTs (regardless of whether chunk loop ran) */
|
|
609
|
+
tag = ca_sweep_sync_outputs(st, &error);
|
|
556
610
|
/* detach attached / release arena scratch buffers (reverse order) */
|
|
557
611
|
for (k_op = st->n_ops - 1; k_op >= 0; k_op--) {
|
|
558
612
|
if (st->attached[k_op]) {
|
|
@@ -570,13 +624,16 @@ ca_sweep_release_chunked (ca_sweep_state_t *st)
|
|
|
570
624
|
ca_lazy_arena_release(st->mask_scratch);
|
|
571
625
|
st->mask_scratch = NULL;
|
|
572
626
|
}
|
|
627
|
+
if (tag) {
|
|
628
|
+
st->chunked_state = 3; /* everything has been given back */
|
|
629
|
+
}
|
|
630
|
+
ca_sweep_reraise(tag, error);
|
|
573
631
|
}
|
|
574
632
|
|
|
575
633
|
/* ===== WHOLE_BUFFER function form (rb_ensure-protected) ===== */
|
|
576
634
|
|
|
577
635
|
typedef struct {
|
|
578
636
|
CArray *ca;
|
|
579
|
-
int writable;
|
|
580
637
|
ca_with_buffer_body_fn body_fn;
|
|
581
638
|
void *user_data;
|
|
582
639
|
} ca_with_buffer_ctx_t;
|
|
@@ -589,49 +646,15 @@ ca_with_buffer_body_trampoline (VALUE ctx_val)
|
|
|
589
646
|
return Qnil;
|
|
590
647
|
}
|
|
591
648
|
|
|
592
|
-
static VALUE
|
|
593
|
-
ca_with_buffer_ensure_trampoline (VALUE ctx_val)
|
|
594
|
-
{
|
|
595
|
-
ca_with_buffer_ctx_t *ctx = (ca_with_buffer_ctx_t *) ctx_val;
|
|
596
|
-
if (ctx->writable) ca_sync(ctx->ca);
|
|
597
|
-
ca_detach(ctx->ca);
|
|
598
|
-
return Qnil;
|
|
599
|
-
}
|
|
600
|
-
|
|
601
649
|
void
|
|
602
650
|
rb_ca_call_with_buffer (VALUE r_ca, int writable,
|
|
603
651
|
ca_with_buffer_body_fn body, void *user_data)
|
|
604
652
|
{
|
|
605
653
|
ca_with_buffer_ctx_t ctx;
|
|
606
654
|
TypedData_Get_Struct(r_ca, CArray, &carray_data_type, ctx.ca);
|
|
607
|
-
/* CAREFUL: attach outside rb_ensure. If attach raises, we are not
|
|
608
|
-
* yet inside the ensure scope, so the ensure trampoline will not
|
|
609
|
-
* fire on a not-attached ca (which would try to detach and crash). */
|
|
610
|
-
ca_attach(ctx.ca);
|
|
611
|
-
ctx.writable = writable;
|
|
612
655
|
ctx.body_fn = body;
|
|
613
656
|
ctx.user_data = user_data;
|
|
614
|
-
|
|
615
|
-
|
|
657
|
+
ca_attach_window(1, &ctx.ca, writable ? "w" : "r",
|
|
658
|
+
ca_with_buffer_body_trampoline, (VALUE) &ctx);
|
|
616
659
|
}
|
|
617
660
|
|
|
618
|
-
void
|
|
619
|
-
ca_sweep_check_same_shape (CArray *ca_in, CArray *ca_out, const char *src_label)
|
|
620
|
-
{
|
|
621
|
-
int k;
|
|
622
|
-
const char *lbl = src_label ? src_label : "ca_sweep_check_same_shape";
|
|
623
|
-
if (ca_in->ndim != ca_out->ndim) {
|
|
624
|
-
rb_raise(rb_eRuntimeError,
|
|
625
|
-
"%s: shape mismatch (ndim %d vs %d)",
|
|
626
|
-
lbl, ca_in->ndim, ca_out->ndim);
|
|
627
|
-
}
|
|
628
|
-
for (k = 0; k < ca_in->ndim; k++) {
|
|
629
|
-
if (ca_in->dim[k] != ca_out->dim[k]) {
|
|
630
|
-
rb_raise(rb_eRuntimeError,
|
|
631
|
-
"%s: shape mismatch at dim[%d] (%lld vs %lld)",
|
|
632
|
-
lbl, k,
|
|
633
|
-
(long long) ca_in->dim[k],
|
|
634
|
-
(long long) ca_out->dim[k]);
|
|
635
|
-
}
|
|
636
|
-
}
|
|
637
|
-
}
|
data/ext/ca_sweep_engine.h
CHANGED
|
@@ -5,8 +5,7 @@
|
|
|
5
5
|
* Established in PROPOSAL_L0_AUTHOR_SURFACE L0.1 (2026-06-11). Extracts
|
|
6
6
|
* the xfer_all-aware lifecycle template previously duplicated 7-times in
|
|
7
7
|
* ext/carray_call_cfunc.c (= one body per arity 1..7) into a single shared
|
|
8
|
-
* helper
|
|
9
|
-
* family (L0.2a) and the WHOLE_BUFFER family (L0.2c).
|
|
8
|
+
* helper, also used by the chunked slab family (ca_call_cslab_*).
|
|
10
9
|
*
|
|
11
10
|
* Lifecycle template:
|
|
12
11
|
* (1) per-operand acquire
|
|
@@ -64,15 +63,10 @@ typedef struct ca_sweep_state {
|
|
|
64
63
|
boolean8_t *m0;
|
|
65
64
|
ca_size_t n_kernel;
|
|
66
65
|
const char *src_label;
|
|
67
|
-
/* no_mask: if non-zero, ca_sweep_acquire raises when any INPUT operand has
|
|
68
|
-
* a mask. Used by the NO_MASK macro forms (CA_FOR_EACH_ELEMENT etc.) to
|
|
69
|
-
* implement Q3=(ii) runtime error policy. Default 0 (= masked INPUT OK,
|
|
70
|
-
* m0 built as usual). Caller sets before calling ca_sweep_acquire. */
|
|
71
|
-
int no_mask;
|
|
72
66
|
|
|
73
67
|
/* === chunked path fields (L0.1 chunk, PROPOSAL_L0_AUTHOR_SURFACE) === */
|
|
74
68
|
/* Used by ca_sweep_acquire_chunked / ca_sweep_next_chunk / ca_sweep_release_chunked
|
|
75
|
-
* (= the
|
|
69
|
+
* (= the chunked slab family, ca_call_cslab_*). Unused by the whole-materialise path
|
|
76
70
|
* (ca_sweep_acquire / ca_sweep_release) — cfunc and other whole-buffer callers
|
|
77
71
|
* leave these zero. */
|
|
78
72
|
char **base_orig; /* per-op original ptr (= ca->ptr for
|
|
@@ -93,7 +87,7 @@ typedef struct ca_sweep_state {
|
|
|
93
87
|
|
|
94
88
|
/* Validate fsync length, pair the operands (shape), acquire per-op
|
|
95
89
|
* buffers, build mask m0, propagate mask to OUTPUTs. May raise: refusals
|
|
96
|
-
* (fsync length, shape mismatch
|
|
90
|
+
* (fsync length, shape mismatch) before
|
|
97
91
|
* anything is held, and a raising operand read after -- in which case
|
|
98
92
|
* everything acquired so far is detached / freed before the raise
|
|
99
93
|
* propagates, so the caller must not call release. The chunked path
|
|
@@ -120,13 +114,7 @@ void ca_sweep_run_chunked (ca_sweep_state_t *st, VALUE (*walk)(VALUE), VALUE arg
|
|
|
120
114
|
int ca_sweep_same_shape (CArray *a, CArray *b);
|
|
121
115
|
NORETURN(void ca_sweep_refuse_shapes (CArray *a, CArray *b));
|
|
122
116
|
|
|
123
|
-
/*
|
|
124
|
-
* corruption seam prevention). Raises if ndim differs or any dim[k]
|
|
125
|
-
* differs. Returns void; raises on mismatch. */
|
|
126
|
-
void ca_sweep_check_same_shape (CArray *ca_in, CArray *ca_out,
|
|
127
|
-
const char *src_label);
|
|
128
|
-
|
|
129
|
-
/* ===== Chunked path API (sweep ELEMENT macro family, L0.1 chunk) =====
|
|
117
|
+
/* ===== Chunked path API (ca_call_cslab_*) =====
|
|
130
118
|
*
|
|
131
119
|
* Unlike ca_sweep_acquire / ca_sweep_release (= whole-buffer materialise used
|
|
132
120
|
* by cfunc), the chunked path keeps INPUT memory peak bounded to chunk
|
|
@@ -134,7 +122,7 @@ void ca_sweep_check_same_shape (CArray *ca_in, CArray *ca_out,
|
|
|
134
122
|
* memory peak guarantee: shrinking views materialise into chunk scratch
|
|
135
123
|
* sized to inner-axis multiples, not full operand size.
|
|
136
124
|
*
|
|
137
|
-
* Usage shape
|
|
125
|
+
* Usage shape:
|
|
138
126
|
* ca_sweep_acquire_chunked(&st);
|
|
139
127
|
* while (ca_sweep_next_chunk(&st)) {
|
|
140
128
|
* for (k = 0; k < st.chunk_n; k++) {
|
|
@@ -162,9 +150,8 @@ void ca_sweep_check_same_shape (CArray *ca_in, CArray *ca_out,
|
|
|
162
150
|
* indexed within the chunk, NOT by the flat cell index. (This differs
|
|
163
151
|
* from the whole-buffer path, where m0 spans n_kernel.)
|
|
164
152
|
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
* masks when the chunk finishes -- at the top of the following
|
|
153
|
+
* The chunk's m0 is flushed into the OUTPUT operands' masks when the
|
|
154
|
+
* chunk finishes -- at the top of the following
|
|
168
155
|
* ca_sweep_next_chunk, and once more in ca_sweep_release_chunked for
|
|
169
156
|
* the last chunk -- so the OUTPUT masks are created up front in
|
|
170
157
|
* acquire_chunked rather than propagated in one go at release.
|
data/ext/ca_triop_dispatch.c
CHANGED
|
@@ -27,6 +27,7 @@ ca_triop_kernel_lookup_vvv (uint16_t op_id, int8_t common_dt)
|
|
|
27
27
|
case CA_TRIOP_FMA: return ca_triop_fma [common_dt];
|
|
28
28
|
case CA_TRIOP_FMS: return ca_triop_fms [common_dt];
|
|
29
29
|
case CA_TRIOP_CLIP: return ca_triop_clip [common_dt];
|
|
30
|
+
case CA_TRIOP_SELECT: return ca_triop_select [common_dt];
|
|
30
31
|
default: return NULL;
|
|
31
32
|
}
|
|
32
33
|
}
|
|
@@ -38,7 +39,9 @@ ca_triop_kernel_lookup_vvv (uint16_t op_id, int8_t common_dt)
|
|
|
38
39
|
int8_t
|
|
39
40
|
ca_lazy_promote_triop (uint16_t op_id, int8_t dt1, int8_t dt2, int8_t dt3)
|
|
40
41
|
{
|
|
41
|
-
(
|
|
42
|
+
if ( op_id == CA_TRIOP_SELECT ) {
|
|
43
|
+
return (int8_t) ca_promote_type(dt2, dt3);
|
|
44
|
+
}
|
|
42
45
|
return (int8_t) ca_promote_type(ca_promote_type(dt1, dt2), dt3);
|
|
43
46
|
}
|
|
44
47
|
|
data/ext/ca_triop_dispatch.h
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
runs (cast-before invariant, mirrors CABinOp). Output data_type is
|
|
12
12
|
always the common data_type (preserve).
|
|
13
13
|
|
|
14
|
-
Scope: fma / fms / clip (see ext/mkkernel.rb
|
|
14
|
+
Scope: fma / fms / clip / select (see ext/mkkernel.rb).
|
|
15
15
|
None of the currently-defined triops trap on integer zero divisor.
|
|
16
16
|
|
|
17
17
|
---------------------------------------------------------------------------- */
|
|
@@ -29,6 +29,7 @@ enum {
|
|
|
29
29
|
CA_TRIOP_FMA = 0, /* a * b + c (single-rounding on float) */
|
|
30
30
|
CA_TRIOP_FMS = 1, /* a * b - c (single-rounding on float) */
|
|
31
31
|
CA_TRIOP_CLIP = 2, /* min(max(a, lo), hi), NaN-preserving */
|
|
32
|
+
CA_TRIOP_SELECT = 3, /* a ? b : c (lazy then_else) */
|
|
32
33
|
|
|
33
34
|
CA_TRIOP_COUNT
|
|
34
35
|
};
|
|
@@ -40,6 +41,7 @@ enum {
|
|
|
40
41
|
extern ca_triop_func_t ca_triop_fma [CA_NTYPE];
|
|
41
42
|
extern ca_triop_func_t ca_triop_fms [CA_NTYPE];
|
|
42
43
|
extern ca_triop_func_t ca_triop_clip [CA_NTYPE];
|
|
44
|
+
extern ca_triop_func_t ca_triop_select [CA_NTYPE];
|
|
43
45
|
|
|
44
46
|
/* ------------------------------------------------------------------- */
|
|
45
47
|
/* Dispatch API */
|
|
@@ -49,7 +51,8 @@ extern ca_triop_func_t ca_triop_clip [CA_NTYPE];
|
|
|
49
51
|
not implemented at the requested data_type. */
|
|
50
52
|
ca_triop_func_t ca_triop_kernel_lookup_vvv (uint16_t op_id, int8_t common_dt);
|
|
51
53
|
|
|
52
|
-
/* 3-way promotion: common data_type of (dt1, dt2, dt3).
|
|
54
|
+
/* 3-way promotion: common data_type of (dt1, dt2, dt3). For select it is
|
|
55
|
+
that of the two branches (dt2, dt3); the condition is converted to it. */
|
|
53
56
|
int8_t ca_lazy_promote_triop (uint16_t op_id, int8_t dt1, int8_t dt2, int8_t dt3);
|
|
54
57
|
|
|
55
58
|
/* Each operand's cast-target data_type (= common data_type for all three,
|