carray-jit 0.1.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 +7 -0
- data/.yardopts +10 -0
- data/CHANGELOG.md +84 -0
- data/LICENSE +21 -0
- data/README.md +88 -0
- data/bin/carray-jit +194 -0
- data/carray-jit.gemspec +41 -0
- data/docs/00_Introduction.md +40 -0
- data/docs/01_GettingStarted.md +80 -0
- data/docs/02_KernelShapes.md +397 -0
- data/docs/03_SupportedFeatures.md +595 -0
- data/docs/04_Compiling.md +234 -0
- data/docs/05_DesignNotes.md +136 -0
- data/docs/06_Cheatsheet.md +177 -0
- data/examples/README.md +56 -0
- data/examples/applications/game_of_life.rb +161 -0
- data/examples/applications/heat_equation.rb +117 -0
- data/examples/applications/kepler.rb +178 -0
- data/examples/applications/mandelbrot.rb +151 -0
- data/examples/applications/moving_average.rb +124 -0
- data/examples/applications/partial_sums.rb +141 -0
- data/examples/applications/point_cloud.rb +110 -0
- data/examples/applications/quicksort.rb +118 -0
- data/examples/applications/recursion.rb +121 -0
- data/examples/applications/relaxation.rb +115 -0
- data/examples/applications/sensor_gaps.rb +118 -0
- data/examples/applications/sieve.rb +95 -0
- data/examples/applications/sobel_edges.rb +80 -0
- data/examples/features/01_element_wise.rb +69 -0
- data/examples/features/02_stencil.rb +40 -0
- data/examples/features/03_recurrence.rb +50 -0
- data/examples/features/04_thomas.rb +81 -0
- data/examples/features/05_reduction.rb +90 -0
- data/examples/features/06_jit_contract.rb +58 -0
- data/examples/features/07_masks.rb +55 -0
- data/examples/features/08_views.rb +46 -0
- data/examples/features/09_inspecting.rb +55 -0
- data/examples/features/10_complex.rb +107 -0
- data/examples/features/11_c_functions.rb +260 -0
- data/examples/features/12_sweep.rb +139 -0
- data/examples/features/13_cscalar.rb +80 -0
- data/examples/features/14_stencil_window.rb +106 -0
- data/examples/features/15_loops.rb +148 -0
- data/examples/features/16_raising.rb +69 -0
- data/ext/carray_jit_access/carray_jit_access.c +460 -0
- data/ext/carray_jit_access/extconf.rb +8 -0
- data/lib/carray/jit/analyzer.rb +1847 -0
- data/lib/carray/jit/block_reader.rb +139 -0
- data/lib/carray/jit/c_function.rb +777 -0
- data/lib/carray/jit/c_generator.rb +2305 -0
- data/lib/carray/jit/compiler.rb +468 -0
- data/lib/carray/jit/errors.rb +37 -0
- data/lib/carray/jit/expression.rb +202 -0
- data/lib/carray/jit/kernel.rb +509 -0
- data/lib/carray/jit/node.rb +573 -0
- data/lib/carray/jit/sweep.rb +97 -0
- data/lib/carray/jit/type_assignment.rb +811 -0
- data/lib/carray/jit/version.rb +5 -0
- data/lib/carray/jit.rb +1210 -0
- metadata +139 -0
|
@@ -0,0 +1,2305 @@
|
|
|
1
|
+
require "digest"
|
|
2
|
+
|
|
3
|
+
class CArray
|
|
4
|
+
module JIT
|
|
5
|
+
|
|
6
|
+
# Emits C for a typed kernel body.
|
|
7
|
+
#
|
|
8
|
+
# The output is meant to be read: real indentation, the same variable
|
|
9
|
+
# names the block used, and casts only where a type actually changes.
|
|
10
|
+
# `CARRAY_JIT_DUMP=1` prints it.
|
|
11
|
+
#
|
|
12
|
+
# Every kernel has the same C signature, so one Fiddle::Function shape
|
|
13
|
+
# serves all of them; the per-kernel detail is unpacked into named locals
|
|
14
|
+
# at the top of the body, where it also reads better.
|
|
15
|
+
class CGenerator
|
|
16
|
+
|
|
17
|
+
STORAGE_C_TYPES = {
|
|
18
|
+
"float64" => "double",
|
|
19
|
+
"float32" => "float",
|
|
20
|
+
"int64" => "int64_t",
|
|
21
|
+
"int32" => "int32_t",
|
|
22
|
+
"int16" => "int16_t",
|
|
23
|
+
"int8" => "int8_t",
|
|
24
|
+
"uint64" => "uint64_t",
|
|
25
|
+
"uint32" => "uint32_t",
|
|
26
|
+
"uint16" => "uint16_t",
|
|
27
|
+
"uint8" => "uint8_t",
|
|
28
|
+
"boolean" => "uint8_t",
|
|
29
|
+
# C99 lays a complex out as two reals in order, which is what CArray
|
|
30
|
+
# stores, so a cell is read in place rather than assembled.
|
|
31
|
+
"cmplx64" => "float _Complex",
|
|
32
|
+
"cmplx128" => "double _Complex",
|
|
33
|
+
}.freeze
|
|
34
|
+
|
|
35
|
+
COMPUTATION_C_TYPES = {
|
|
36
|
+
:double => "double",
|
|
37
|
+
:float => "float",
|
|
38
|
+
:int64 => "int64_t",
|
|
39
|
+
:uint64 => "uint64_t",
|
|
40
|
+
:complex => "double _Complex",
|
|
41
|
+
:float_complex => "float _Complex",
|
|
42
|
+
:boolean => "int",
|
|
43
|
+
}.freeze
|
|
44
|
+
|
|
45
|
+
# How far each numeric type has to be widened to reach another, taken
|
|
46
|
+
# from the order they widen in. Only widening is ever emitted: nothing
|
|
47
|
+
# narrows a complex back to a real without the block having asked for
|
|
48
|
+
# it by name.
|
|
49
|
+
NUMERIC_RANK =
|
|
50
|
+
TypeAssignment::NUMERIC_TYPES.each_with_index.to_h.freeze
|
|
51
|
+
|
|
52
|
+
FUNCTION_NAME = "carray_jit_kernel"
|
|
53
|
+
|
|
54
|
+
# The frame's own entry point. Same signature, same statements, and
|
|
55
|
+
# the reads carry the border rule; the caller walks it over the boxes
|
|
56
|
+
# the interior left, so nothing here has to know which cell is which.
|
|
57
|
+
BORDER_NAME = "carray_jit_border"
|
|
58
|
+
|
|
59
|
+
# `functions` carries the address of each C function the block called,
|
|
60
|
+
# beside the captured scalars: the kernel is not linked against them, so
|
|
61
|
+
# a compiled kernel does not depend on where they came from.
|
|
62
|
+
# `functions` carries the address of each C function the block called and
|
|
63
|
+
# `data` the address of each array it handed to one whole. Both sit
|
|
64
|
+
# beside the captured scalars for the same reason: they do not vary with
|
|
65
|
+
# the cell, and the kernel is not linked against either.
|
|
66
|
+
SIGNATURE =
|
|
67
|
+
"(char **pointers, int64_t *strides, int64_t *bounds, " \
|
|
68
|
+
"double *reals, int64_t *integers, void **functions, void **data, " \
|
|
69
|
+
"char **mask_pointers, int64_t *mask_strides, int32_t *error)".freeze
|
|
70
|
+
|
|
71
|
+
ARGUMENTS =
|
|
72
|
+
"pointers, strides, bounds, reals, integers, functions, data, " \
|
|
73
|
+
"mask_pointers, mask_strides, error".freeze
|
|
74
|
+
|
|
75
|
+
# Higher binds tighter. Used to parenthesize only where C would
|
|
76
|
+
# otherwise regroup the expression.
|
|
77
|
+
# C's precedence, not Ruby's: the tree comes from Prism, and what this
|
|
78
|
+
# table decides is where the parentheses go on the way out.
|
|
79
|
+
PRECEDENCE = {
|
|
80
|
+
:* => 80, :/ => 80,
|
|
81
|
+
:+ => 70, :- => 70,
|
|
82
|
+
:<< => 65, :>> => 65,
|
|
83
|
+
:< => 60, :<= => 60, :> => 60, :>= => 60,
|
|
84
|
+
:== => 55, :!= => 55,
|
|
85
|
+
:& => 52,
|
|
86
|
+
:^ => 50,
|
|
87
|
+
:| => 48,
|
|
88
|
+
:"&&" => 40,
|
|
89
|
+
:"||" => 35,
|
|
90
|
+
}.freeze
|
|
91
|
+
|
|
92
|
+
LEAF_PRECEDENCE = 100
|
|
93
|
+
UNARY_PRECEDENCE = 90
|
|
94
|
+
|
|
95
|
+
# cmplx64's helpers are the same helpers in the narrower width, so they
|
|
96
|
+
# are written once and spelled twice rather than kept in step by hand.
|
|
97
|
+
# The substitution is textual and the order matters -- `double _Complex`
|
|
98
|
+
# before `double` -- which is why it is a list and not a hash.
|
|
99
|
+
NARROW_COMPLEX_SPELLING = [
|
|
100
|
+
["double _Complex", "float _Complex"],
|
|
101
|
+
["carray_jit_", "carray_jit_f_"],
|
|
102
|
+
["creal", "crealf"], ["cimag", "cimagf"],
|
|
103
|
+
["CMPLX(", "CMPLXF("], ["fabs(", "fabsf("],
|
|
104
|
+
["double", "float"], ["1.0", "1.0f"], ["0.0", "0.0f"],
|
|
105
|
+
].freeze
|
|
106
|
+
|
|
107
|
+
def self.narrowed_complex_helper (text)
|
|
108
|
+
NARROW_COMPLEX_SPELLING.reduce(text) { |source, (wide, narrow)|
|
|
109
|
+
source.gsub(wide, narrow)
|
|
110
|
+
}
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# The complex operations Ruby does not compute the way C's operators
|
|
114
|
+
# would. Only the ones a kernel actually uses are emitted, and the
|
|
115
|
+
# reason each exists is at emit_complex_binary.
|
|
116
|
+
COMPLEX_HELPERS = {
|
|
117
|
+
"complex_add_real" => <<~C,
|
|
118
|
+
/* Ruby leaves the imaginary part of `z + x` exactly as it was,
|
|
119
|
+
rather than adding the real operand's zero to it. */
|
|
120
|
+
static inline double _Complex
|
|
121
|
+
carray_jit_complex_add_real (double _Complex z, double x)
|
|
122
|
+
{
|
|
123
|
+
return CMPLX(creal(z) + x, cimag(z));
|
|
124
|
+
}
|
|
125
|
+
C
|
|
126
|
+
"real_add_complex" => <<~C,
|
|
127
|
+
/* And the same the other way round: `x + z` leaves it alone too. */
|
|
128
|
+
static inline double _Complex
|
|
129
|
+
carray_jit_real_add_complex (double x, double _Complex z)
|
|
130
|
+
{
|
|
131
|
+
return CMPLX(x + creal(z), cimag(z));
|
|
132
|
+
}
|
|
133
|
+
C
|
|
134
|
+
"complex_add_imaginary" => <<~C,
|
|
135
|
+
/* `z + 2i` adds to one part and leaves the other, for the same
|
|
136
|
+
reason: the literal's real part is an exact zero. */
|
|
137
|
+
static inline double _Complex
|
|
138
|
+
carray_jit_complex_add_imaginary (double _Complex z, double y)
|
|
139
|
+
{
|
|
140
|
+
return CMPLX(creal(z), cimag(z) + y);
|
|
141
|
+
}
|
|
142
|
+
C
|
|
143
|
+
"imaginary_add_complex" => <<~C,
|
|
144
|
+
static inline double _Complex
|
|
145
|
+
carray_jit_imaginary_add_complex (double y, double _Complex z)
|
|
146
|
+
{
|
|
147
|
+
return CMPLX(creal(z), y + cimag(z));
|
|
148
|
+
}
|
|
149
|
+
C
|
|
150
|
+
"complex_mul_real" => <<~C,
|
|
151
|
+
/* `z * x` scales each part. `x * z` does not -- Ruby coerces the
|
|
152
|
+
x and multiplies in full -- so it is left to C's operator. */
|
|
153
|
+
static inline double _Complex
|
|
154
|
+
carray_jit_complex_mul_real (double _Complex z, double x)
|
|
155
|
+
{
|
|
156
|
+
return CMPLX(creal(z) * x, cimag(z) * x);
|
|
157
|
+
}
|
|
158
|
+
C
|
|
159
|
+
"complex_div_real" => <<~C,
|
|
160
|
+
static inline double _Complex
|
|
161
|
+
carray_jit_complex_div_real (double _Complex z, double x)
|
|
162
|
+
{
|
|
163
|
+
return CMPLX(creal(z) / x, cimag(z) / x);
|
|
164
|
+
}
|
|
165
|
+
C
|
|
166
|
+
"complex_divide" => <<~C,
|
|
167
|
+
/* Ruby divides one Complex by another with Smith's method, and so
|
|
168
|
+
does this -- in the order complex.c writes it, because the last
|
|
169
|
+
bit of the answer depends on that order and the C library's own
|
|
170
|
+
__divdc3 arrives at a different one. */
|
|
171
|
+
static inline double _Complex
|
|
172
|
+
carray_jit_complex_divide (double _Complex a, double _Complex b)
|
|
173
|
+
{
|
|
174
|
+
double are = creal(a), aim = cimag(a);
|
|
175
|
+
double bre = creal(b), bim = cimag(b);
|
|
176
|
+
double r, n;
|
|
177
|
+
if ( fabs(bre) > fabs(bim) ) {
|
|
178
|
+
r = bim / bre;
|
|
179
|
+
n = bre * (1.0 + r * r);
|
|
180
|
+
return CMPLX((are + aim * r) / n, (aim - are * r) / n);
|
|
181
|
+
}
|
|
182
|
+
r = bre / bim;
|
|
183
|
+
n = bim * (1.0 + r * r);
|
|
184
|
+
return CMPLX((are * r + aim) / n, (aim * r - are) / n);
|
|
185
|
+
}
|
|
186
|
+
C
|
|
187
|
+
"real_divide_complex" => <<~C,
|
|
188
|
+
/* The same method for a real numerator, which Ruby coerces to a
|
|
189
|
+
Complex whose imaginary part is an exact Integer zero. That
|
|
190
|
+
zero multiplies like a float -- `0 * r` carries r's sign -- but
|
|
191
|
+
adds like an exact one, which f_add returns the other operand
|
|
192
|
+
for. So `+ 0.0 * r` appears on one branch and not on the other,
|
|
193
|
+
and the signs of the zeros in the answer depend on it. */
|
|
194
|
+
static inline double _Complex
|
|
195
|
+
carray_jit_real_divide_complex (double a, double _Complex b)
|
|
196
|
+
{
|
|
197
|
+
double bre = creal(b), bim = cimag(b);
|
|
198
|
+
double r, n;
|
|
199
|
+
if ( fabs(bre) > fabs(bim) ) {
|
|
200
|
+
r = bim / bre;
|
|
201
|
+
n = bre * (1.0 + r * r);
|
|
202
|
+
return CMPLX((a + 0.0 * r) / n, (0.0 - a * r) / n);
|
|
203
|
+
}
|
|
204
|
+
r = bre / bim;
|
|
205
|
+
n = bim * (1.0 + r * r);
|
|
206
|
+
return CMPLX(a * r / n, (0.0 * r - a) / n);
|
|
207
|
+
}
|
|
208
|
+
C
|
|
209
|
+
}.freeze
|
|
210
|
+
|
|
211
|
+
# What an accumulator starts from, per computation type. A complex sum
|
|
212
|
+
# starts from a complex zero: CMPLX carries the sign of both zeros,
|
|
213
|
+
# which a plain 0.0 widened to complex would not.
|
|
214
|
+
ZEROES = {
|
|
215
|
+
:int64 => "INT64_C(0)",
|
|
216
|
+
:float => "0.0f",
|
|
217
|
+
:uint64 => "UINT64_C(0)",
|
|
218
|
+
:double => "0.0",
|
|
219
|
+
:complex => "CMPLX(0.0, 0.0)",
|
|
220
|
+
:float_complex => "CMPLXF(0.0f, 0.0f)",
|
|
221
|
+
}.freeze
|
|
222
|
+
|
|
223
|
+
# What a product accumulator starts from, for the same reason.
|
|
224
|
+
ONES = {
|
|
225
|
+
:int64 => "INT64_C(1)",
|
|
226
|
+
:float => "1.0f",
|
|
227
|
+
:uint64 => "UINT64_C(1)",
|
|
228
|
+
:double => "1.0",
|
|
229
|
+
:complex => "CMPLX(1.0, 0.0)",
|
|
230
|
+
:float_complex => "CMPLXF(1.0f, 0.0f)",
|
|
231
|
+
}.freeze
|
|
232
|
+
|
|
233
|
+
# How many partial accumulators a licensed reduction runs. A serial
|
|
234
|
+
# accumulator is one dependent chain of additions and waits out the
|
|
235
|
+
# latency of each; eight of them fill it. Measured over row sums and a
|
|
236
|
+
# matrix multiply, eight is where the gain stops growing.
|
|
237
|
+
#
|
|
238
|
+
# The number is here, and the order it implies is written into the C,
|
|
239
|
+
# rather than left to the compiler's unroller: this gem compiles on the
|
|
240
|
+
# machine it runs on, so a schedule chosen by the compiler would vary
|
|
241
|
+
# with the machine and with nothing in the source saying so.
|
|
242
|
+
PARTIAL_ACCUMULATORS = 8
|
|
243
|
+
|
|
244
|
+
BORDER_RULES = [:zero, :clamp, :wrap].freeze
|
|
245
|
+
|
|
246
|
+
def initialize (analyzer, storage_types, scalar_types, c_functions: {},
|
|
247
|
+
masked: false, reassociate: false,
|
|
248
|
+
steps: nil, origin: nil, block_source: nil, border: nil)
|
|
249
|
+
@masked = masked
|
|
250
|
+
# What a window read gets where it falls off the array. Nil for every
|
|
251
|
+
# kernel but a stencil's, and for a stencil whose border is the frame
|
|
252
|
+
# rather than a rule -- `:mask` and `:skip` are cells the loop never
|
|
253
|
+
# reaches, and a cell not reached needs no C.
|
|
254
|
+
@border = border
|
|
255
|
+
# True while the border body is being emitted, which is the same
|
|
256
|
+
# statements as the interior with the rule woven into the reads.
|
|
257
|
+
@bordering = false
|
|
258
|
+
@reassociate = reassociate
|
|
259
|
+
@steps = steps || Array.new(analyzer.rank, 1)
|
|
260
|
+
@analyzer = analyzer
|
|
261
|
+
@storage_types = storage_types
|
|
262
|
+
@scalar_types = scalar_types
|
|
263
|
+
@arrays = analyzer.arrays_used.sort
|
|
264
|
+
# Arrays no longer share a rank, so each one's strides start where the
|
|
265
|
+
# previous one's left off.
|
|
266
|
+
@stride_offsets = {}
|
|
267
|
+
position = 0
|
|
268
|
+
@arrays.each do |array|
|
|
269
|
+
@stride_offsets[array] = position
|
|
270
|
+
position += analyzer.array_ranks.fetch(array, analyzer.rank)
|
|
271
|
+
end
|
|
272
|
+
@array_ranks = analyzer.array_ranks
|
|
273
|
+
@reals = analyzer.scalar_names.select { |name| scalar_types[name] == :double }.sort
|
|
274
|
+
@integers = analyzer.scalar_names.select { |name| scalar_types[name] == :int64 }.sort
|
|
275
|
+
# A captured Complex rides in the reals buffer as its two parts, so
|
|
276
|
+
# that the kernel signature stays the one shape every kernel has.
|
|
277
|
+
@complexes = analyzer.scalar_names.select { |name| scalar_types[name] == :complex }.sort
|
|
278
|
+
# Three buses and no fourth: a capture whose type is none of these
|
|
279
|
+
# would be packed into nothing and read as whatever the slot held, so
|
|
280
|
+
# it is caught here rather than at the cell it computes wrongly.
|
|
281
|
+
carried = @reals.size + @integers.size + @complexes.size
|
|
282
|
+
unless carried == analyzer.scalar_names.size
|
|
283
|
+
missing = analyzer.scalar_names - @reals - @integers - @complexes
|
|
284
|
+
raise Error,
|
|
285
|
+
"captured #{missing.join(", ")} travel in none of the kernel's " \
|
|
286
|
+
"three scalar buses; a computation type was added without a " \
|
|
287
|
+
"way to hand a value of it to the C"
|
|
288
|
+
end
|
|
289
|
+
# Only the ones the block actually called, in a fixed order, because
|
|
290
|
+
# the caller packs the addresses into `functions` by this order.
|
|
291
|
+
@c_functions = analyzer.c_function_names.sort.to_h { |name| [name, c_functions.fetch(name)] }
|
|
292
|
+
# A function written here is pasted into this kernel and called by its
|
|
293
|
+
# symbol; a borrowed one arrives as an address. Only the second kind
|
|
294
|
+
# takes a slot in `functions`, so it is that hash the caller packs by.
|
|
295
|
+
pasted, addressed = @c_functions.partition { |_, function| function.pasted? }
|
|
296
|
+
@pasted_functions = pasted.to_h
|
|
297
|
+
@address_functions = addressed.to_h
|
|
298
|
+
# Arrays handed to a C function whole, in a fixed order, because the
|
|
299
|
+
# caller packs their addresses into `data` by this order.
|
|
300
|
+
@address_arrays = analyzer.address_arrays.sort
|
|
301
|
+
@address_parameters = analyzer.address_parameters
|
|
302
|
+
@rank = analyzer.rank
|
|
303
|
+
# Every axis some array is read at a computed index. Each needs its
|
|
304
|
+
# extent inside the kernel, to check the index against as it is
|
|
305
|
+
# reached; they ride in after the captured integers.
|
|
306
|
+
@extent_slots = @arrays.flat_map { |array|
|
|
307
|
+
axes = analyzer.dynamic_axes(array)
|
|
308
|
+
# A border rule is applied against the extent, so a windowed array
|
|
309
|
+
# needs every one of its axes here -- the same slots a computed
|
|
310
|
+
# subscript already rides in, asked for by a different question.
|
|
311
|
+
if @border && analyzer.windows.include?(array)
|
|
312
|
+
axes |= (0...array_rank_of(analyzer, array)).to_a
|
|
313
|
+
end
|
|
314
|
+
axes.sort.map { |axis| [array, axis] }
|
|
315
|
+
}
|
|
316
|
+
@uses_index_check = false
|
|
317
|
+
@body_reports = false
|
|
318
|
+
@uses_clamp = false
|
|
319
|
+
@uses_wrap = false
|
|
320
|
+
@uses_real_arg = false
|
|
321
|
+
@complex_helpers = []
|
|
322
|
+
@uses_floor_divide = false
|
|
323
|
+
@uses_floor_modulo = false
|
|
324
|
+
@uses_integer_power = false
|
|
325
|
+
@uses_unsigned_divide = false
|
|
326
|
+
@uses_unsigned_modulo = false
|
|
327
|
+
@uses_unsigned_power = false
|
|
328
|
+
@uses_floor_modulo_float = false
|
|
329
|
+
@contiguous = false
|
|
330
|
+
@in_function = false
|
|
331
|
+
@uses_error_flag = false
|
|
332
|
+
@error_parameter = false
|
|
333
|
+
@masked_flag = nil
|
|
334
|
+
@carried_masks = []
|
|
335
|
+
# What `raise` in the block said, by the code a cell writes into the
|
|
336
|
+
# error slot to say which one it was -- and what the bodies pasted in
|
|
337
|
+
# here can say, since their codes come back through the same slot and
|
|
338
|
+
# this kernel is what answers for them. The codes agree because they
|
|
339
|
+
# are taken from the messages, not counted off.
|
|
340
|
+
@raise_messages = {}
|
|
341
|
+
@pasted_functions.each_value do |function|
|
|
342
|
+
(function.raise_messages || {}).each do |code, message|
|
|
343
|
+
register_raise(code, message)
|
|
344
|
+
end
|
|
345
|
+
end
|
|
346
|
+
@position_temporaries = {}.compare_by_identity
|
|
347
|
+
@origin = origin
|
|
348
|
+
@block_source = block_source
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
attr_reader :arrays, :reals, :integers, :complexes, :masked, :extent_slots,
|
|
352
|
+
:c_functions, :pasted_functions, :address_functions,
|
|
353
|
+
:address_arrays, :address_parameters,
|
|
354
|
+
# The messages `raise` in the block gave, by the code a cell
|
|
355
|
+
# writes into the error slot to say which one it was.
|
|
356
|
+
:raise_messages
|
|
357
|
+
|
|
358
|
+
# Whether the compiled function reports through the flag, so the caller
|
|
359
|
+
# knows whether to look the symbol up.
|
|
360
|
+
def uses_error_flag?
|
|
361
|
+
@uses_error_flag
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
def generate
|
|
365
|
+
strided = build_body(false)
|
|
366
|
+
contiguous = build_body(true)
|
|
367
|
+
# The frame is walked strided, whatever the operands are: it is a
|
|
368
|
+
# handful of boxes cut out of the array, and the innermost of them is
|
|
369
|
+
# not the run of cells the contiguous path is for.
|
|
370
|
+
border = @border ? bordered_body : nil
|
|
371
|
+
preamble +
|
|
372
|
+
"static void\ncarray_jit_strided #{SIGNATURE}\n" + strided + "\n" +
|
|
373
|
+
"static void\ncarray_jit_contiguous #{SIGNATURE}\n" + contiguous + "\n" +
|
|
374
|
+
dispatcher +
|
|
375
|
+
(border ? "\nvoid\n#{BORDER_NAME} #{SIGNATURE}\n" + border : "")
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
# The same tree a third time. `generate` already emits it twice, for
|
|
379
|
+
# the contiguous and strided ways of reaching a cell; the border is the
|
|
380
|
+
# third way, and differs only in what a read that falls off the array
|
|
381
|
+
# gives. Nothing about the loop changes -- the caller says which cells.
|
|
382
|
+
def bordered_body
|
|
383
|
+
@bordering = true
|
|
384
|
+
build_body(false)
|
|
385
|
+
ensure
|
|
386
|
+
@bordering = false
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
# A scalar C function: no buffers, no loop, no dispatcher. The body is
|
|
390
|
+
# the same statements the kernel path emits -- what differs is only what
|
|
391
|
+
# surrounds them, which is why the expression emitter below is shared
|
|
392
|
+
# rather than written twice.
|
|
393
|
+
# `error_parameter` generates the form a kernel pastes: the flag arrives
|
|
394
|
+
# as a trailing `int32_t *` rather than standing in the file, so the body
|
|
395
|
+
# reports into whatever the caller is watching. The standalone object
|
|
396
|
+
# keeps the other form -- it is what `CFunction#call` reads, and what a
|
|
397
|
+
# library handed the address calls without knowing about any of this.
|
|
398
|
+
def generate_function (name, parameter_names, parameter_c_types,
|
|
399
|
+
return_c_type, return_type, error_parameter: false)
|
|
400
|
+
@own_symbol = name
|
|
401
|
+
@in_function = true
|
|
402
|
+
@error_parameter = error_parameter
|
|
403
|
+
@contiguous = true
|
|
404
|
+
@declared_locals = {}
|
|
405
|
+
@temporary_count = 0
|
|
406
|
+
@carried_masks = []
|
|
407
|
+
statements = @analyzer.body.statements
|
|
408
|
+
# A `void` body has no last expression to return: every statement in
|
|
409
|
+
# it is a statement, and what it did is where its pointers pointed.
|
|
410
|
+
@returns_nothing = return_type.nil?
|
|
411
|
+
held = @returns_nothing ? statements : statements[0..-2]
|
|
412
|
+
lines = held.map { |statement| emit_statement(statement, " ") }.join
|
|
413
|
+
value = @returns_nothing ? nil : emit(statements.last, return_type)
|
|
414
|
+
parameters = parameter_names.zip(parameter_c_types)
|
|
415
|
+
.map { |parameter, type| type.declare(parameter) }
|
|
416
|
+
# Passed whether or not the body turned out to use it, so that a
|
|
417
|
+
# recursive call emitted before the first division still passes the
|
|
418
|
+
# same argument list the definition ends up declaring.
|
|
419
|
+
parameters << "int32_t *#{ERROR_FLAG}" if error_parameter
|
|
420
|
+
parameters = ["void"] if parameters.empty?
|
|
421
|
+
flag = @uses_error_flag && !error_parameter ? error_flag_declaration : ""
|
|
422
|
+
# Kept apart from the file it is compiled in: the definition alone is
|
|
423
|
+
# what a kernel pastes into its own translation unit, where the
|
|
424
|
+
# includes are already written and the helpers are shared.
|
|
425
|
+
@function_definition =
|
|
426
|
+
"#{return_c_type}\n#{name} (#{parameters.join(', ')})\n{\n" +
|
|
427
|
+
lines + (@returns_nothing ? "}\n" : " return #{value};\n}\n")
|
|
428
|
+
preamble + flag + @function_definition
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
attr_reader :function_definition
|
|
432
|
+
|
|
433
|
+
# What the body took from the preamble, so that a definition pasted
|
|
434
|
+
# somewhere else can be given the same helpers. Only the ones a pasted
|
|
435
|
+
# function can want are here: the rest -- the index check, the two
|
|
436
|
+
# flooring helpers -- report through the error flag, and a function that
|
|
437
|
+
# touches the flag is not pasted at all.
|
|
438
|
+
def helper_needs
|
|
439
|
+
{ :integer_power => @uses_integer_power,
|
|
440
|
+
:unsigned_divide => @uses_unsigned_divide,
|
|
441
|
+
:unsigned_modulo => @uses_unsigned_modulo,
|
|
442
|
+
:unsigned_power => @uses_unsigned_power,
|
|
443
|
+
:floor_modulo_float => @uses_floor_modulo_float,
|
|
444
|
+
:real_arg => @uses_real_arg,
|
|
445
|
+
:complex => @complex_helpers.dup,
|
|
446
|
+
:index_check => @uses_index_check,
|
|
447
|
+
:floor_divide => @uses_floor_divide,
|
|
448
|
+
:floor_modulo => @uses_floor_modulo }
|
|
449
|
+
end
|
|
450
|
+
|
|
451
|
+
# One per compiled object, so two functions never share it, and zeroed
|
|
452
|
+
# by whoever is about to look -- the same discipline the kernel's own
|
|
453
|
+
# error slot keeps. It is written only where the value returned is
|
|
454
|
+
# already a stand-in, so a caller that never looks is no worse off than
|
|
455
|
+
# C leaves it.
|
|
456
|
+
def error_flag_declaration
|
|
457
|
+
"/* Standing at 1 when a division had no divisor, and at the code of\n" \
|
|
458
|
+
" a `raise` in the body where one was reached -- the numbers a\n" \
|
|
459
|
+
" kernel reports through its own slot, with the same meanings. A\n" \
|
|
460
|
+
" subscript on a pointer parameter is not checked here and does not\n" \
|
|
461
|
+
" appear: it is the caller's business, as it is in C. */\n" \
|
|
462
|
+
"int32_t #{ERROR_FLAG} = 0;\n\n"
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
# The same kernel, wrapped so carray can drive it.
|
|
466
|
+
#
|
|
467
|
+
# `ca_call_cslab_N` hands a callback one chunk at a time -- base and
|
|
468
|
+
# stride per operand and a cell count -- which is what this kernel's
|
|
469
|
+
# first three arguments already are, with the bounds of a flat sweep.
|
|
470
|
+
# So the wrapper is a call, not a second code generator: the loop, the
|
|
471
|
+
# arithmetic and the contiguous/strided split are the ones above.
|
|
472
|
+
#
|
|
473
|
+
# The mask is carray's here. It ORs the inputs' masks, propagates the
|
|
474
|
+
# result to the output, and hands the chunk's slice over -- which this
|
|
475
|
+
# ignores, computing every cell as the kernel does anyway, because the
|
|
476
|
+
# bytes under a mask are out of contract and a branchless loop is the
|
|
477
|
+
# point. What carray cannot express is a body that *asks* about a
|
|
478
|
+
# mask, so those do not come this way.
|
|
479
|
+
SLAB_SIGNATURE =
|
|
480
|
+
"(char **base, int64_t *stride, int64_t n, " \
|
|
481
|
+
"const uint8_t *m0, void *userdata)".freeze
|
|
482
|
+
|
|
483
|
+
def generate_slab (name)
|
|
484
|
+
generate + <<~C
|
|
485
|
+
|
|
486
|
+
/* What the sweep cannot carry: the captured values, the C functions
|
|
487
|
+
and the arrays handed to one whole. carray passes one pointer
|
|
488
|
+
through untouched, so they travel behind it. */
|
|
489
|
+
struct carray_jit_slab_context {
|
|
490
|
+
double *reals;
|
|
491
|
+
int64_t *integers;
|
|
492
|
+
void **functions;
|
|
493
|
+
void **data;
|
|
494
|
+
char **mask_pointers;
|
|
495
|
+
int64_t *mask_strides;
|
|
496
|
+
int32_t *error;
|
|
497
|
+
};
|
|
498
|
+
|
|
499
|
+
void
|
|
500
|
+
#{name} #{SLAB_SIGNATURE}
|
|
501
|
+
{
|
|
502
|
+
const struct carray_jit_slab_context *const context = userdata;
|
|
503
|
+
int64_t bounds[3];
|
|
504
|
+
(void) m0;
|
|
505
|
+
bounds[0] = 0;
|
|
506
|
+
bounds[1] = n;
|
|
507
|
+
bounds[2] = 1;
|
|
508
|
+
#{FUNCTION_NAME}(base, stride, bounds,
|
|
509
|
+
context->reals, context->integers,
|
|
510
|
+
context->functions, context->data,
|
|
511
|
+
context->mask_pointers, context->mask_strides,
|
|
512
|
+
context->error);
|
|
513
|
+
}
|
|
514
|
+
C
|
|
515
|
+
end
|
|
516
|
+
|
|
517
|
+
# Where this came from. A generated file that says only what it does is
|
|
518
|
+
# hard to place months later; the block it was written from is short, so
|
|
519
|
+
# it goes in whole, above the C it turned into.
|
|
520
|
+
def provenance
|
|
521
|
+
return "" unless @origin || @block_source
|
|
522
|
+
text = +"/*\n * Generated by carray-jit #{VERSION}.\n"
|
|
523
|
+
text << " *\n * #{@origin}\n" if @origin
|
|
524
|
+
if @block_source
|
|
525
|
+
text << " *\n"
|
|
526
|
+
@block_source.lines.each { |line| text << " * #{line.rstrip}\n" }
|
|
527
|
+
end
|
|
528
|
+
text << " */\n\n"
|
|
529
|
+
text
|
|
530
|
+
end
|
|
531
|
+
|
|
532
|
+
private
|
|
533
|
+
|
|
534
|
+
def preamble
|
|
535
|
+
# A pasted body wants the same helpers here that it had in its own
|
|
536
|
+
# file, and it is emitted below them, so this is asked before any of
|
|
537
|
+
# them is written out.
|
|
538
|
+
@pasted_functions.each_value do |function|
|
|
539
|
+
needs = function.helpers || {}
|
|
540
|
+
@uses_integer_power ||= needs[:integer_power]
|
|
541
|
+
@uses_unsigned_divide ||= needs[:unsigned_divide]
|
|
542
|
+
@uses_unsigned_modulo ||= needs[:unsigned_modulo]
|
|
543
|
+
@uses_unsigned_power ||= needs[:unsigned_power]
|
|
544
|
+
@uses_floor_modulo_float ||= needs[:floor_modulo_float]
|
|
545
|
+
@uses_real_arg ||= needs[:real_arg]
|
|
546
|
+
@complex_helpers |= needs[:complex] || []
|
|
547
|
+
@uses_index_check ||= needs[:index_check]
|
|
548
|
+
@uses_floor_divide ||= needs[:floor_divide]
|
|
549
|
+
@uses_floor_modulo ||= needs[:floor_modulo]
|
|
550
|
+
end
|
|
551
|
+
text = +"#include <stdint.h>\n#include <math.h>\n#include <complex.h>\n" \
|
|
552
|
+
"#include <stdio.h>\n\n"
|
|
553
|
+
unless @address_functions.empty?
|
|
554
|
+
text << "/* The C functions the block called. They arrive as\n" \
|
|
555
|
+
" addresses rather than by linkage, so nothing here says\n" \
|
|
556
|
+
" which library they came from. */\n"
|
|
557
|
+
@address_functions.each_key do |name|
|
|
558
|
+
text << @address_functions.fetch(name).c_declaration(c_function_type_name(name)) << "\n"
|
|
559
|
+
end
|
|
560
|
+
text << "\n"
|
|
561
|
+
end
|
|
562
|
+
if @masked
|
|
563
|
+
text << <<~C
|
|
564
|
+
/* A plain CArray has no mask at all -- one is created only when a
|
|
565
|
+
cell is actually marked -- so an unmasked operand is pointed at
|
|
566
|
+
this single zero byte with a stride of zero. Reading it costs
|
|
567
|
+
a load the compiler hoists, and saves a branch per cell. */
|
|
568
|
+
static const uint8_t carray_jit_present = 0;
|
|
569
|
+
|
|
570
|
+
C
|
|
571
|
+
end
|
|
572
|
+
if @uses_floor_modulo_float
|
|
573
|
+
text << <<~C
|
|
574
|
+
/* The float twin of carray_jit_floor_modulo_real. Going through
|
|
575
|
+
the double one and rounding back would not give the same answer:
|
|
576
|
+
a remainder is the tail of a subtraction, so it is exactly where
|
|
577
|
+
computing wide and narrowing afterwards stops agreeing with
|
|
578
|
+
computing narrow. */
|
|
579
|
+
static inline float
|
|
580
|
+
carray_jit_floor_modulo_float (float numerator, float denominator)
|
|
581
|
+
{
|
|
582
|
+
float remainder = fmodf(numerator, denominator);
|
|
583
|
+
if ( remainder != 0 ) {
|
|
584
|
+
if ( (remainder < 0) != (denominator < 0) ) remainder += denominator;
|
|
585
|
+
}
|
|
586
|
+
else {
|
|
587
|
+
remainder = copysignf(0.0f, denominator);
|
|
588
|
+
}
|
|
589
|
+
return remainder;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
C
|
|
593
|
+
end
|
|
594
|
+
if @uses_unsigned_power
|
|
595
|
+
text << <<~C
|
|
596
|
+
/* The unsigned twin of carray_jit_integer_power. Squaring in
|
|
597
|
+
uint64_t keeps the wrap CArray's own uint64 operators have,
|
|
598
|
+
where int64_t would take the value through a signed type it
|
|
599
|
+
may not fit. */
|
|
600
|
+
static inline uint64_t
|
|
601
|
+
carray_jit_unsigned_power (uint64_t base, uint64_t exponent)
|
|
602
|
+
{
|
|
603
|
+
uint64_t result = 1;
|
|
604
|
+
while ( exponent ) {
|
|
605
|
+
if ( exponent & 1 ) result *= base;
|
|
606
|
+
base *= base;
|
|
607
|
+
exponent >>= 1;
|
|
608
|
+
}
|
|
609
|
+
return result;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
C
|
|
613
|
+
end
|
|
614
|
+
if @uses_unsigned_divide
|
|
615
|
+
text << <<~C
|
|
616
|
+
/* An unsigned operand cannot be negative, so C's truncation
|
|
617
|
+
already floors and there is no quotient to correct -- what is
|
|
618
|
+
left of carray_jit_floor_divide is the zero divisor, which is
|
|
619
|
+
reported through *error the same way. */
|
|
620
|
+
static inline uint64_t
|
|
621
|
+
carray_jit_unsigned_divide (uint64_t numerator, uint64_t denominator, int32_t *error)
|
|
622
|
+
{
|
|
623
|
+
if ( denominator == 0 ) {
|
|
624
|
+
if ( error ) *error = 1;
|
|
625
|
+
return 0;
|
|
626
|
+
}
|
|
627
|
+
return numerator / denominator;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
C
|
|
631
|
+
end
|
|
632
|
+
if @uses_unsigned_modulo
|
|
633
|
+
text << <<~C
|
|
634
|
+
/* Likewise the remainder: with no sign to disagree about, Ruby's
|
|
635
|
+
floored `%` and C's are the same operation. */
|
|
636
|
+
static inline uint64_t
|
|
637
|
+
carray_jit_unsigned_modulo (uint64_t numerator, uint64_t denominator, int32_t *error)
|
|
638
|
+
{
|
|
639
|
+
if ( denominator == 0 ) {
|
|
640
|
+
if ( error ) *error = 1;
|
|
641
|
+
return 0;
|
|
642
|
+
}
|
|
643
|
+
return numerator % denominator;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
C
|
|
647
|
+
end
|
|
648
|
+
if @uses_integer_power
|
|
649
|
+
text << <<~C
|
|
650
|
+
/* Exponentiation by squaring, so that an integer power is the
|
|
651
|
+
exact integer Ruby would give rather than pow's double. The
|
|
652
|
+
exponent is known non-negative: a negative one gives a Rational
|
|
653
|
+
in Ruby, and is refused. */
|
|
654
|
+
static inline int64_t
|
|
655
|
+
carray_jit_integer_power (int64_t base, int64_t exponent)
|
|
656
|
+
{
|
|
657
|
+
int64_t result = 1;
|
|
658
|
+
while ( exponent > 0 ) {
|
|
659
|
+
if ( exponent & 1 ) result *= base;
|
|
660
|
+
base *= base;
|
|
661
|
+
exponent >>= 1;
|
|
662
|
+
}
|
|
663
|
+
return result;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
C
|
|
667
|
+
end
|
|
668
|
+
COMPLEX_HELPERS.each do |name, definition|
|
|
669
|
+
[false, true].each do |narrow|
|
|
670
|
+
next unless @complex_helpers.include?([name, narrow])
|
|
671
|
+
text << (narrow ? self.class.narrowed_complex_helper(definition)
|
|
672
|
+
: definition) << "\n"
|
|
673
|
+
end
|
|
674
|
+
end
|
|
675
|
+
if @uses_real_arg
|
|
676
|
+
text << <<~C
|
|
677
|
+
/* The argument of a real number, which Ruby reads off the sign
|
|
678
|
+
bit rather than from a comparison: `-0.0.arg` is pi, where
|
|
679
|
+
`-0.0 < 0` is false. A NaN is its own argument. */
|
|
680
|
+
static inline double
|
|
681
|
+
carray_jit_real_arg (double x)
|
|
682
|
+
{
|
|
683
|
+
if ( isnan(x) ) return x;
|
|
684
|
+
return signbit(x) ? M_PI : 0.0;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
C
|
|
688
|
+
end
|
|
689
|
+
if @uses_clamp
|
|
690
|
+
text << <<~C
|
|
691
|
+
/* A window that falls off the array reads the nearest cell it
|
|
692
|
+
has: `border: :clamp`. Written as two comparisons rather
|
|
693
|
+
than min/max of a difference, because the extent is what
|
|
694
|
+
decides and it is right there. */
|
|
695
|
+
static inline int64_t
|
|
696
|
+
carray_jit_clamp (int64_t position, int64_t extent)
|
|
697
|
+
{
|
|
698
|
+
if ( position < 0 ) return 0;
|
|
699
|
+
if ( position >= extent ) return extent - 1;
|
|
700
|
+
return position;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
C
|
|
704
|
+
end
|
|
705
|
+
if @uses_wrap
|
|
706
|
+
text << <<~C
|
|
707
|
+
/* A window that falls off the array comes back on the other
|
|
708
|
+
side: `border: :wrap`. C's `%` truncates toward zero, so a
|
|
709
|
+
negative position needs the extent added back -- the same
|
|
710
|
+
correction the flooring helpers make, for the same reason. */
|
|
711
|
+
static inline int64_t
|
|
712
|
+
carray_jit_wrap (int64_t position, int64_t extent)
|
|
713
|
+
{
|
|
714
|
+
int64_t folded = position % extent;
|
|
715
|
+
return folded < 0 ? folded + extent : folded;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
C
|
|
719
|
+
end
|
|
720
|
+
if @uses_index_check
|
|
721
|
+
text << <<~C
|
|
722
|
+
/* A subscript the kernel computes is checked as it is reached --
|
|
723
|
+
the one thing about a kernel that cannot be settled before it
|
|
724
|
+
runs. Out of range it reports through *error, which the Ruby
|
|
725
|
+
side turns into the IndexError CArray would have raised, and
|
|
726
|
+
reads cell 0 so that nothing is read outside the array in the
|
|
727
|
+
meantime. */
|
|
728
|
+
static inline int64_t
|
|
729
|
+
carray_jit_index (int64_t position, int64_t extent, int32_t *error)
|
|
730
|
+
{
|
|
731
|
+
if ( position < 0 || position >= extent ) {
|
|
732
|
+
if ( error ) *error = 2;
|
|
733
|
+
return 0;
|
|
734
|
+
}
|
|
735
|
+
return position;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
C
|
|
739
|
+
end
|
|
740
|
+
if @uses_floor_modulo
|
|
741
|
+
text << <<~C
|
|
742
|
+
/* Ruby's `%` floors with its division: the remainder carries the
|
|
743
|
+
sign of the divisor, where C's `%` and fmod carry the sign of
|
|
744
|
+
the dividend. So add the divisor back when the remainder is
|
|
745
|
+
non-zero and disagrees with it in sign -- and, for floats,
|
|
746
|
+
give a zero remainder the divisor's sign, so the rule holds
|
|
747
|
+
without exception. This mirrors CArray's own `:mod` kernel in
|
|
748
|
+
`ext/mkkernel.rb`. */
|
|
749
|
+
static inline int64_t
|
|
750
|
+
carray_jit_floor_modulo (int64_t numerator, int64_t denominator, int32_t *error)
|
|
751
|
+
{
|
|
752
|
+
if ( denominator == 0 ) {
|
|
753
|
+
if ( error ) *error = 1;
|
|
754
|
+
return 0;
|
|
755
|
+
}
|
|
756
|
+
int64_t remainder = numerator % denominator;
|
|
757
|
+
if ( remainder != 0 && ((remainder < 0) != (denominator < 0)) ) {
|
|
758
|
+
remainder += denominator;
|
|
759
|
+
}
|
|
760
|
+
return remainder;
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
static inline double
|
|
764
|
+
carray_jit_floor_modulo_real (double numerator, double denominator)
|
|
765
|
+
{
|
|
766
|
+
double remainder = fmod(numerator, denominator);
|
|
767
|
+
if ( remainder != 0 ) {
|
|
768
|
+
if ( (remainder < 0) != (denominator < 0) ) remainder += denominator;
|
|
769
|
+
}
|
|
770
|
+
else {
|
|
771
|
+
remainder = copysign(0.0, denominator);
|
|
772
|
+
}
|
|
773
|
+
return remainder;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
C
|
|
777
|
+
end
|
|
778
|
+
if @uses_floor_divide
|
|
779
|
+
text << <<~C
|
|
780
|
+
/* Ruby and CArray floor integer division and give the remainder the
|
|
781
|
+
sign of the divisor; C truncates toward zero. Correct the C
|
|
782
|
+
quotient by one when the division is inexact and the operands
|
|
783
|
+
disagree in sign. Division by zero cannot raise from here, so it
|
|
784
|
+
is reported through *error and checked on the Ruby side. */
|
|
785
|
+
static inline int64_t
|
|
786
|
+
carray_jit_floor_divide (int64_t numerator, int64_t denominator, int32_t *error)
|
|
787
|
+
{
|
|
788
|
+
if ( denominator == 0 ) {
|
|
789
|
+
/* error is null when the cell being written is masked: the
|
|
790
|
+
value there is out of contract, so a zero that only ever
|
|
791
|
+
feeds a masked cell is not a division by zero the caller
|
|
792
|
+
asked about. */
|
|
793
|
+
if ( error ) *error = 1;
|
|
794
|
+
return 0;
|
|
795
|
+
}
|
|
796
|
+
int64_t quotient = numerator / denominator;
|
|
797
|
+
if ( numerator % denominator != 0 && ((numerator < 0) != (denominator < 0)) ) {
|
|
798
|
+
quotient -= 1;
|
|
799
|
+
}
|
|
800
|
+
return quotient;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
C
|
|
804
|
+
end
|
|
805
|
+
text << pasted_definitions
|
|
806
|
+
end
|
|
807
|
+
|
|
808
|
+
# The bodies of the functions written with `jit_function`, put in this
|
|
809
|
+
# translation unit as statics and called by name. An address the
|
|
810
|
+
# compiler cannot see through is a call it cannot inline, and the loop
|
|
811
|
+
# around it is one it will not vectorise -- so a function the block
|
|
812
|
+
# named is compiled into the kernel as if the expression had been
|
|
813
|
+
# written out where it is called.
|
|
814
|
+
#
|
|
815
|
+
# `static` because the symbol belongs to this kernel: the same body may
|
|
816
|
+
# already stand in its own object, and in another kernel beside this
|
|
817
|
+
# one. The name carries the digest of the body, so two of them are the
|
|
818
|
+
# same function, and one is pasted once however many names the block
|
|
819
|
+
# reached it by.
|
|
820
|
+
def pasted_definitions
|
|
821
|
+
seen = {}
|
|
822
|
+
text = +""
|
|
823
|
+
@pasted_functions.each_value do |function|
|
|
824
|
+
next if seen[function.name]
|
|
825
|
+
seen[function.name] = true
|
|
826
|
+
text << "/* #{function} */\n" \
|
|
827
|
+
"static #{function.definition}\n"
|
|
828
|
+
end
|
|
829
|
+
text
|
|
830
|
+
end
|
|
831
|
+
|
|
832
|
+
# One compiled object holds both loops and picks between them once,
|
|
833
|
+
# outside the loop. The contiguous form indexes a typed pointer along
|
|
834
|
+
# the innermost axis, which the compiler can vectorise; the strided form
|
|
835
|
+
# cannot be, and is what lets a view run without being copied first.
|
|
836
|
+
def dispatcher
|
|
837
|
+
tests = @arrays.map { |array|
|
|
838
|
+
"strides[#{@stride_offsets.fetch(array) + array_rank(array) - 1}] == " \
|
|
839
|
+
"(int64_t) sizeof(#{storage_c_type(array)})"
|
|
840
|
+
}
|
|
841
|
+
# A kernel reaching no array cell has nothing to be contiguous about
|
|
842
|
+
# -- it can only be one whose work is a call, the arrays it touches
|
|
843
|
+
# arriving whole as addresses. There is one loop then, and asking
|
|
844
|
+
# which to take would be an `if` with nothing in it.
|
|
845
|
+
if tests.empty?
|
|
846
|
+
return <<~C
|
|
847
|
+
void
|
|
848
|
+
#{FUNCTION_NAME} #{SIGNATURE}
|
|
849
|
+
{
|
|
850
|
+
carray_jit_contiguous(#{ARGUMENTS});
|
|
851
|
+
}
|
|
852
|
+
C
|
|
853
|
+
end
|
|
854
|
+
<<~C
|
|
855
|
+
void
|
|
856
|
+
#{FUNCTION_NAME} #{SIGNATURE}
|
|
857
|
+
{
|
|
858
|
+
if ( #{tests.join("\n && ")} ) {
|
|
859
|
+
carray_jit_contiguous(#{ARGUMENTS});
|
|
860
|
+
} else {
|
|
861
|
+
carray_jit_strided(#{ARGUMENTS});
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
C
|
|
865
|
+
end
|
|
866
|
+
|
|
867
|
+
def build_body (contiguous)
|
|
868
|
+
@contiguous = contiguous
|
|
869
|
+
@declared_locals = {}
|
|
870
|
+
@temporary_count = 0
|
|
871
|
+
@carried_masks = []
|
|
872
|
+
# Whether this body has a way to report -- asked of the statements
|
|
873
|
+
# themselves rather than of what rides in beside them. A windowed
|
|
874
|
+
# kernel carries every extent of its arrays so the border rule has
|
|
875
|
+
# them, which says nothing about whether a cell can fail.
|
|
876
|
+
@body_reports = false
|
|
877
|
+
statements = @analyzer.body.statements.map { |statement|
|
|
878
|
+
emit_statement(statement, " " + " " * @rank)
|
|
879
|
+
}.join
|
|
880
|
+
"{\n" + declarations + loop_open + statements + loop_close + "}\n"
|
|
881
|
+
end
|
|
882
|
+
|
|
883
|
+
def array_rank (array)
|
|
884
|
+
@array_ranks.fetch(array, @rank)
|
|
885
|
+
end
|
|
886
|
+
|
|
887
|
+
def array_rank_of (analyzer, array)
|
|
888
|
+
analyzer.array_ranks.fetch(array, analyzer.rank)
|
|
889
|
+
end
|
|
890
|
+
|
|
891
|
+
def declarations
|
|
892
|
+
lines = []
|
|
893
|
+
@arrays.each_with_index do |array, index|
|
|
894
|
+
base = @stride_offsets.fetch(array)
|
|
895
|
+
lines << " char *const #{pointer_name(array)} = pointers[#{index}];\n"
|
|
896
|
+
array_rank(array).times do |axis|
|
|
897
|
+
lines << " const int64_t #{stride_name(array, axis)} = " \
|
|
898
|
+
"strides[#{base + axis}];\n"
|
|
899
|
+
end
|
|
900
|
+
next unless @masked
|
|
901
|
+
given = "mask_pointers[#{index}]"
|
|
902
|
+
if @analyzer.written_arrays.include?(array)
|
|
903
|
+
# An array the kernel writes always has a mask by the time it gets
|
|
904
|
+
# here: the caller creates one when any operand carries one.
|
|
905
|
+
lines << " uint8_t *const #{mask_pointer_name(array)} = " \
|
|
906
|
+
"(uint8_t *) #{given};\n"
|
|
907
|
+
array_rank(array).times do |axis|
|
|
908
|
+
lines << " const int64_t #{mask_stride_name(array, axis)} = " \
|
|
909
|
+
"mask_strides[#{base + axis}];\n"
|
|
910
|
+
end
|
|
911
|
+
else
|
|
912
|
+
lines << " const uint8_t *const #{mask_pointer_name(array)} = " \
|
|
913
|
+
"#{given} ? (const uint8_t *) #{given} : &carray_jit_present;\n"
|
|
914
|
+
array_rank(array).times do |axis|
|
|
915
|
+
lines << " const int64_t #{mask_stride_name(array, axis)} = " \
|
|
916
|
+
"#{given} ? mask_strides[#{base + axis}] : 0;\n"
|
|
917
|
+
end
|
|
918
|
+
end
|
|
919
|
+
end
|
|
920
|
+
@reals.each_with_index do |name, index|
|
|
921
|
+
lines << " const double #{c_name(name)} = reals[#{index}];\n"
|
|
922
|
+
end
|
|
923
|
+
@complexes.each_with_index do |name, index|
|
|
924
|
+
slot = @reals.size + 2 * index
|
|
925
|
+
lines << " const double _Complex #{c_name(name)} = " \
|
|
926
|
+
"CMPLX(reals[#{slot}], reals[#{slot + 1}]);\n"
|
|
927
|
+
end
|
|
928
|
+
@integers.each_with_index do |name, index|
|
|
929
|
+
lines << " const int64_t #{c_name(name)} = integers[#{index}];\n"
|
|
930
|
+
end
|
|
931
|
+
@extent_slots.each_with_index do |(array, axis), slot|
|
|
932
|
+
lines << " const int64_t #{extent_name(array, axis)} = " \
|
|
933
|
+
"integers[#{@integers.size + slot}];\n"
|
|
934
|
+
end
|
|
935
|
+
@address_functions.each_key.with_index do |name, index|
|
|
936
|
+
lines << " const #{c_function_type_name(name)} #{c_name(name)} = " \
|
|
937
|
+
"(#{c_function_type_name(name)}) functions[#{index}];\n"
|
|
938
|
+
end
|
|
939
|
+
@address_arrays.each_with_index do |array, index|
|
|
940
|
+
# Typed by the array rather than by the parameter it will be passed
|
|
941
|
+
# to: the caller has already checked that the two agree, and the
|
|
942
|
+
# array is the one that owns the memory.
|
|
943
|
+
lines << " #{storage_c_type(array)} *const #{c_name(array)} = " \
|
|
944
|
+
"(#{storage_c_type(array)} *) data[#{index}];\n"
|
|
945
|
+
end
|
|
946
|
+
lines.empty? ? "" : lines.join + "\n"
|
|
947
|
+
end
|
|
948
|
+
|
|
949
|
+
# Which way each axis runs is derived, not chosen: reading a cell the
|
|
950
|
+
# kernel will later write means that cell has to be reached in one
|
|
951
|
+
# particular order, and any other order would read what was never
|
|
952
|
+
# written.
|
|
953
|
+
# Each axis carries a start, a limit and a step. The step is a compiled
|
|
954
|
+
# constant when it is one, because `i++` is what lets the loop vectorise
|
|
955
|
+
# and that is the case nearly every kernel is in.
|
|
956
|
+
def loop_open
|
|
957
|
+
@rank.times.map { |axis|
|
|
958
|
+
indent = " " + " " * axis
|
|
959
|
+
index = @analyzer.index_names[axis]
|
|
960
|
+
# A kernel that can fail at a cell -- a computed index off the
|
|
961
|
+
# end, a division with no divisor, a `raise` -- stops there, as the
|
|
962
|
+
# Ruby loop it stands for does. So each loop leaves as soon as one
|
|
963
|
+
# has: the flag is already in cache, and a kernel whose body has no
|
|
964
|
+
# way to report does not pay for the test at all.
|
|
965
|
+
guard = @body_reports ? "#{indent} if ( *error ) break;\n" : ""
|
|
966
|
+
start = "bounds[#{3 * axis}]"
|
|
967
|
+
limit = "bounds[#{3 * axis + 1}]"
|
|
968
|
+
step = @steps[axis]
|
|
969
|
+
if step && step.negative?
|
|
970
|
+
advance = step == -1 ? "#{index}--" : "#{index} += bounds[#{3 * axis + 2}]"
|
|
971
|
+
"#{indent}for (int64_t #{index} = #{start}; #{index} > #{limit}; " \
|
|
972
|
+
"#{advance}) {\n#{guard}"
|
|
973
|
+
else
|
|
974
|
+
advance = step == 1 ? "#{index}++" : "#{index} += bounds[#{3 * axis + 2}]"
|
|
975
|
+
"#{indent}for (int64_t #{index} = #{start}; #{index} < #{limit}; " \
|
|
976
|
+
"#{advance}) {\n#{guard}"
|
|
977
|
+
end
|
|
978
|
+
}.join
|
|
979
|
+
end
|
|
980
|
+
|
|
981
|
+
def loop_close
|
|
982
|
+
@rank.downto(1).map { |depth| " " + " " * (depth - 1) + "}\n" }.join
|
|
983
|
+
end
|
|
984
|
+
|
|
985
|
+
def emit_statement (statement, indent)
|
|
986
|
+
case statement
|
|
987
|
+
when Assignment then emit_assignment(statement, indent)
|
|
988
|
+
when ElementWrite then guarded(statement, indent) { |inner|
|
|
989
|
+
emit_element_write(statement, inner) }
|
|
990
|
+
when MaskWrite then guarded(statement, indent) { |inner|
|
|
991
|
+
emit_mask_only_write(statement, inner) }
|
|
992
|
+
when PointerWrite then
|
|
993
|
+
"#{indent}#{statement.name}[#{emit(statement.index, :int64)}] = " \
|
|
994
|
+
"#{emit(statement.expression, statement.type)};\n"
|
|
995
|
+
when InnerLoop then emit_inner_loop(statement, indent)
|
|
996
|
+
when While then emit_while(statement, indent)
|
|
997
|
+
when Branch then emit_branch(statement, indent)
|
|
998
|
+
when CallStatement then emit_call_statement(statement, indent)
|
|
999
|
+
when Print then emit_print(statement, indent)
|
|
1000
|
+
when Raise then emit_raise(statement, indent)
|
|
1001
|
+
when LoopSkip then "#{indent}continue;\n"
|
|
1002
|
+
when LoopStop then "#{indent}break;\n"
|
|
1003
|
+
else
|
|
1004
|
+
raise Error, "code generation reached #{statement.class}"
|
|
1005
|
+
end
|
|
1006
|
+
end
|
|
1007
|
+
|
|
1008
|
+
# 1 and 2 are the divisor that was not there and the subscript that ran
|
|
1009
|
+
# off its array. A `raise` in the block takes a code from here up.
|
|
1010
|
+
RAISE_CODE_FLOOR = 3
|
|
1011
|
+
|
|
1012
|
+
# The code is taken from the message rather than counted off as messages
|
|
1013
|
+
# are met. Counting would number the same block differently depending
|
|
1014
|
+
# on what else had been compiled, and a kernel is cached on disk under
|
|
1015
|
+
# the C it generated: a number that means one string today and another
|
|
1016
|
+
# tomorrow would raise the wrong message, quietly, out of a cache hit.
|
|
1017
|
+
# From the message, one string is one code in every process.
|
|
1018
|
+
def raise_code (message)
|
|
1019
|
+
span = 2**31 - RAISE_CODE_FLOOR
|
|
1020
|
+
RAISE_CODE_FLOOR +
|
|
1021
|
+
Digest::SHA256.hexdigest(message)[0, 16].to_i(16) % span
|
|
1022
|
+
end
|
|
1023
|
+
|
|
1024
|
+
# The cell reports and the loop stops; the Ruby side raises when it has
|
|
1025
|
+
# control back. Stopping is what `raise` means -- the cells after this
|
|
1026
|
+
# one are not computed, and neither is the rest of this one.
|
|
1027
|
+
#
|
|
1028
|
+
# Nothing is reported from a cell that is missing. `raise "x" if a < 0`
|
|
1029
|
+
# under a mask was decided by bytes that mean nothing -- the rule an
|
|
1030
|
+
# `if` in a masked kernel already keeps for what it writes -- so the
|
|
1031
|
+
# masks carried in gate the report, and a null error slot gates it
|
|
1032
|
+
# again where the raise stands inside a masked write.
|
|
1033
|
+
#
|
|
1034
|
+
# The first report wins. A sweep hands the kernel one chunk at a time,
|
|
1035
|
+
# so a later chunk still runs after a cell has raised in this one.
|
|
1036
|
+
def emit_raise (node, indent)
|
|
1037
|
+
code = raise_code(node.message)
|
|
1038
|
+
register_raise(code, node.message)
|
|
1039
|
+
slot = error_argument
|
|
1040
|
+
tests = []
|
|
1041
|
+
unless @carried_masks.empty?
|
|
1042
|
+
mask = combine_masks(@carried_masks)
|
|
1043
|
+
tests << "!#{mask}" unless mask == "0"
|
|
1044
|
+
end
|
|
1045
|
+
# A body standing on its own reports into the flag beside it, which is
|
|
1046
|
+
# there to be reported into: there is nothing to ask about, and asking
|
|
1047
|
+
# would read as though there were.
|
|
1048
|
+
if @in_function && !@error_parameter
|
|
1049
|
+
tests << "!#{ERROR_FLAG}"
|
|
1050
|
+
report = "#{ERROR_FLAG} = #{code};"
|
|
1051
|
+
else
|
|
1052
|
+
tests << "#{slot} && !*#{slot}"
|
|
1053
|
+
report = "*#{slot} = #{code};"
|
|
1054
|
+
end
|
|
1055
|
+
# A function returns whatever it returns, whatever happened -- the
|
|
1056
|
+
# arrangement its C caller is left with, and the one the division
|
|
1057
|
+
# helper already keeps by returning zero from a divide it refused.
|
|
1058
|
+
# The value is not the answer; the flag says so.
|
|
1059
|
+
leaving = @in_function && !@returns_nothing ? "return 0;" : "return;"
|
|
1060
|
+
"#{indent}if ( #{tests.join(' && ')} ) {\n" \
|
|
1061
|
+
"#{indent} #{report}\n" \
|
|
1062
|
+
"#{indent} #{leaving}\n" \
|
|
1063
|
+
"#{indent}}\n"
|
|
1064
|
+
end
|
|
1065
|
+
|
|
1066
|
+
# One message per code. Two that collided would be one string raised
|
|
1067
|
+
# where the other was written, so it is worth the check even though the
|
|
1068
|
+
# code is 64 bits of digest folded down and this cannot really happen.
|
|
1069
|
+
def register_raise (code, message)
|
|
1070
|
+
held = @raise_messages[code]
|
|
1071
|
+
if held && held != message
|
|
1072
|
+
raise Error, "two raise messages share a code: " \
|
|
1073
|
+
"#{held.inspect} and #{message.inspect}"
|
|
1074
|
+
end
|
|
1075
|
+
@raise_messages[code] = message
|
|
1076
|
+
end
|
|
1077
|
+
|
|
1078
|
+
# A conversion as Ruby writes it: flags, width, precision, and the
|
|
1079
|
+
# letter that says what is being printed.
|
|
1080
|
+
CONVERSION = /%([-+ 0#]*)(\d*)((?:\.\d+)?)([a-zA-Z%])/
|
|
1081
|
+
|
|
1082
|
+
INTEGER_CONVERSIONS = %w[d i u x X o].freeze
|
|
1083
|
+
REAL_CONVERSIONS = %w[e E f F g G a A].freeze
|
|
1084
|
+
|
|
1085
|
+
# The format is rewritten rather than passed through, because the same
|
|
1086
|
+
# directive does not mean the same thing in both languages: Ruby's `%d`
|
|
1087
|
+
# takes any Integer, and C's takes an `int`, which is not what a cell
|
|
1088
|
+
# holds. What each argument is, is known here, so the directive is
|
|
1089
|
+
# settled here too.
|
|
1090
|
+
# A call whose value is dropped. The cast says the dropping was meant,
|
|
1091
|
+
# which is what a reader of the generated C would want to know and what
|
|
1092
|
+
# a compiler warning would otherwise ask about.
|
|
1093
|
+
#
|
|
1094
|
+
# Under a mask the call does not happen at all. Every other statement
|
|
1095
|
+
# can run on bytes that mean nothing and mark what it wrote; a call
|
|
1096
|
+
# cannot be taken back, so this follows `raise` rather than the
|
|
1097
|
+
# arithmetic: a cell whose arguments are missing is a cell the function
|
|
1098
|
+
# is not told about.
|
|
1099
|
+
def emit_call_statement (statement, indent)
|
|
1100
|
+
text = emit_raw(statement.call).first
|
|
1101
|
+
return "#{indent}(void) #{text};\n" unless @masked
|
|
1102
|
+
mask = combine_masks(@carried_masks + [emit_mask(statement.call)])
|
|
1103
|
+
return "#{indent}(void) #{text};\n" if mask == "0"
|
|
1104
|
+
"#{indent}if ( ! #{mask} ) {\n" \
|
|
1105
|
+
"#{indent} (void) #{text};\n" \
|
|
1106
|
+
"#{indent}}\n"
|
|
1107
|
+
end
|
|
1108
|
+
|
|
1109
|
+
def emit_print (print, indent)
|
|
1110
|
+
arguments = print.arguments.dup
|
|
1111
|
+
text = +""
|
|
1112
|
+
rest = print.template
|
|
1113
|
+
until rest.empty?
|
|
1114
|
+
match = CONVERSION.match(rest)
|
|
1115
|
+
unless match
|
|
1116
|
+
text << c_string(rest)
|
|
1117
|
+
break
|
|
1118
|
+
end
|
|
1119
|
+
text << c_string(match.pre_match)
|
|
1120
|
+
rest = match.post_match
|
|
1121
|
+
if match[4] == "%"
|
|
1122
|
+
text << "%%"
|
|
1123
|
+
next
|
|
1124
|
+
end
|
|
1125
|
+
argument = arguments.shift
|
|
1126
|
+
unless argument
|
|
1127
|
+
raise Unsupported.new(
|
|
1128
|
+
"printf's format asks for more values than were given",
|
|
1129
|
+
print.location)
|
|
1130
|
+
end
|
|
1131
|
+
text << conversion_for(argument, match, print)
|
|
1132
|
+
end
|
|
1133
|
+
unless arguments.empty?
|
|
1134
|
+
raise Unsupported.new(
|
|
1135
|
+
"printf was given more values than its format asks for",
|
|
1136
|
+
print.location)
|
|
1137
|
+
end
|
|
1138
|
+
values = print.arguments.flat_map { |argument| printed_values(argument) }
|
|
1139
|
+
# Flushed at once: C's buffer is not Ruby's, so without this the
|
|
1140
|
+
# kernel's output arrives after everything the program printed
|
|
1141
|
+
# around it, which is no use for seeing where you are.
|
|
1142
|
+
"#{indent}printf(\"#{text}\"#{values.map { |v| ", #{v}" }.join});\n" \
|
|
1143
|
+
"#{indent}fflush(stdout);\n"
|
|
1144
|
+
end
|
|
1145
|
+
|
|
1146
|
+
def conversion_for (argument, match, print)
|
|
1147
|
+
flags, width, precision, letter = match[1], match[2], match[3], match[4]
|
|
1148
|
+
case argument.type
|
|
1149
|
+
when :int64
|
|
1150
|
+
unless INTEGER_CONVERSIONS.include?(letter)
|
|
1151
|
+
raise Unsupported.new(
|
|
1152
|
+
"this value is an integer, so `%#{letter}` does not print it; " \
|
|
1153
|
+
"write `%d`",
|
|
1154
|
+
argument.location || print.location)
|
|
1155
|
+
end
|
|
1156
|
+
# `l`, because a cell holds an int64_t and C's `%d` is an int.
|
|
1157
|
+
"%#{flags}#{width}#{precision}l#{letter}"
|
|
1158
|
+
when :uint64
|
|
1159
|
+
unless INTEGER_CONVERSIONS.include?(letter)
|
|
1160
|
+
raise Unsupported.new(
|
|
1161
|
+
"this value is an integer, so `%#{letter}` does not print it; " \
|
|
1162
|
+
"write `%d`",
|
|
1163
|
+
argument.location || print.location)
|
|
1164
|
+
end
|
|
1165
|
+
# `%d` of a uint64_t above 2**63 would print a negative number, and
|
|
1166
|
+
# the value Ruby prints is the one the cell holds -- so the signed
|
|
1167
|
+
# conversions become their unsigned twin. The rest (`%x`, `%o`) are
|
|
1168
|
+
# unsigned already.
|
|
1169
|
+
"%#{flags}#{width}#{precision}l#{letter == "i" ? "u" : letter.sub("d", "u")}"
|
|
1170
|
+
when :double
|
|
1171
|
+
unless REAL_CONVERSIONS.include?(letter)
|
|
1172
|
+
raise Unsupported.new(
|
|
1173
|
+
"this value is a real, so `%#{letter}` does not print it; " \
|
|
1174
|
+
"write `%g`",
|
|
1175
|
+
argument.location || print.location)
|
|
1176
|
+
end
|
|
1177
|
+
"%#{flags}#{width}#{precision}#{letter}"
|
|
1178
|
+
when :complex
|
|
1179
|
+
# C has no directive for a complex, so it is printed as the two
|
|
1180
|
+
# numbers it is, with the sign of the imaginary part always shown.
|
|
1181
|
+
unless REAL_CONVERSIONS.include?(letter)
|
|
1182
|
+
raise Unsupported.new(
|
|
1183
|
+
"this value is complex, so `%#{letter}` does not print it; " \
|
|
1184
|
+
"write `%g`",
|
|
1185
|
+
argument.location || print.location)
|
|
1186
|
+
end
|
|
1187
|
+
"%#{flags}#{width}#{precision}#{letter}" \
|
|
1188
|
+
"%+#{width}#{precision}#{letter}i"
|
|
1189
|
+
when :boolean
|
|
1190
|
+
"%#{flags}#{width}#{precision}d"
|
|
1191
|
+
else
|
|
1192
|
+
raise Unsupported.new("printf cannot print this value",
|
|
1193
|
+
argument.location || print.location)
|
|
1194
|
+
end
|
|
1195
|
+
end
|
|
1196
|
+
|
|
1197
|
+
def printed_values (argument)
|
|
1198
|
+
case argument.type
|
|
1199
|
+
when :complex
|
|
1200
|
+
["creal(#{emit(argument, :complex)})", "cimag(#{emit(argument, :complex)})"]
|
|
1201
|
+
when :boolean
|
|
1202
|
+
["(int) (#{emit(argument, :boolean)})"]
|
|
1203
|
+
else
|
|
1204
|
+
[emit(argument, argument.type)]
|
|
1205
|
+
end
|
|
1206
|
+
end
|
|
1207
|
+
|
|
1208
|
+
# The literal parts of the format, spelled the way C spells a string.
|
|
1209
|
+
def c_string (text)
|
|
1210
|
+
text.gsub(/[\\"\n\t\r\e\0]/,
|
|
1211
|
+
"\\" => "\\\\", "\"" => "\\\"", "\n" => "\\n",
|
|
1212
|
+
"\t" => "\\t", "\r" => "\\r", "\e" => "\\033",
|
|
1213
|
+
"\0" => "\\0")
|
|
1214
|
+
end
|
|
1215
|
+
|
|
1216
|
+
# Reading outside an array can be made harmless -- read cell zero and
|
|
1217
|
+
# report -- but writing outside it cannot: the report would arrive after
|
|
1218
|
+
# the damage. So a scatter works its positions out first, and writes
|
|
1219
|
+
# only if every one of them is inside.
|
|
1220
|
+
def guarded (write, indent)
|
|
1221
|
+
positions = scattered_positions(write)
|
|
1222
|
+
return yield(indent) if positions.empty?
|
|
1223
|
+
|
|
1224
|
+
lines = "#{indent}{\n"
|
|
1225
|
+
positions.each do |temporary, node, array, axis|
|
|
1226
|
+
lines << "#{indent} const int64_t #{temporary} = #{emit(node, :int64)};\n"
|
|
1227
|
+
@position_temporaries[node] = temporary
|
|
1228
|
+
end
|
|
1229
|
+
test = positions.map { |temporary, _, array, axis|
|
|
1230
|
+
"#{temporary} >= 0 && #{temporary} < #{extent_name(array, axis)}"
|
|
1231
|
+
}.join(" && ")
|
|
1232
|
+
lines << "#{indent} if ( #{test} ) {\n"
|
|
1233
|
+
lines << yield("#{indent} ")
|
|
1234
|
+
lines << "#{indent} } else {\n"
|
|
1235
|
+
lines << "#{indent} if ( #{error_argument} ) *#{error_argument} = 2;\n"
|
|
1236
|
+
lines << "#{indent} }\n"
|
|
1237
|
+
lines << "#{indent}}\n"
|
|
1238
|
+
positions.each { |_, node, _, _| @position_temporaries.delete(node) }
|
|
1239
|
+
lines
|
|
1240
|
+
end
|
|
1241
|
+
|
|
1242
|
+
def scattered_positions (write)
|
|
1243
|
+
subscripts = write_subscripts(write)
|
|
1244
|
+
subscripts.each_with_index.filter_map { |(index, offset), axis|
|
|
1245
|
+
next unless index.nil? && offset.is_a?(Node) &&
|
|
1246
|
+
!Analyzer.fixed_subscript?(offset)
|
|
1247
|
+
[next_temporary("position"), offset, write.array, axis]
|
|
1248
|
+
}
|
|
1249
|
+
end
|
|
1250
|
+
|
|
1251
|
+
# `out[i] = UNDEF` marks the cell missing. The bytes underneath are out
|
|
1252
|
+
# of contract, so there is nothing to store into them.
|
|
1253
|
+
def emit_mask_only_write (write, indent)
|
|
1254
|
+
"#{indent}#{cell_reference(write.array, write_subscripts(write), :mask)} = 1;\n"
|
|
1255
|
+
end
|
|
1256
|
+
|
|
1257
|
+
# The loop a reduction runs in, inside one cell of the outer loops.
|
|
1258
|
+
def inner_loop_guard (indent)
|
|
1259
|
+
@body_reports ? "#{indent} if ( *error ) break;\n" : ""
|
|
1260
|
+
end
|
|
1261
|
+
|
|
1262
|
+
# The same shape the bounded loop gets, minus the counter -- including
|
|
1263
|
+
# the guard, so a `raise` inside a `while` stops it at the pass that
|
|
1264
|
+
# raised rather than running the condition once more.
|
|
1265
|
+
def emit_while (loop_node, indent)
|
|
1266
|
+
# A loop entered on a value read from a missing cell is in the same
|
|
1267
|
+
# position a branch taken on one is: it was decided by bytes that mean
|
|
1268
|
+
# nothing, so what it writes inherits that. The carrying is the
|
|
1269
|
+
# branch's, spelled the same way, because it is the same rule.
|
|
1270
|
+
#
|
|
1271
|
+
# What is not the same is what running on those bytes can cost. A
|
|
1272
|
+
# branch on garbage takes the wrong arm and finishes; a `while` on
|
|
1273
|
+
# garbage can fail to finish at all, since the garbage is what decides
|
|
1274
|
+
# how many passes there are. Skipping the loop instead was the other
|
|
1275
|
+
# candidate and is worse: the locals would keep their pre-loop values
|
|
1276
|
+
# and a write after the loop carries no mask, so a missing cell would
|
|
1277
|
+
# buy a wrong answer quietly rather than a slow one loudly. A kernel
|
|
1278
|
+
# over masked data whose loop bound comes from a cell should say so --
|
|
1279
|
+
# `if a[i] == UNDEF` -- which is the same advice masked arithmetic
|
|
1280
|
+
# already gets.
|
|
1281
|
+
outer = @carried_masks
|
|
1282
|
+
@carried_masks =
|
|
1283
|
+
@masked ? (outer + [emit_mask(loop_node.condition)]).uniq : outer
|
|
1284
|
+
|
|
1285
|
+
text = "#{indent}while (#{emit(loop_node.condition, :boolean)}) {\n" +
|
|
1286
|
+
inner_loop_guard(indent)
|
|
1287
|
+
text += loop_node.statements.map { |statement|
|
|
1288
|
+
emit_statement(statement, indent + " ")
|
|
1289
|
+
}.join
|
|
1290
|
+
@carried_masks = outer
|
|
1291
|
+
text + "#{indent}}\n"
|
|
1292
|
+
end
|
|
1293
|
+
|
|
1294
|
+
def emit_inner_loop (loop_node, indent)
|
|
1295
|
+
accumulation = reduction_accumulation(loop_node)
|
|
1296
|
+
return emit_split_reduction(loop_node, accumulation, indent) if accumulation
|
|
1297
|
+
|
|
1298
|
+
index = loop_node.index
|
|
1299
|
+
text = "#{indent}for (int64_t #{index} = #{emit(loop_node.from, :int64)}; " \
|
|
1300
|
+
"#{index} < #{emit(loop_node.to, :int64)}; #{index}++) {\n" +
|
|
1301
|
+
inner_loop_guard(indent)
|
|
1302
|
+
text += loop_node.statements.map { |statement|
|
|
1303
|
+
emit_statement(statement, indent + " ")
|
|
1304
|
+
}.join
|
|
1305
|
+
text + "#{indent}}\n"
|
|
1306
|
+
end
|
|
1307
|
+
|
|
1308
|
+
# The loop is a reduction when its whole body is one local folding a
|
|
1309
|
+
# term into itself with an associative operator, and that local was
|
|
1310
|
+
# already live when the loop began. Anything else -- a second
|
|
1311
|
+
# statement, a branch, a term that reads the accumulator twice -- is not
|
|
1312
|
+
# a fold and keeps the serial loop.
|
|
1313
|
+
#
|
|
1314
|
+
# Returns [accumulator, operator, term], or nil.
|
|
1315
|
+
def reduction_accumulation (loop_node)
|
|
1316
|
+
return nil unless @reassociate
|
|
1317
|
+
# A masked accumulator carries a mask beside its value, and a partial
|
|
1318
|
+
# sum would need one each. Left out rather than half-done.
|
|
1319
|
+
return nil if @masked
|
|
1320
|
+
return nil unless loop_node.statements.size == 1
|
|
1321
|
+
|
|
1322
|
+
assignment = loop_node.statements.first
|
|
1323
|
+
return nil unless assignment.is_a?(Assignment)
|
|
1324
|
+
name = assignment.binding_name
|
|
1325
|
+
# Declared before this loop, at this type, so the fold continues a
|
|
1326
|
+
# value rather than starting one.
|
|
1327
|
+
return nil unless @declared_locals[name] == assignment.type
|
|
1328
|
+
|
|
1329
|
+
expression = assignment.expression
|
|
1330
|
+
return nil unless expression.is_a?(BinaryOperation)
|
|
1331
|
+
operator = expression.operator
|
|
1332
|
+
return nil unless licensed_fold?(operator, assignment.type)
|
|
1333
|
+
|
|
1334
|
+
# Short of one full round the chains never run, and their setup is
|
|
1335
|
+
# all the loop would pay for them. Where the extent is written out,
|
|
1336
|
+
# that is known here and costs nothing to act on; where it is a value
|
|
1337
|
+
# the kernel is handed, it is not, and a runtime test for it was
|
|
1338
|
+
# measured and bought less than it cost.
|
|
1339
|
+
return nil if literal_extent(loop_node)&.<(PARTIAL_ACCUMULATORS)
|
|
1340
|
+
|
|
1341
|
+
left, right = expression.left, expression.right
|
|
1342
|
+
if accumulator?(left, name) && !mentions_local?(right, name)
|
|
1343
|
+
[assignment, operator]
|
|
1344
|
+
elsif accumulator?(right, name) && !mentions_local?(left, name)
|
|
1345
|
+
[assignment, operator]
|
|
1346
|
+
end
|
|
1347
|
+
end
|
|
1348
|
+
|
|
1349
|
+
def literal_extent (loop_node)
|
|
1350
|
+
from, to = loop_node.from, loop_node.to
|
|
1351
|
+
return nil unless from.is_a?(IntegerLiteral) && to.is_a?(IntegerLiteral)
|
|
1352
|
+
to.value - from.value
|
|
1353
|
+
end
|
|
1354
|
+
|
|
1355
|
+
# Addition is taken in double and in complex; a complex sum is
|
|
1356
|
+
# componentwise in both Ruby and C, so partial sums add the same way.
|
|
1357
|
+
# Multiplication is taken in double only -- Ruby multiplies two
|
|
1358
|
+
# Complexes its own way, and combining partial products would have to
|
|
1359
|
+
# go through that rather than through C's operator.
|
|
1360
|
+
def licensed_fold? (operator, type)
|
|
1361
|
+
case operator
|
|
1362
|
+
when :+ then type == :double || type == :complex
|
|
1363
|
+
when :* then type == :double
|
|
1364
|
+
end
|
|
1365
|
+
end
|
|
1366
|
+
|
|
1367
|
+
def accumulator? (node, name)
|
|
1368
|
+
node.is_a?(LocalRead) && node.binding_name == name
|
|
1369
|
+
end
|
|
1370
|
+
|
|
1371
|
+
def mentions_local? (node, name)
|
|
1372
|
+
return true if accumulator?(node, name)
|
|
1373
|
+
node.children.any? { |child| mentions_local?(child, name) }
|
|
1374
|
+
end
|
|
1375
|
+
|
|
1376
|
+
# The fold, run as PARTIAL_ACCUMULATORS chains instead of one, with the
|
|
1377
|
+
# cells that do not fill a round left to a serial tail.
|
|
1378
|
+
#
|
|
1379
|
+
# The incoming value rides in the first chain and the rest start from
|
|
1380
|
+
# the operator's identity, so it is folded in exactly once. Each chain
|
|
1381
|
+
# keeps the term's own operand order -- what is licensed here is the
|
|
1382
|
+
# order the *iterations* are grouped in, not the order within one.
|
|
1383
|
+
def emit_split_reduction (loop_node, accumulation, indent)
|
|
1384
|
+
assignment, operator = accumulation
|
|
1385
|
+
name = assignment.binding_name
|
|
1386
|
+
type = assignment.type
|
|
1387
|
+
index = loop_node.index
|
|
1388
|
+
base = "#{index}__base"
|
|
1389
|
+
limit = "#{index}__end"
|
|
1390
|
+
lanes = (0...PARTIAL_ACCUMULATORS).map { |lane| "#{name}__p#{lane}" }
|
|
1391
|
+
identity = (operator == :* ? ONES : ZEROES).fetch(type)
|
|
1392
|
+
|
|
1393
|
+
inner = indent + " "
|
|
1394
|
+
text = "#{indent}{\n"
|
|
1395
|
+
text += "#{inner}int64_t #{base} = #{emit(loop_node.from, :int64)};\n"
|
|
1396
|
+
text += "#{inner}const int64_t #{limit} = #{emit(loop_node.to, :int64)};\n"
|
|
1397
|
+
text += "#{inner}#{COMPUTATION_C_TYPES.fetch(type)} #{lanes.first} = #{name}" +
|
|
1398
|
+
lanes.drop(1).map { |lane| ", #{lane} = #{identity}" }.join + ";\n"
|
|
1399
|
+
text += "#{inner}for (; #{base} + #{PARTIAL_ACCUMULATORS} <= #{limit}; " \
|
|
1400
|
+
"#{base} += #{PARTIAL_ACCUMULATORS}) {\n"
|
|
1401
|
+
text += inner_loop_guard(inner)
|
|
1402
|
+
lanes.each_with_index do |lane, offset|
|
|
1403
|
+
term = lane_expression(assignment.expression, name, lane)
|
|
1404
|
+
text += "#{inner} {\n"
|
|
1405
|
+
text += "#{inner} const int64_t #{index} = #{base} + #{offset};\n"
|
|
1406
|
+
text += "#{inner} #{lane} = #{emit(term, type)};\n"
|
|
1407
|
+
text += "#{inner} }\n"
|
|
1408
|
+
end
|
|
1409
|
+
text += "#{inner}}\n"
|
|
1410
|
+
text += "#{inner}#{name} = #{combine_partials(lanes, operator)};\n"
|
|
1411
|
+
text += "#{inner}for (int64_t #{index} = #{base}; #{index} < #{limit}; " \
|
|
1412
|
+
"#{index}++) {\n"
|
|
1413
|
+
text += inner_loop_guard(inner)
|
|
1414
|
+
text += emit_statement(assignment, inner + " ")
|
|
1415
|
+
text += "#{inner}}\n"
|
|
1416
|
+
text + "#{indent}}\n"
|
|
1417
|
+
end
|
|
1418
|
+
|
|
1419
|
+
# The same term, folding into one chain instead of into the
|
|
1420
|
+
# accumulator. The accumulator appears once and at the top, which is
|
|
1421
|
+
# what reduction_accumulation checked, so only that operand moves.
|
|
1422
|
+
def lane_expression (expression, name, lane)
|
|
1423
|
+
operands = [expression.left, expression.right].map { |operand|
|
|
1424
|
+
next operand unless accumulator?(operand, name)
|
|
1425
|
+
read = LocalRead.new(operand.name, operand.location)
|
|
1426
|
+
read.binding_name = lane
|
|
1427
|
+
read.type = operand.type
|
|
1428
|
+
read
|
|
1429
|
+
}
|
|
1430
|
+
term = BinaryOperation.new(expression.operator, *operands,
|
|
1431
|
+
expression.location)
|
|
1432
|
+
term.type = expression.type
|
|
1433
|
+
term
|
|
1434
|
+
end
|
|
1435
|
+
|
|
1436
|
+
# Pairwise, so that the chains meet in a tree rather than in a line.
|
|
1437
|
+
def combine_partials (lanes, operator)
|
|
1438
|
+
terms = lanes
|
|
1439
|
+
while terms.size > 1
|
|
1440
|
+
terms = terms.each_slice(2).map { |left, right|
|
|
1441
|
+
right ? "(#{left} #{operator} #{right})" : left
|
|
1442
|
+
}
|
|
1443
|
+
end
|
|
1444
|
+
terms.first
|
|
1445
|
+
end
|
|
1446
|
+
|
|
1447
|
+
# `if` in statement position. A branch not taken writes nothing, so the
|
|
1448
|
+
# cell keeps both its value and its mask -- which is what the same `if`
|
|
1449
|
+
# would do in Ruby.
|
|
1450
|
+
def emit_branch (branch, indent)
|
|
1451
|
+
# A branch taken on a value read from a missing cell was decided by
|
|
1452
|
+
# bytes that mean nothing, so what it writes inherits that. A branch
|
|
1453
|
+
# taken on `a[i] == UNDEF` was not.
|
|
1454
|
+
outer = @carried_masks
|
|
1455
|
+
@carried_masks = @masked ? (outer + [emit_mask(branch.condition)]).uniq : outer
|
|
1456
|
+
|
|
1457
|
+
text = "#{indent}if ( #{emit(branch.condition, :boolean)} ) {\n"
|
|
1458
|
+
text += branch.consequent.map { |statement|
|
|
1459
|
+
emit_statement(statement, indent + " ")
|
|
1460
|
+
}.join
|
|
1461
|
+
unless branch.alternative.empty?
|
|
1462
|
+
text += "#{indent}} else {\n"
|
|
1463
|
+
text += branch.alternative.map { |statement|
|
|
1464
|
+
emit_statement(statement, indent + " ")
|
|
1465
|
+
}.join
|
|
1466
|
+
end
|
|
1467
|
+
@carried_masks = outer
|
|
1468
|
+
text + "#{indent}}\n"
|
|
1469
|
+
end
|
|
1470
|
+
|
|
1471
|
+
def emit_assignment (assignment, indent)
|
|
1472
|
+
name = assignment.binding_name
|
|
1473
|
+
type = assignment.type
|
|
1474
|
+
declared = @declared_locals.key?(name)
|
|
1475
|
+
@declared_locals[name] = type
|
|
1476
|
+
|
|
1477
|
+
# A local carries a mask alongside its value, so that reading it later
|
|
1478
|
+
# is the same as reading what it was computed from.
|
|
1479
|
+
mask = if @masked
|
|
1480
|
+
"#{indent}#{declared ? '' : 'uint8_t '}" \
|
|
1481
|
+
"#{local_mask_name(name)} = #{emit_mask(assignment.expression)};\n"
|
|
1482
|
+
else
|
|
1483
|
+
""
|
|
1484
|
+
end
|
|
1485
|
+
|
|
1486
|
+
if assignment.expression.is_a?(Conditional)
|
|
1487
|
+
lines = declared ? "" : "#{indent}#{COMPUTATION_C_TYPES.fetch(type)} #{name};\n"
|
|
1488
|
+
lines + emit_conditional_statement(assignment.expression, name, type, indent) + mask
|
|
1489
|
+
else
|
|
1490
|
+
prefix = declared ? "" : "#{COMPUTATION_C_TYPES.fetch(type)} "
|
|
1491
|
+
"#{indent}#{prefix}#{name} = #{emit(assignment.expression, type)};\n" + mask
|
|
1492
|
+
end
|
|
1493
|
+
end
|
|
1494
|
+
|
|
1495
|
+
# Masked data is out of contract -- a kernel may compute anything into a
|
|
1496
|
+
# masked cell -- so the value is computed for every cell and only the
|
|
1497
|
+
# mask is reconciled. That is what lets the loop stay branchless.
|
|
1498
|
+
#
|
|
1499
|
+
# The one thing that cannot simply be computed and discarded is an
|
|
1500
|
+
# integer division by zero, which would be reported even though the
|
|
1501
|
+
# cell it feeds is masked. So the mask is settled first and gates the
|
|
1502
|
+
# report.
|
|
1503
|
+
# Where a write lands. Normally the cell the outer indices are on; a
|
|
1504
|
+
# contraction into a fixed cell says so with a constant subscript.
|
|
1505
|
+
def write_subscripts (write)
|
|
1506
|
+
subscripts = write.subscripts if write.respond_to?(:subscripts)
|
|
1507
|
+
subscripts || @analyzer.index_names.map { |name| [name, 0] }
|
|
1508
|
+
end
|
|
1509
|
+
|
|
1510
|
+
def emit_element_write (write, indent)
|
|
1511
|
+
unless @masked
|
|
1512
|
+
@masked_flag = nil
|
|
1513
|
+
return emit_value_write(write, indent)
|
|
1514
|
+
end
|
|
1515
|
+
|
|
1516
|
+
flag = next_temporary("masked")
|
|
1517
|
+
lines = "#{indent}const uint8_t #{flag} = " \
|
|
1518
|
+
"#{combine_masks(@carried_masks + [emit_mask(write.expression)])};\n"
|
|
1519
|
+
lines += "#{indent}#{cell_reference(write.array, write_subscripts(write), :mask)} = " \
|
|
1520
|
+
"#{flag};\n"
|
|
1521
|
+
@masked_flag = flag
|
|
1522
|
+
lines += emit_value_write(write, indent)
|
|
1523
|
+
@masked_flag = nil
|
|
1524
|
+
lines
|
|
1525
|
+
end
|
|
1526
|
+
|
|
1527
|
+
# The mask of an expression, built the same shape as the expression.
|
|
1528
|
+
#
|
|
1529
|
+
# A flat union of everything the expression touches would be wrong for a
|
|
1530
|
+
# conditional: `cond ? 0.0 : a[i]` does not read `a[i]` when the
|
|
1531
|
+
# condition holds, and Ruby's answer there is not missing. So the
|
|
1532
|
+
# conditional's mask is conditional too.
|
|
1533
|
+
def emit_mask (node)
|
|
1534
|
+
case node
|
|
1535
|
+
when ElementRead
|
|
1536
|
+
cell_reference(node.array, node.subscripts, :mask)
|
|
1537
|
+
when LocalRead
|
|
1538
|
+
local_mask_name(node.binding_name)
|
|
1539
|
+
when Conditional
|
|
1540
|
+
branches = "#{emit(node.condition, :boolean)} ? " \
|
|
1541
|
+
"#{emit_mask(node.consequent)} : #{emit_mask(node.alternative)}"
|
|
1542
|
+
combine_masks([emit_mask(node.condition), "(#{branches})"])
|
|
1543
|
+
when IntegerLiteral, FloatLiteral, IndexVariable, CaptureRead, MaskTest,
|
|
1544
|
+
BoundsValue, ZeroLike
|
|
1545
|
+
"0"
|
|
1546
|
+
else
|
|
1547
|
+
combine_masks(node.children.map { |child| emit_mask(child) })
|
|
1548
|
+
end
|
|
1549
|
+
end
|
|
1550
|
+
|
|
1551
|
+
def combine_masks (parts)
|
|
1552
|
+
present = parts.reject { |part| part == "0" }.uniq
|
|
1553
|
+
return "0" if present.empty?
|
|
1554
|
+
present.size == 1 ? present.first : "(#{present.join(' | ')})"
|
|
1555
|
+
end
|
|
1556
|
+
|
|
1557
|
+
def local_mask_name (name)
|
|
1558
|
+
"#{name}__mask"
|
|
1559
|
+
end
|
|
1560
|
+
|
|
1561
|
+
def emit_value_write (write, indent)
|
|
1562
|
+
target = cell_reference(write.array, write_subscripts(write))
|
|
1563
|
+
expression = write.expression
|
|
1564
|
+
if expression.is_a?(Conditional)
|
|
1565
|
+
temporary = next_temporary
|
|
1566
|
+
type = expression.type
|
|
1567
|
+
"#{indent}#{COMPUTATION_C_TYPES.fetch(type)} #{temporary};\n" +
|
|
1568
|
+
emit_conditional_statement(expression, temporary, type, indent) +
|
|
1569
|
+
"#{indent}#{target} = #{cast_to_storage(temporary, type, write.array)};\n"
|
|
1570
|
+
else
|
|
1571
|
+
value = cast_to_storage(emit(expression, expression.type),
|
|
1572
|
+
expression.type, write.array)
|
|
1573
|
+
"#{indent}#{target} = #{value};\n"
|
|
1574
|
+
end
|
|
1575
|
+
end
|
|
1576
|
+
|
|
1577
|
+
# A conditional in statement position becomes if/else rather than a
|
|
1578
|
+
# ternary; it reads far better when the branches are long.
|
|
1579
|
+
def emit_conditional_statement (node, target, type, indent)
|
|
1580
|
+
"#{indent}if ( #{emit(node.condition, :boolean)} ) {\n" \
|
|
1581
|
+
"#{indent} #{target} = #{emit(node.consequent, type)};\n" \
|
|
1582
|
+
"#{indent}} else {\n" \
|
|
1583
|
+
"#{indent} #{target} = #{emit(node.alternative, type)};\n" \
|
|
1584
|
+
"#{indent}}\n"
|
|
1585
|
+
end
|
|
1586
|
+
|
|
1587
|
+
def storage_c_type (array)
|
|
1588
|
+
STORAGE_C_TYPES.fetch(@storage_types.fetch(array))
|
|
1589
|
+
end
|
|
1590
|
+
|
|
1591
|
+
def cast_to_storage (text, type, array)
|
|
1592
|
+
# Storing into a boolean array normalises: CArray's boolean is a byte
|
|
1593
|
+
# holding 0 or 1, and a kernel is not the place that invariant stops
|
|
1594
|
+
# being true.
|
|
1595
|
+
return "(uint8_t)((#{text}) ? 1 : 0)" if boolean_storage?(array)
|
|
1596
|
+
target = storage_c_type(array)
|
|
1597
|
+
return text if COMPUTATION_C_TYPES.fetch(type) == target
|
|
1598
|
+
"(#{target})(#{text})"
|
|
1599
|
+
end
|
|
1600
|
+
|
|
1601
|
+
# The cast that takes a stored cell to the type the kernel computes in,
|
|
1602
|
+
# or nil where the two already agree.
|
|
1603
|
+
def widening_cast (array, type)
|
|
1604
|
+
return nil if type == :boolean
|
|
1605
|
+
wanted = COMPUTATION_C_TYPES.fetch(type)
|
|
1606
|
+
return nil if storage_c_type(array) == wanted
|
|
1607
|
+
"(#{wanted})"
|
|
1608
|
+
end
|
|
1609
|
+
|
|
1610
|
+
def boolean_storage? (array)
|
|
1611
|
+
@storage_types.fetch(array, nil) == "boolean"
|
|
1612
|
+
end
|
|
1613
|
+
|
|
1614
|
+
# A Ruby name is not always a C one: `Foo::TABLE` names one thing in
|
|
1615
|
+
# Ruby and nothing at all in C, so the qualified part is spelled with
|
|
1616
|
+
# underscores. Every name that reaches the source goes through here.
|
|
1617
|
+
def c_name (name)
|
|
1618
|
+
name.to_s.gsub("::", "__")
|
|
1619
|
+
end
|
|
1620
|
+
|
|
1621
|
+
def pointer_name (array)
|
|
1622
|
+
"p_#{c_name(array)}"
|
|
1623
|
+
end
|
|
1624
|
+
|
|
1625
|
+
# The block's own name for the function, with the typedef beside it.
|
|
1626
|
+
def c_function_type_name (name)
|
|
1627
|
+
"#{c_name(name)}_fn_t"
|
|
1628
|
+
end
|
|
1629
|
+
|
|
1630
|
+
def extent_name (array, axis)
|
|
1631
|
+
"#{c_name(array)}_n#{axis}"
|
|
1632
|
+
end
|
|
1633
|
+
|
|
1634
|
+
# An error reported from a cell whose mask is already set is not one the
|
|
1635
|
+
# caller asked about: the value there is out of contract.
|
|
1636
|
+
# A kernel is handed a place to report through; a compiled function is
|
|
1637
|
+
# not, having only the signature its declaration gave it. So the
|
|
1638
|
+
# object carries one of its own -- a single exported int the helpers
|
|
1639
|
+
# write the same code into -- and `CFunction#call` reads it and raises
|
|
1640
|
+
# what the kernel raises. Nothing in the C reaches Ruby to do it: the
|
|
1641
|
+
# function still returns a number and touches no Ruby value, which is
|
|
1642
|
+
# what lets its address be handed to a library, or called off the GVL.
|
|
1643
|
+
ERROR_FLAG = "carray_jit_error"
|
|
1644
|
+
|
|
1645
|
+
# A computation type this generator has no C for. Nothing reaches here
|
|
1646
|
+
# today -- every type TypeAssignment can assign is named at each of the
|
|
1647
|
+
# sites that calls this -- and that is the point: a type added later
|
|
1648
|
+
# stops here rather than falling into whichever branch happened to be
|
|
1649
|
+
# last, which for `abs`, `%` and `**` alike was the one for doubles.
|
|
1650
|
+
def unhandled_type (node, what)
|
|
1651
|
+
raise Error, "`#{what}` has no C for a #{node.type} -- this compiler " \
|
|
1652
|
+
"gained a computation type without gaining the code that " \
|
|
1653
|
+
"emits it"
|
|
1654
|
+
end
|
|
1655
|
+
|
|
1656
|
+
def error_argument
|
|
1657
|
+
if @in_function
|
|
1658
|
+
@uses_error_flag = true
|
|
1659
|
+
# A pasted body reports into the kernel's slot, which reaches it as
|
|
1660
|
+
# a parameter; a standalone one into the flag in its own object.
|
|
1661
|
+
return @error_parameter ? ERROR_FLAG : "&#{ERROR_FLAG}"
|
|
1662
|
+
end
|
|
1663
|
+
# Asking for the slot is what says this body can report, and every
|
|
1664
|
+
# place that can -- a checked subscript, the division helpers, a
|
|
1665
|
+
# `raise`, a pasted function that takes the flag -- asks here. So the
|
|
1666
|
+
# loops above learn it from the statements they will hold, which are
|
|
1667
|
+
# emitted before the loop is opened.
|
|
1668
|
+
@body_reports = true
|
|
1669
|
+
@masked_flag ? "(#{@masked_flag} ? (int32_t *) 0 : error)" : "error"
|
|
1670
|
+
end
|
|
1671
|
+
|
|
1672
|
+
|
|
1673
|
+
def stride_name (array, axis)
|
|
1674
|
+
"#{c_name(array)}_s#{axis}"
|
|
1675
|
+
end
|
|
1676
|
+
|
|
1677
|
+
def mask_pointer_name (array)
|
|
1678
|
+
"m_#{c_name(array)}"
|
|
1679
|
+
end
|
|
1680
|
+
|
|
1681
|
+
def mask_stride_name (array, axis)
|
|
1682
|
+
"#{c_name(array)}_ms#{axis}"
|
|
1683
|
+
end
|
|
1684
|
+
|
|
1685
|
+
def index_expression (array, axis, index, offset, reporting: true)
|
|
1686
|
+
if index.nil?
|
|
1687
|
+
# A pinned axis carries either a plain position or the expression
|
|
1688
|
+
# that computes one -- and where that expression is one only the
|
|
1689
|
+
# running kernel can work out, the position is checked here.
|
|
1690
|
+
return offset.to_s unless offset.is_a?(Node)
|
|
1691
|
+
return emit(offset, :int64) if Analyzer.fixed_subscript?(offset)
|
|
1692
|
+
# A scatter has already worked this position out and checked it.
|
|
1693
|
+
held = @position_temporaries[offset]
|
|
1694
|
+
return held if held
|
|
1695
|
+
@uses_index_check = true
|
|
1696
|
+
return "carray_jit_index(#{emit(offset, :int64)}, " \
|
|
1697
|
+
"#{extent_name(array, axis)}, " \
|
|
1698
|
+
"#{reporting ? error_argument : '(int32_t *) 0'})"
|
|
1699
|
+
end
|
|
1700
|
+
# A walked one may carry an expression too: `a[i - window]` reads the
|
|
1701
|
+
# offset out of the kernel's integer arguments.
|
|
1702
|
+
return "#{index} + (#{emit(offset, :int64)})" if offset.is_a?(Node)
|
|
1703
|
+
return index.to_s if offset.zero?
|
|
1704
|
+
plain = offset.negative? ? "#{index} - #{-offset}" : "#{index} + #{offset}"
|
|
1705
|
+
return plain unless bordered_read?(array)
|
|
1706
|
+
case @border
|
|
1707
|
+
when :clamp
|
|
1708
|
+
@uses_clamp = true
|
|
1709
|
+
"carray_jit_clamp(#{plain}, #{extent_name(array, axis)})"
|
|
1710
|
+
when :wrap
|
|
1711
|
+
@uses_wrap = true
|
|
1712
|
+
"carray_jit_wrap(#{plain}, #{extent_name(array, axis)})"
|
|
1713
|
+
else
|
|
1714
|
+
# `:zero` leaves the position alone and answers 0 instead of
|
|
1715
|
+
# reading, which is the reference's business rather than the
|
|
1716
|
+
# index's -- see cell_reference.
|
|
1717
|
+
plain
|
|
1718
|
+
end
|
|
1719
|
+
end
|
|
1720
|
+
|
|
1721
|
+
# True for a read this body has to answer for: a window's, while the
|
|
1722
|
+
# frame is being emitted. The result array is written at the cell and
|
|
1723
|
+
# reaches nowhere, so it is never one of these.
|
|
1724
|
+
def bordered_read? (array)
|
|
1725
|
+
@bordering && @analyzer.windows.include?(array)
|
|
1726
|
+
end
|
|
1727
|
+
|
|
1728
|
+
# Where the window has to be for there to be a cell to read: one test
|
|
1729
|
+
# per axis it reaches away from the cell on, and none for an axis it
|
|
1730
|
+
# sits still on.
|
|
1731
|
+
def inside_tests (array, subscripts)
|
|
1732
|
+
subscripts.each_with_index.filter_map { |(index, offset), axis|
|
|
1733
|
+
next if index.nil? || !offset.is_a?(Integer) || offset.zero?
|
|
1734
|
+
position = index_expression(array, axis, index, offset)
|
|
1735
|
+
"(#{position}) >= 0 && (#{position}) < #{extent_name(array, axis)}"
|
|
1736
|
+
}
|
|
1737
|
+
end
|
|
1738
|
+
|
|
1739
|
+
# Emits with the border set aside, so that the reference inside a guard
|
|
1740
|
+
# is the plain one rather than a guard around a guard.
|
|
1741
|
+
def with_border (rule)
|
|
1742
|
+
held = @border
|
|
1743
|
+
@border = rule
|
|
1744
|
+
yield
|
|
1745
|
+
ensure
|
|
1746
|
+
@border = held
|
|
1747
|
+
end
|
|
1748
|
+
|
|
1749
|
+
# The address of one cell. On the contiguous path the innermost axis
|
|
1750
|
+
# becomes a typed index, which is the part that decides whether the
|
|
1751
|
+
# compiler can vectorise the loop.
|
|
1752
|
+
def cell_reference (array, subscripts, kind = :data)
|
|
1753
|
+
# `:zero` says a read outside the array gives zero rather than
|
|
1754
|
+
# somewhere else's cell, so the position is left alone and the read
|
|
1755
|
+
# is asked for only where there is one. Outside, a value is 0 and a
|
|
1756
|
+
# mask byte is 0 too: the cell is not missing, it is not there.
|
|
1757
|
+
if @border == :zero && bordered_read?(array)
|
|
1758
|
+
inside = inside_tests(array, subscripts)
|
|
1759
|
+
unless inside.empty?
|
|
1760
|
+
reference = with_border(nil) { cell_reference(array, subscripts, kind) }
|
|
1761
|
+
return "(#{inside.join(' && ')} ? #{reference} : 0)"
|
|
1762
|
+
end
|
|
1763
|
+
end
|
|
1764
|
+
count = subscripts.size
|
|
1765
|
+
if kind == :mask
|
|
1766
|
+
# The mask read does not report an index of its own: it is asked
|
|
1767
|
+
# first, before it is known whether the cell being written is
|
|
1768
|
+
# masked, and reporting there would raise for a cell whose value is
|
|
1769
|
+
# out of contract anyway. The read of the value asks the same
|
|
1770
|
+
# question a moment later, with that known.
|
|
1771
|
+
terms = subscripts.each_with_index.map { |(index, offset), axis|
|
|
1772
|
+
"(#{index_expression(array, axis, index, offset, reporting: false)}) * " \
|
|
1773
|
+
"#{mask_stride_name(array, axis)}"
|
|
1774
|
+
}
|
|
1775
|
+
return "#{mask_pointer_name(array)}[#{terms.join(' + ')}]"
|
|
1776
|
+
end
|
|
1777
|
+
|
|
1778
|
+
type = storage_c_type(array)
|
|
1779
|
+
outer = (0...(count - 1)).map { |axis|
|
|
1780
|
+
index, offset = subscripts[axis]
|
|
1781
|
+
"(#{index_expression(array, axis, index, offset)}) * " \
|
|
1782
|
+
"#{stride_name(array, axis)}"
|
|
1783
|
+
}
|
|
1784
|
+
last = count - 1
|
|
1785
|
+
last_index, last_offset = subscripts[last]
|
|
1786
|
+
if @contiguous
|
|
1787
|
+
base = outer.empty? ? pointer_name(array)
|
|
1788
|
+
: "#{pointer_name(array)} + #{outer.join(' + ')}"
|
|
1789
|
+
"((#{type} *)(#{base}))" \
|
|
1790
|
+
"[#{index_expression(array, last, last_index, last_offset)}]"
|
|
1791
|
+
else
|
|
1792
|
+
terms = outer + ["(#{index_expression(array, last, last_index, last_offset)}) * " \
|
|
1793
|
+
"#{stride_name(array, last)}"]
|
|
1794
|
+
"*(#{type} *)(#{pointer_name(array)} + #{terms.join(' + ')})"
|
|
1795
|
+
end
|
|
1796
|
+
end
|
|
1797
|
+
|
|
1798
|
+
# Emits `node` so that its value has type `target`, inserting a cast
|
|
1799
|
+
# only where the type actually changes.
|
|
1800
|
+
def emit (node, target)
|
|
1801
|
+
widen(*emit_raw(node), node.type, target).first
|
|
1802
|
+
end
|
|
1803
|
+
|
|
1804
|
+
def emit_operand (node, target, parent_precedence, right_side = false)
|
|
1805
|
+
text, precedence = widen(*emit_raw(node), node.type, target)
|
|
1806
|
+
parenthesize(text, precedence, right_side ? parent_precedence + 1 : parent_precedence)
|
|
1807
|
+
end
|
|
1808
|
+
|
|
1809
|
+
# The cast from one computation type to a wider one. C would perform
|
|
1810
|
+
# most of these on its own, but writing them down is what makes the
|
|
1811
|
+
# dumped source say where the type changed -- and `int64_t` to
|
|
1812
|
+
# `double _Complex` is a conversion worth seeing.
|
|
1813
|
+
def widen (text, precedence, from, to)
|
|
1814
|
+
here, there = NUMERIC_RANK[from], NUMERIC_RANK[to]
|
|
1815
|
+
return [text, precedence] if here.nil? || there.nil? || there <= here
|
|
1816
|
+
["(#{COMPUTATION_C_TYPES.fetch(to)})" \
|
|
1817
|
+
"#{parenthesize(text, precedence, LEAF_PRECEDENCE)}", UNARY_PRECEDENCE]
|
|
1818
|
+
end
|
|
1819
|
+
|
|
1820
|
+
def parenthesize (text, precedence, needed)
|
|
1821
|
+
precedence < needed ? "(#{text})" : text
|
|
1822
|
+
end
|
|
1823
|
+
|
|
1824
|
+
def emit_raw (node)
|
|
1825
|
+
case node
|
|
1826
|
+
when IntegerLiteral then [format_integer(node.value), LEAF_PRECEDENCE]
|
|
1827
|
+
when FloatLiteral then [format_float(node.value, node.type), LEAF_PRECEDENCE]
|
|
1828
|
+
when ImaginaryLiteral
|
|
1829
|
+
real = TypeAssignment::REAL_PART_TYPES.fetch(node.type)
|
|
1830
|
+
["#{complex_build(node.type)}(#{format_float(0.0, real)}, " \
|
|
1831
|
+
"#{format_float(node.value.to_f, real)})", LEAF_PRECEDENCE]
|
|
1832
|
+
when BooleanLiteral then [node.value ? "1" : "0", LEAF_PRECEDENCE]
|
|
1833
|
+
when BitwiseNot
|
|
1834
|
+
["~#{emit_operand(node.operand, node.type, UNARY_PRECEDENCE)}",
|
|
1835
|
+
UNARY_PRECEDENCE]
|
|
1836
|
+
when IndexVariable then [node.name.to_s, LEAF_PRECEDENCE]
|
|
1837
|
+
when BoundsValue then ["bounds[#{node.slot}]", LEAF_PRECEDENCE]
|
|
1838
|
+
when ZeroLike
|
|
1839
|
+
[ZEROES.fetch(node.type), LEAF_PRECEDENCE]
|
|
1840
|
+
when LocalRead then [node.binding_name.to_s, LEAF_PRECEDENCE]
|
|
1841
|
+
when CaptureRead then [c_name(node.name), LEAF_PRECEDENCE]
|
|
1842
|
+
# A read is widened to the type the kernel computes in, because that
|
|
1843
|
+
# is the type the Ruby loop computes in: reading a float32 cell in
|
|
1844
|
+
# Ruby gives a Float, and reading an int32 cell gives an Integer that
|
|
1845
|
+
# does not stop at 2**31. Left as it lies, C would do the arithmetic
|
|
1846
|
+
# in float and in int -- narrower than Ruby on both counts, and
|
|
1847
|
+
# undefined rather than merely different when a narrow int overflows.
|
|
1848
|
+
#
|
|
1849
|
+
# A boolean cell is a byte holding 0 or 1, so it is already the value
|
|
1850
|
+
# it stands for. Keeping it that way on the way out is this kernel's
|
|
1851
|
+
# business, and cast_to_storage does it.
|
|
1852
|
+
when ElementRead
|
|
1853
|
+
cell = cell_reference(node.array, node.subscripts)
|
|
1854
|
+
widening = widening_cast(node.array, node.type)
|
|
1855
|
+
if widening
|
|
1856
|
+
["#{widening}#{parenthesize(cell, LEAF_PRECEDENCE, UNARY_PRECEDENCE)}",
|
|
1857
|
+
UNARY_PRECEDENCE]
|
|
1858
|
+
else
|
|
1859
|
+
[cell, LEAF_PRECEDENCE]
|
|
1860
|
+
end
|
|
1861
|
+
when MaskTest then emit_mask_test(node)
|
|
1862
|
+
when UnaryMinus
|
|
1863
|
+
operand, precedence = emit_raw(node.operand)
|
|
1864
|
+
["-#{parenthesize(operand, precedence, UNARY_PRECEDENCE)}", UNARY_PRECEDENCE]
|
|
1865
|
+
when LogicalNot
|
|
1866
|
+
operand, precedence = emit_raw(node.operand)
|
|
1867
|
+
["! #{parenthesize(operand, precedence, UNARY_PRECEDENCE)}", UNARY_PRECEDENCE]
|
|
1868
|
+
when LogicalOperation
|
|
1869
|
+
precedence = PRECEDENCE.fetch(node.operator)
|
|
1870
|
+
["#{emit_operand(node.left, :boolean, precedence)} #{node.operator} " \
|
|
1871
|
+
"#{emit_operand(node.right, :boolean, precedence)}", precedence]
|
|
1872
|
+
when AbsoluteValue then emit_absolute_value(node)
|
|
1873
|
+
when Conversion then emit_conversion(node)
|
|
1874
|
+
when ComplexPart then emit_complex_part(node)
|
|
1875
|
+
when ComplexBuild
|
|
1876
|
+
real = TypeAssignment::REAL_PART_TYPES.fetch(node.type)
|
|
1877
|
+
["#{complex_build(node.type)}(#{emit(node.real, real)}, " \
|
|
1878
|
+
"#{emit(node.imaginary, real)})", LEAF_PRECEDENCE]
|
|
1879
|
+
when Power then emit_power(node)
|
|
1880
|
+
when MathCall then emit_math_call(node)
|
|
1881
|
+
when ArrayAddress
|
|
1882
|
+
[c_name(node.array), LEAF_PRECEDENCE]
|
|
1883
|
+
when PointerRead
|
|
1884
|
+
# A pointer parameter is reached the way C reaches it: contiguous,
|
|
1885
|
+
# from the address it was handed. No base, no stride, no bounds --
|
|
1886
|
+
# a subscript here is the caller's business, as it is in C.
|
|
1887
|
+
["#{node.name}[#{emit(node.index, :int64)}]", LEAF_PRECEDENCE]
|
|
1888
|
+
when RecursiveCall
|
|
1889
|
+
# The function calls itself by the symbol it is being defined
|
|
1890
|
+
# under, not by the name the declaration spelled: that name is
|
|
1891
|
+
# unqualified and would reach whatever else in the process answers
|
|
1892
|
+
# to it. A captured function travels as a pointer beside the
|
|
1893
|
+
# scalars; this one is right here, so the call is direct.
|
|
1894
|
+
arguments = node.arguments.zip(node.parameters)
|
|
1895
|
+
.map { |argument, parameter|
|
|
1896
|
+
if argument.is_a?(ArrayAddress)
|
|
1897
|
+
emit(argument, :address)
|
|
1898
|
+
else
|
|
1899
|
+
emit(argument, parameter.computation)
|
|
1900
|
+
end
|
|
1901
|
+
}
|
|
1902
|
+
arguments << ERROR_FLAG if @error_parameter
|
|
1903
|
+
["#{@own_symbol}(#{arguments.join(', ')})", LEAF_PRECEDENCE]
|
|
1904
|
+
when CFunctionCall
|
|
1905
|
+
c_function = @c_functions.fetch(node.name)
|
|
1906
|
+
arguments = node.arguments.zip(c_function.parameters)
|
|
1907
|
+
.map { |argument, parameter|
|
|
1908
|
+
if argument.is_a?(ArrayAddress)
|
|
1909
|
+
emit(argument, :address)
|
|
1910
|
+
else
|
|
1911
|
+
emit(argument, parameter.computation)
|
|
1912
|
+
end
|
|
1913
|
+
}
|
|
1914
|
+
# A pasted body is reached by its symbol; an address by the local
|
|
1915
|
+
# the declarations bound it to.
|
|
1916
|
+
called = c_function.pasted? ? c_function.name : c_name(node.name)
|
|
1917
|
+
# And one that can report a failure is handed the slot this kernel
|
|
1918
|
+
# is watching -- null under a masked cell, where the value written
|
|
1919
|
+
# is out of contract and a division by zero there was not asked
|
|
1920
|
+
# about. The helpers it calls check for that, as the kernel's own
|
|
1921
|
+
# do.
|
|
1922
|
+
arguments << error_argument if c_function.pasted_takes_error?
|
|
1923
|
+
["#{called}(#{arguments.join(', ')})", LEAF_PRECEDENCE]
|
|
1924
|
+
when Conditional then emit_ternary(node)
|
|
1925
|
+
when BinaryOperation then emit_binary(node)
|
|
1926
|
+
else
|
|
1927
|
+
raise Error, "code generation reached #{node.class}"
|
|
1928
|
+
end
|
|
1929
|
+
end
|
|
1930
|
+
|
|
1931
|
+
# `a[i] == UNDEF` reads the mask byte, never the value.
|
|
1932
|
+
def emit_mask_test (node)
|
|
1933
|
+
cell = cell_reference(node.array, node.subscripts, :mask)
|
|
1934
|
+
node.negated ? ["! #{cell}", UNARY_PRECEDENCE] : [cell, LEAF_PRECEDENCE]
|
|
1935
|
+
end
|
|
1936
|
+
|
|
1937
|
+
# Float#floor and friends hand back an Integer in Ruby, so the C rounds
|
|
1938
|
+
# and then narrows. An Integer receiver is already there.
|
|
1939
|
+
def emit_conversion (node)
|
|
1940
|
+
if TypeAssignment::INTEGER_TYPES.include?(node.operand.type)
|
|
1941
|
+
if node.type == node.operand.type
|
|
1942
|
+
return [emit(node.operand, node.operand.type), LEAF_PRECEDENCE]
|
|
1943
|
+
end
|
|
1944
|
+
return ["(double)#{parenthesize(*emit_raw(node.operand), LEAF_PRECEDENCE)}",
|
|
1945
|
+
UNARY_PRECEDENCE]
|
|
1946
|
+
end
|
|
1947
|
+
return [emit(node.operand, :double), LEAF_PRECEDENCE] if node.result_type == :double
|
|
1948
|
+
["(int64_t)#{node.name}(#{emit(node.operand, :double)})", UNARY_PRECEDENCE]
|
|
1949
|
+
end
|
|
1950
|
+
|
|
1951
|
+
# creal and cimag are the way out of the complex type; conj stays in it.
|
|
1952
|
+
#
|
|
1953
|
+
# A real number answers all four in Ruby, and three of them without
|
|
1954
|
+
# computing anything: it is its own real part and its own conjugate,
|
|
1955
|
+
# and its imaginary part is a zero.
|
|
1956
|
+
def emit_complex_part (node)
|
|
1957
|
+
if complex_type?(node.operand.type)
|
|
1958
|
+
function = { :real => "creal", :imaginary => "cimag",
|
|
1959
|
+
:conjugate => "conj", :arg => "carg" }.fetch(node.name)
|
|
1960
|
+
function += "f" if node.operand.type == :float_complex
|
|
1961
|
+
return ["#{function}(#{emit(node.operand, node.operand.type)})",
|
|
1962
|
+
LEAF_PRECEDENCE]
|
|
1963
|
+
end
|
|
1964
|
+
case node.name
|
|
1965
|
+
when :real, :conjugate then emit_raw(node.operand)
|
|
1966
|
+
when :imaginary then [ZEROES.fetch(:int64), LEAF_PRECEDENCE]
|
|
1967
|
+
else
|
|
1968
|
+
@uses_real_arg = true
|
|
1969
|
+
["carray_jit_real_arg(#{emit(node.operand, :double)})", LEAF_PRECEDENCE]
|
|
1970
|
+
end
|
|
1971
|
+
end
|
|
1972
|
+
|
|
1973
|
+
# An integer power is squared out rather than sent through pow, whose
|
|
1974
|
+
# double result would not be the exact integer Ruby gives.
|
|
1975
|
+
#
|
|
1976
|
+
# A complex power is the one place in this compiler where the answer is
|
|
1977
|
+
# not the Ruby loop's to the last bit. Ruby raises a Complex to a power
|
|
1978
|
+
# by binary powering, with exact answers along the axes; cpow goes round
|
|
1979
|
+
# through exp and log. They agree to within an ulp, and that is
|
|
1980
|
+
# accepted here rather than reproducing complex.c's algorithm -- which
|
|
1981
|
+
# has changed between Ruby versions, so reproducing it would tie a
|
|
1982
|
+
# compiled kernel to the interpreter that compiled it.
|
|
1983
|
+
def emit_power (node)
|
|
1984
|
+
if complex_type?(node.type)
|
|
1985
|
+
text = "cpow(#{emit(node.base, :complex)}, " \
|
|
1986
|
+
"#{emit(node.exponent, :complex)})"
|
|
1987
|
+
return [text, LEAF_PRECEDENCE] if node.type == :complex
|
|
1988
|
+
return ["(float _Complex)#{text}", UNARY_PRECEDENCE]
|
|
1989
|
+
end
|
|
1990
|
+
if node.type == :uint64
|
|
1991
|
+
@uses_unsigned_power = true
|
|
1992
|
+
return ["carray_jit_unsigned_power(#{emit(node.base, :uint64)}, " \
|
|
1993
|
+
"#{emit(node.exponent, :uint64)})", LEAF_PRECEDENCE]
|
|
1994
|
+
end
|
|
1995
|
+
if node.type == :int64
|
|
1996
|
+
@uses_integer_power = true
|
|
1997
|
+
return ["carray_jit_integer_power(#{emit(node.base, :int64)}, " \
|
|
1998
|
+
"#{emit(node.exponent, :int64)})", LEAF_PRECEDENCE]
|
|
1999
|
+
end
|
|
2000
|
+
if node.type == :float
|
|
2001
|
+
return ["powf(#{emit(node.base, :float)}, " \
|
|
2002
|
+
"#{emit(node.exponent, :float)})", LEAF_PRECEDENCE]
|
|
2003
|
+
end
|
|
2004
|
+
unhandled_type(node, "**") unless node.type == :double
|
|
2005
|
+
["pow(#{emit(node.base, :double)}, #{emit(node.exponent, :double)})",
|
|
2006
|
+
LEAF_PRECEDENCE]
|
|
2007
|
+
end
|
|
2008
|
+
|
|
2009
|
+
# cabs, fabs and llabs are three functions rather than one because C has
|
|
2010
|
+
# no generic for them, so this names each type instead of letting one of
|
|
2011
|
+
# them be what a type it has not heard of falls into.
|
|
2012
|
+
def emit_absolute_value (node)
|
|
2013
|
+
# The complex case is asked of the operand rather than of the result:
|
|
2014
|
+
# the magnitude of a complex number is a real one, so node.type is
|
|
2015
|
+
# already :double by the time we are here.
|
|
2016
|
+
if complex_type?(node.operand.type)
|
|
2017
|
+
function = node.operand.type == :float_complex ? "cabsf" : "cabs"
|
|
2018
|
+
return ["#{function}(#{emit(node.operand, node.operand.type)})",
|
|
2019
|
+
LEAF_PRECEDENCE]
|
|
2020
|
+
end
|
|
2021
|
+
case node.type
|
|
2022
|
+
when :double then ["fabs(#{emit(node.operand, :double)})", LEAF_PRECEDENCE]
|
|
2023
|
+
when :float then ["fabsf(#{emit(node.operand, :float)})", LEAF_PRECEDENCE]
|
|
2024
|
+
when :int64 then ["llabs(#{emit(node.operand, :int64)})", LEAF_PRECEDENCE]
|
|
2025
|
+
# An unsigned number is its own magnitude, and llabs would take it
|
|
2026
|
+
# through a signed type on the way.
|
|
2027
|
+
when :uint64 then [emit(node.operand, :uint64), LEAF_PRECEDENCE]
|
|
2028
|
+
else unhandled_type(node, "abs")
|
|
2029
|
+
end
|
|
2030
|
+
end
|
|
2031
|
+
|
|
2032
|
+
def emit_ternary (node)
|
|
2033
|
+
["#{emit(node.condition, :boolean)} ? " \
|
|
2034
|
+
"#{emit(node.consequent, node.type)} : " \
|
|
2035
|
+
"#{emit(node.alternative, node.type)}", 10]
|
|
2036
|
+
end
|
|
2037
|
+
|
|
2038
|
+
def emit_binary (node)
|
|
2039
|
+
return emit_integer_division(node) if node.operator == :/ &&
|
|
2040
|
+
TypeAssignment::INTEGER_TYPES.include?(node.type)
|
|
2041
|
+
return emit_modulo(node) if node.operator == :%
|
|
2042
|
+
return emit_complex_binary(node) if complex_type?(node.type)
|
|
2043
|
+
|
|
2044
|
+
operand_type =
|
|
2045
|
+
if Analyzer::COMPARISON_OPERATORS.include?(node.operator)
|
|
2046
|
+
# Both sides are brought to the wider of the two, which is the
|
|
2047
|
+
# only way `z == 1` asks what Ruby asks.
|
|
2048
|
+
[node.left.type, node.right.type]
|
|
2049
|
+
.max_by { |type| NUMERIC_RANK.fetch(type, -1) }
|
|
2050
|
+
else
|
|
2051
|
+
node.type
|
|
2052
|
+
end
|
|
2053
|
+
|
|
2054
|
+
precedence = PRECEDENCE.fetch(node.operator)
|
|
2055
|
+
left = emit_operand(node.left, operand_type, precedence)
|
|
2056
|
+
# Every operator here groups to the left, so a right operand of the
|
|
2057
|
+
# same precedence needs parentheses whatever the operator is. Not
|
|
2058
|
+
# because C would compute a different number for integers -- it would
|
|
2059
|
+
# not -- but because floating-point arithmetic does not associate:
|
|
2060
|
+
# `a + (b + c)` regrouped as `(a + b) + c` is a different sum, and
|
|
2061
|
+
# this compiler's whole claim is that it computes what the same
|
|
2062
|
+
# expression computes in Ruby. `-` and `/` were handled and `+` and
|
|
2063
|
+
# `*` were not, on the reasoning that they associate. They associate
|
|
2064
|
+
# in arithmetic; doubles are not arithmetic.
|
|
2065
|
+
right = emit_operand(node.right, operand_type, precedence, true)
|
|
2066
|
+
["#{left} #{node.operator} #{right}", precedence]
|
|
2067
|
+
end
|
|
2068
|
+
|
|
2069
|
+
# Ruby's Complex arithmetic is not C's, in three places that matter.
|
|
2070
|
+
#
|
|
2071
|
+
# A Complex added to a real number is added component by component, and
|
|
2072
|
+
# the imaginary part comes through untouched rather than having a zero
|
|
2073
|
+
# added to it: Ruby's `f_add` returns the other operand as it stands
|
|
2074
|
+
# when one of them is the exact Integer zero a real operand carries.
|
|
2075
|
+
# So `Complex(1.0, -0.0) + 2.0` is `3.0-0.0i`, where widening the 2.0
|
|
2076
|
+
# to a complex first would give `3.0+0.0i`, because `-0.0 + 0.0` is
|
|
2077
|
+
# `+0.0`. Multiplying by a real scales each part for the same reason,
|
|
2078
|
+
# and dividing by one divides each part.
|
|
2079
|
+
#
|
|
2080
|
+
# Subtraction is the exception: there the zero really is subtracted, in
|
|
2081
|
+
# Ruby as in C, so `z - x` and `x - z` are C's own operator. So is
|
|
2082
|
+
# `x * z`, which Ruby coerces and multiplies out in full -- which is
|
|
2083
|
+
# why `2.0 * Complex(1.0, -0.0)` and `Complex(1.0, -0.0) * 2.0` do not
|
|
2084
|
+
# agree with each other, and each is reproduced its own way.
|
|
2085
|
+
#
|
|
2086
|
+
# And a complex division is Smith's method as complex.c writes it,
|
|
2087
|
+
# which is not what the C library's __divdc3 computes.
|
|
2088
|
+
#
|
|
2089
|
+
# Each of these goes through a function rather than being written out,
|
|
2090
|
+
# so that the operand is named once: spelling `CMPLX(creal(z) + x,
|
|
2091
|
+
# cimag(z))` inline would emit the whole of z twice, and twice again
|
|
2092
|
+
# for the operation outside it.
|
|
2093
|
+
def emit_complex_binary (node)
|
|
2094
|
+
complex_type = node.type
|
|
2095
|
+
# Multiplying and dividing two complex numbers are the operations
|
|
2096
|
+
# that cancel: `(ac - bd)` and Smith's method both subtract numbers of
|
|
2097
|
+
# the same size, so the narrow width loses the answer rather than the
|
|
2098
|
+
# last bit of it. Both are reached in double and rounded once, which
|
|
2099
|
+
# is what Ruby does and what CArray settled on. Adding and
|
|
2100
|
+
# subtracting do not cancel past the one rounding a store makes
|
|
2101
|
+
# anyway, so they stay narrow.
|
|
2102
|
+
if complex_type == :float_complex &&
|
|
2103
|
+
[:*, :/].include?(node.operator) &&
|
|
2104
|
+
complex_type?(node.left.type) && complex_type?(node.right.type)
|
|
2105
|
+
helper = mixed_helper(node, :complex)
|
|
2106
|
+
if helper
|
|
2107
|
+
text, = emit_helper_call(*helper, false)
|
|
2108
|
+
else
|
|
2109
|
+
precedence = PRECEDENCE.fetch(node.operator)
|
|
2110
|
+
# Parenthesised, because the cast binds tighter than the operator:
|
|
2111
|
+
# without them it would narrow the left operand and leave the
|
|
2112
|
+
# multiplication to be worked out around it.
|
|
2113
|
+
text = "(#{emit_operand(node.left, :complex, precedence)} " \
|
|
2114
|
+
"#{node.operator} " \
|
|
2115
|
+
"#{emit_operand(node.right, :complex, precedence)})"
|
|
2116
|
+
end
|
|
2117
|
+
return ["(float _Complex)#{text}", UNARY_PRECEDENCE]
|
|
2118
|
+
end
|
|
2119
|
+
narrow = complex_type == :float_complex
|
|
2120
|
+
helper = mixed_helper(node, complex_type)
|
|
2121
|
+
return emit_helper_call(*helper, narrow) if helper
|
|
2122
|
+
precedence = PRECEDENCE.fetch(node.operator)
|
|
2123
|
+
["#{emit_operand(node.left, complex_type, precedence)} #{node.operator} " \
|
|
2124
|
+
"#{emit_operand(node.right, complex_type, precedence, node.operator == :-)}",
|
|
2125
|
+
precedence]
|
|
2126
|
+
end
|
|
2127
|
+
|
|
2128
|
+
def emit_helper_call (name, arguments, narrow)
|
|
2129
|
+
@complex_helpers |= [[name, narrow]]
|
|
2130
|
+
prefix = narrow ? "carray_jit_f_" : "carray_jit_"
|
|
2131
|
+
["#{prefix}#{name}(#{arguments.join(', ')})", LEAF_PRECEDENCE]
|
|
2132
|
+
end
|
|
2133
|
+
|
|
2134
|
+
# Which helper an operation carrying a complex operand needs, and what
|
|
2135
|
+
# to pass it -- or nil where C's own operator is what Ruby computes.
|
|
2136
|
+
def mixed_helper (node, ctype)
|
|
2137
|
+
rtype = TypeAssignment::REAL_PART_TYPES.fetch(ctype)
|
|
2138
|
+
left, right, operator = node.left, node.right, node.operator
|
|
2139
|
+
case operator
|
|
2140
|
+
when :/
|
|
2141
|
+
return ["complex_div_real", [emit(left, ctype), emit(right, rtype)]] if
|
|
2142
|
+
real_type?(right)
|
|
2143
|
+
return ["real_divide_complex", [emit(left, rtype), emit(right, ctype)]] if
|
|
2144
|
+
real_type?(left)
|
|
2145
|
+
["complex_divide", [emit(left, ctype), emit(right, ctype)]]
|
|
2146
|
+
when :+
|
|
2147
|
+
if imaginary_literal?(right)
|
|
2148
|
+
["complex_add_imaginary", [emit(left, ctype), format_float(right.value.to_f, rtype)]]
|
|
2149
|
+
elsif imaginary_literal?(left)
|
|
2150
|
+
["imaginary_add_complex", [format_float(left.value.to_f, rtype), emit(right, ctype)]]
|
|
2151
|
+
elsif real_type?(right)
|
|
2152
|
+
["complex_add_real", [emit(left, ctype), emit(right, rtype)]]
|
|
2153
|
+
elsif real_type?(left)
|
|
2154
|
+
["real_add_complex", [emit(left, rtype), emit(right, ctype)]]
|
|
2155
|
+
end
|
|
2156
|
+
when :*
|
|
2157
|
+
if real_type?(right)
|
|
2158
|
+
["complex_mul_real", [emit(left, ctype), emit(right, rtype)]]
|
|
2159
|
+
end
|
|
2160
|
+
end
|
|
2161
|
+
end
|
|
2162
|
+
|
|
2163
|
+
def complex_type? (type)
|
|
2164
|
+
TypeAssignment.complex?(type)
|
|
2165
|
+
end
|
|
2166
|
+
|
|
2167
|
+
# CMPLX and CMPLXF, which carry the sign of both zeros where writing
|
|
2168
|
+
# `re + im * I` would not.
|
|
2169
|
+
def complex_build (type)
|
|
2170
|
+
type == :float_complex ? "CMPLXF" : "CMPLX"
|
|
2171
|
+
end
|
|
2172
|
+
|
|
2173
|
+
# The complex functions that are safe to reach at the narrow width, and
|
|
2174
|
+
# they are a list rather than a rule because the reason is inside the
|
|
2175
|
+
# library. A libm transcendental is written to complete at its own
|
|
2176
|
+
# width, so `csinf` is a float32 answer to a float32 question. What is
|
|
2177
|
+
# not safe is a function that builds something out of the operand
|
|
2178
|
+
# before the transcendental starts: `clog(z)` needs `log|z|`, and on the
|
|
2179
|
+
# unit circle that is the difference of two numbers near one. Computed
|
|
2180
|
+
# in float, |z| rounds to exactly one and the real part of the answer
|
|
2181
|
+
# becomes zero -- measured here on four thousand points of the unit
|
|
2182
|
+
# circle, the error is 3.2e-08 where the answer itself is 3.7e-08.
|
|
2183
|
+
#
|
|
2184
|
+
# `**` is the same fault at one remove, `cpow` being `cexp(z * clog(a))`,
|
|
2185
|
+
# so it is emitted wide in emit_power and is not on this list either.
|
|
2186
|
+
NARROW_COMPLEX_MATH = %w[
|
|
2187
|
+
csqrt cexp csin ccos ctan casin catan csinh ccosh ctanh
|
|
2188
|
+
].freeze
|
|
2189
|
+
|
|
2190
|
+
# A math function is reached at the width its argument claims, where
|
|
2191
|
+
# that is safe: `sinf` for a float32 cell, `csqrtf` for a cmplx64 one.
|
|
2192
|
+
# That is what CArray computes, and agreeing with it by construction is
|
|
2193
|
+
# worth more than agreeing because this platform happens to implement
|
|
2194
|
+
# `sinf` through `sin`.
|
|
2195
|
+
#
|
|
2196
|
+
# The real functions are all safe: `log(x)` reads its argument rather
|
|
2197
|
+
# than building one, so `logf` does not lose what `clogf` loses.
|
|
2198
|
+
def emit_math_call (node)
|
|
2199
|
+
if complex_type?(node.type)
|
|
2200
|
+
function = Analyzer::COMPLEX_MATH_FUNCTIONS.fetch(node.name)
|
|
2201
|
+
narrow = node.type == :float_complex &&
|
|
2202
|
+
NARROW_COMPLEX_MATH.include?(function)
|
|
2203
|
+
type = narrow ? :float_complex : :complex
|
|
2204
|
+
function += "f" if narrow
|
|
2205
|
+
arguments = node.arguments.map { |argument| emit(argument, type) }
|
|
2206
|
+
text = "#{function}(#{arguments.join(', ')})"
|
|
2207
|
+
return [text, LEAF_PRECEDENCE] if narrow || node.type == :complex
|
|
2208
|
+
return ["(float _Complex)#{text}", UNARY_PRECEDENCE]
|
|
2209
|
+
end
|
|
2210
|
+
function = node.name.to_s
|
|
2211
|
+
function += "f" if node.type == :float
|
|
2212
|
+
arguments = node.arguments.map { |argument| emit(argument, node.type) }
|
|
2213
|
+
["#{function}(#{arguments.join(', ')})", LEAF_PRECEDENCE]
|
|
2214
|
+
end
|
|
2215
|
+
|
|
2216
|
+
def real_type? (node)
|
|
2217
|
+
TypeAssignment::REAL_TYPES.include?(node.type)
|
|
2218
|
+
end
|
|
2219
|
+
|
|
2220
|
+
def imaginary_literal? (node)
|
|
2221
|
+
node.is_a?(ImaginaryLiteral)
|
|
2222
|
+
end
|
|
2223
|
+
|
|
2224
|
+
# Integer `/` is floored to agree with Ruby and with CArray's own
|
|
2225
|
+
# kernels. A positive power-of-two divisor needs no correction at all:
|
|
2226
|
+
# an arithmetic shift already floors, and is cheaper than the truncating
|
|
2227
|
+
# divide the C operator would emit.
|
|
2228
|
+
def emit_integer_division (node)
|
|
2229
|
+
type = node.type
|
|
2230
|
+
shift = power_of_two_shift(node.right)
|
|
2231
|
+
if shift
|
|
2232
|
+
return ["#{emit_operand(node.left, type, 85)} >> #{shift}", 50]
|
|
2233
|
+
end
|
|
2234
|
+
# An unsigned operand cannot be negative, so C's truncation already
|
|
2235
|
+
# floors and only the zero divisor has to be caught.
|
|
2236
|
+
if type == :uint64
|
|
2237
|
+
@uses_unsigned_divide = true
|
|
2238
|
+
return ["carray_jit_unsigned_divide(#{emit(node.left, :uint64)}, " \
|
|
2239
|
+
"#{emit(node.right, :uint64)}, #{error_argument})",
|
|
2240
|
+
LEAF_PRECEDENCE]
|
|
2241
|
+
end
|
|
2242
|
+
@uses_floor_divide = true
|
|
2243
|
+
["carray_jit_floor_divide(#{emit(node.left, :int64)}, " \
|
|
2244
|
+
"#{emit(node.right, :int64)}, #{error_argument})", LEAF_PRECEDENCE]
|
|
2245
|
+
end
|
|
2246
|
+
|
|
2247
|
+
# `%` floors, so it is not C's `%` -- see the helper above.
|
|
2248
|
+
def emit_modulo (node)
|
|
2249
|
+
if node.type == :uint64
|
|
2250
|
+
@uses_unsigned_modulo = true
|
|
2251
|
+
return ["carray_jit_unsigned_modulo(#{emit(node.left, :uint64)}, " \
|
|
2252
|
+
"#{emit(node.right, :uint64)}, #{error_argument})",
|
|
2253
|
+
LEAF_PRECEDENCE]
|
|
2254
|
+
end
|
|
2255
|
+
@uses_floor_modulo = true
|
|
2256
|
+
if node.type == :int64
|
|
2257
|
+
return ["carray_jit_floor_modulo(#{emit(node.left, :int64)}, " \
|
|
2258
|
+
"#{emit(node.right, :int64)}, #{error_argument})", LEAF_PRECEDENCE]
|
|
2259
|
+
end
|
|
2260
|
+
if node.type == :float
|
|
2261
|
+
@uses_floor_modulo_float = true
|
|
2262
|
+
return ["carray_jit_floor_modulo_float(#{emit(node.left, :float)}, " \
|
|
2263
|
+
"#{emit(node.right, :float)})", LEAF_PRECEDENCE]
|
|
2264
|
+
end
|
|
2265
|
+
unhandled_type(node, "%") unless node.type == :double
|
|
2266
|
+
["carray_jit_floor_modulo_real(#{emit(node.left, :double)}, " \
|
|
2267
|
+
"#{emit(node.right, :double)})", LEAF_PRECEDENCE]
|
|
2268
|
+
end
|
|
2269
|
+
|
|
2270
|
+
def power_of_two_shift (node)
|
|
2271
|
+
return nil unless node.is_a?(IntegerLiteral)
|
|
2272
|
+
value = node.value
|
|
2273
|
+
return nil unless value > 0 && (value & (value - 1)).zero?
|
|
2274
|
+
Math.log2(value).to_i
|
|
2275
|
+
end
|
|
2276
|
+
|
|
2277
|
+
def format_integer (value)
|
|
2278
|
+
"INT64_C(#{value})"
|
|
2279
|
+
end
|
|
2280
|
+
|
|
2281
|
+
def format_float (value, type = :double)
|
|
2282
|
+
# A float literal without the suffix is a double, and one double in an
|
|
2283
|
+
# expression takes the whole expression with it -- so the suffix is
|
|
2284
|
+
# what keeps a float32 kernel computing in float.
|
|
2285
|
+
if type == :float
|
|
2286
|
+
# 9 significant digits round-trips a float exactly.
|
|
2287
|
+
text = format("%.9g", value)
|
|
2288
|
+
text << ".0" unless text.match?(/[.eEn]/)
|
|
2289
|
+
return text << "f"
|
|
2290
|
+
end
|
|
2291
|
+
# 17 significant digits round-trips a double exactly.
|
|
2292
|
+
text = format("%.17g", value)
|
|
2293
|
+
text << ".0" unless text.match?(/[.eEn]/)
|
|
2294
|
+
text
|
|
2295
|
+
end
|
|
2296
|
+
|
|
2297
|
+
def next_temporary (prefix = "result")
|
|
2298
|
+
@temporary_count += 1
|
|
2299
|
+
"#{prefix}#{@temporary_count}"
|
|
2300
|
+
end
|
|
2301
|
+
|
|
2302
|
+
end
|
|
2303
|
+
|
|
2304
|
+
end
|
|
2305
|
+
end
|