carray-jit 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +917 -3
- data/README.md +14 -12
- data/carray-jit.gemspec +1 -3
- data/docs/00_Introduction.md +5 -3
- data/docs/01_GettingStarted.md +1 -1
- data/docs/02_KernelShapes.md +94 -15
- data/docs/03_SupportedFeatures.md +665 -32
- data/docs/04_Compiling.md +34 -7
- data/docs/05_DesignNotes.md +3 -3
- data/docs/06_Cheatsheet.md +200 -7
- data/docs/07_StepByStep.ja.md +533 -0
- data/docs/07_StepByStep.md +534 -0
- data/docs/08_LanguageReference.md +427 -0
- data/examples/README.md +12 -0
- data/examples/applications/alarm.rb +121 -0
- data/examples/applications/collatz.rb +105 -0
- data/examples/applications/cubic_spline.rb +331 -0
- data/examples/applications/dithering.rb +144 -0
- data/examples/applications/group_stats.rb +115 -0
- data/examples/applications/lookup.rb +126 -0
- data/examples/applications/median_filter.rb +153 -0
- data/examples/applications/parcel_ascent.rb +220 -0
- data/examples/applications/point_in_polygon.rb +111 -0
- data/examples/applications/random_walk.rb +98 -0
- data/examples/applications/van_der_pol.rb +186 -0
- data/examples/applications/wet_bulb.rb +140 -0
- data/examples/features/10_complex.rb +14 -4
- data/examples/features/15_loops.rb +7 -1
- data/lib/carray/jit/access.rb +14 -0
- data/lib/carray/jit/analyzer.rb +2218 -140
- data/lib/carray/jit/block_reader.rb +37 -6
- data/lib/carray/jit/c_function.rb +961 -86
- data/lib/carray/jit/c_generator.rb +1970 -191
- data/lib/carray/jit/call.rb +68 -0
- data/lib/carray/jit/compiler.rb +102 -14
- data/lib/carray/jit/expression.rb +271 -33
- data/lib/carray/jit/kernel.rb +382 -32
- data/lib/carray/jit/node.rb +374 -9
- data/lib/carray/jit/sorting_networks.rb +182 -0
- data/lib/carray/jit/type_assignment.rb +424 -35
- data/lib/carray/jit/version.rb +1 -1
- data/lib/carray/jit.rb +547 -77
- metadata +23 -8
- data/ext/carray_jit_access/carray_jit_access.c +0 -460
- data/ext/carray_jit_access/extconf.rb +0 -8
|
@@ -126,6 +126,15 @@ class CArray
|
|
|
126
126
|
# would. Only the ones a kernel actually uses are emitted, and the
|
|
127
127
|
# reason each exists is at emit_complex_binary.
|
|
128
128
|
COMPLEX_HELPERS = {
|
|
129
|
+
"complex_finite" => <<~C,
|
|
130
|
+
/* Complex#finite? is both parts finite, which is what Ruby asks
|
|
131
|
+
and is not what isfinite of a complex would mean. */
|
|
132
|
+
static inline int
|
|
133
|
+
carray_jit_complex_finite (double _Complex z)
|
|
134
|
+
{
|
|
135
|
+
return isfinite(creal(z)) && isfinite(cimag(z));
|
|
136
|
+
}
|
|
137
|
+
C
|
|
129
138
|
"complex_add_real" => <<~C,
|
|
130
139
|
/* Ruby leaves the imaginary part of `z + x` exactly as it was,
|
|
131
140
|
rather than adding the real operand's zero to it. */
|
|
@@ -159,9 +168,61 @@ class CArray
|
|
|
159
168
|
return CMPLX(creal(z), y + cimag(z));
|
|
160
169
|
}
|
|
161
170
|
C
|
|
171
|
+
"safe_multiply" => <<~C,
|
|
172
|
+
/* complex.c's safe_mul, which every part of a Ruby complex product
|
|
173
|
+
goes through: a zero against a number that is not a zero or a
|
|
174
|
+
NaN meets only that number's sign, so a zero times an infinity is
|
|
175
|
+
a signed zero rather than C's NaN. C's own complex `*` recovers
|
|
176
|
+
from infinities by Annex G's rules instead, which are not these. */
|
|
177
|
+
static inline double
|
|
178
|
+
carray_jit_safe_multiply (double a, double b)
|
|
179
|
+
{
|
|
180
|
+
if ( a != 0 && b == 0 && ! isnan(a) ) a = signbit(a) ? -1.0 : 1.0;
|
|
181
|
+
else if ( b != 0 && a == 0 && ! isnan(b) ) b = signbit(b) ? -1.0 : 1.0;
|
|
182
|
+
return a * b;
|
|
183
|
+
}
|
|
184
|
+
C
|
|
185
|
+
"complex_multiply" => <<~C,
|
|
186
|
+
/* Ruby's `a * b` for two Complex numbers, part by part in
|
|
187
|
+
complex.c's order: comp_mul. safe_mul differs from a plain
|
|
188
|
+
product only for a zero against an infinity, which a plain
|
|
189
|
+
product answers NaN -- so the plain parts are the answer unless
|
|
190
|
+
one of them is a NaN, and only then is it worked out again the
|
|
191
|
+
safe way. C's own `*` takes the same shape, and costs the same. */
|
|
192
|
+
static inline double _Complex
|
|
193
|
+
carray_jit_complex_multiply (double _Complex a, double _Complex b)
|
|
194
|
+
{
|
|
195
|
+
double are = creal(a), aim = cimag(a);
|
|
196
|
+
double bre = creal(b), bim = cimag(b);
|
|
197
|
+
double re = are * bre - aim * bim, im = are * bim + aim * bre;
|
|
198
|
+
if ( isnan(re) || isnan(im) ) {
|
|
199
|
+
re = carray_jit_safe_multiply(are, bre) - carray_jit_safe_multiply(aim, bim);
|
|
200
|
+
im = carray_jit_safe_multiply(are, bim) + carray_jit_safe_multiply(aim, bre);
|
|
201
|
+
}
|
|
202
|
+
return CMPLX(re, im);
|
|
203
|
+
}
|
|
204
|
+
C
|
|
205
|
+
"real_multiply_complex" => <<~C,
|
|
206
|
+
/* `x * z` coerces x to a Complex whose imaginary part is an exact
|
|
207
|
+
zero, and multiplies in full. That zero meets safe_mul as any
|
|
208
|
+
zero does, so it is written as 0.0 here and gives the same parts;
|
|
209
|
+
only an addition treats an exact zero differently, and here the
|
|
210
|
+
zeros are multiplied first. */
|
|
211
|
+
static inline double _Complex
|
|
212
|
+
carray_jit_real_multiply_complex (double x, double _Complex b)
|
|
213
|
+
{
|
|
214
|
+
double bre = creal(b), bim = cimag(b);
|
|
215
|
+
double re = x * bre - 0.0 * bim, im = x * bim + 0.0 * bre;
|
|
216
|
+
if ( isnan(re) || isnan(im) ) {
|
|
217
|
+
re = carray_jit_safe_multiply(x, bre) - carray_jit_safe_multiply(0.0, bim);
|
|
218
|
+
im = carray_jit_safe_multiply(x, bim) + carray_jit_safe_multiply(0.0, bre);
|
|
219
|
+
}
|
|
220
|
+
return CMPLX(re, im);
|
|
221
|
+
}
|
|
222
|
+
C
|
|
162
223
|
"complex_mul_real" => <<~C,
|
|
163
|
-
/* `z * x` scales each part
|
|
164
|
-
|
|
224
|
+
/* `z * x` scales each part, with no safe_mul: Ruby's Complex#*
|
|
225
|
+
takes a real operand apart and multiplies each part by it. */
|
|
165
226
|
static inline double _Complex
|
|
166
227
|
carray_jit_complex_mul_real (double _Complex z, double x)
|
|
167
228
|
{
|
|
@@ -256,8 +317,10 @@ class CArray
|
|
|
256
317
|
BORDER_RULES = [:zero, :clamp, :wrap].freeze
|
|
257
318
|
|
|
258
319
|
def initialize (analyzer, storage_types, scalar_types, c_functions: {},
|
|
320
|
+
randoms: {},
|
|
259
321
|
masked: false, reassociate: false,
|
|
260
|
-
steps: nil, origin: nil, block_source: nil, border: nil
|
|
322
|
+
steps: nil, origin: nil, block_source: nil, border: nil,
|
|
323
|
+
scalar_parameters: [])
|
|
261
324
|
@masked = masked
|
|
262
325
|
# What a window read gets where it falls off the array. Nil for every
|
|
263
326
|
# kernel but a stencil's, and for a stencil whose border is the frame
|
|
@@ -282,25 +345,51 @@ class CArray
|
|
|
282
345
|
position += analyzer.array_ranks.fetch(array, analyzer.rank)
|
|
283
346
|
end
|
|
284
347
|
@array_ranks = analyzer.array_ranks
|
|
285
|
-
|
|
286
|
-
|
|
348
|
+
# A compiled function's scalars are the parameters of its own C
|
|
349
|
+
# signature: they arrive by the ABI, in whatever type the declaration
|
|
350
|
+
# named, so no buffer carries them and they take no bus. Set aside
|
|
351
|
+
# here, which leaves the check below about captures -- what it was
|
|
352
|
+
# always about.
|
|
353
|
+
carried_names = analyzer.scalar_names - scalar_parameters
|
|
354
|
+
@reals = carried_names.select { |name| scalar_types[name] == :double }.sort
|
|
355
|
+
@integers = carried_names.select { |name| scalar_types[name] == :int64 }.sort
|
|
287
356
|
# A captured Complex rides in the reals buffer as its two parts, so
|
|
288
357
|
# that the kernel signature stays the one shape every kernel has.
|
|
289
|
-
@complexes =
|
|
290
|
-
#
|
|
291
|
-
#
|
|
292
|
-
#
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
358
|
+
@complexes = carried_names.select { |name| scalar_types[name] == :complex }.sort
|
|
359
|
+
# And a captured uint64 rides in the integers buffer, which is the
|
|
360
|
+
# same eight bytes read the other way: the caller packs the value's
|
|
361
|
+
# bits and the kernel casts the slot back, so nothing is lost and the
|
|
362
|
+
# signature keeps its three buffers. Behind the signed ones, in the
|
|
363
|
+
# order the caller packs them.
|
|
364
|
+
@unsigned_integers =
|
|
365
|
+
carried_names.select { |name| scalar_types[name] == :uint64 }.sort
|
|
366
|
+
# A captured Integer that rides as int64 but meets a uint64, where it
|
|
367
|
+
# takes that width. A negative one has none to take; the caller
|
|
368
|
+
# refuses it, as CArray refuses the same operand (see Kernel).
|
|
369
|
+
@unsigned_captures = []
|
|
370
|
+
# Three buffers and no fourth: a capture whose type rides in none of
|
|
371
|
+
# them would be packed into nothing and read as whatever the slot
|
|
372
|
+
# held, so it is caught here rather than at the cell it computes
|
|
373
|
+
# wrongly.
|
|
374
|
+
carried = @reals.size + @integers.size + @complexes.size +
|
|
375
|
+
@unsigned_integers.size
|
|
376
|
+
unless carried == carried_names.size
|
|
377
|
+
missing = carried_names - @reals - @integers - @complexes -
|
|
378
|
+
@unsigned_integers
|
|
296
379
|
raise Error,
|
|
297
380
|
"captured #{missing.join(", ")} travel in none of the kernel's " \
|
|
298
|
-
"
|
|
381
|
+
"scalar buffers; a computation type was added without a " \
|
|
299
382
|
"way to hand a value of it to the C"
|
|
300
383
|
end
|
|
301
384
|
# Only the ones the block actually called, in a fixed order, because
|
|
302
385
|
# the caller packs the addresses into `functions` by this order.
|
|
303
386
|
@c_functions = analyzer.c_function_names.sort.to_h { |name| [name, c_functions.fetch(name)] }
|
|
387
|
+
# The generators the block drew from, by the name it drew through.
|
|
388
|
+
# Unlike a C function, one takes no slot in any buffer: its C is
|
|
389
|
+
# pasted and its state is an address array, so what is kept here is
|
|
390
|
+
# only which generator each name is -- the kind, to paste, and the
|
|
391
|
+
# symbol to call.
|
|
392
|
+
@randoms = analyzer.random_names.to_h { |name| [name, randoms.fetch(name)] }
|
|
304
393
|
# A function written here is pasted into this kernel and called by its
|
|
305
394
|
# symbol; a borrowed one arrives as an address. Only the second kind
|
|
306
395
|
# takes a slot in `functions`, so it is that hash the caller packs by.
|
|
@@ -330,10 +419,16 @@ class CArray
|
|
|
330
419
|
@uses_clamp = false
|
|
331
420
|
@uses_wrap = false
|
|
332
421
|
@uses_real_arg = false
|
|
422
|
+
@uses_real_power_complex = false
|
|
423
|
+
@clamp_types = []
|
|
424
|
+
@gamma_types = []
|
|
333
425
|
@complex_helpers = []
|
|
334
426
|
@uses_floor_divide = false
|
|
335
427
|
@uses_floor_modulo = false
|
|
428
|
+
@uses_shift = false
|
|
336
429
|
@uses_integer_power = false
|
|
430
|
+
@uses_integer_abs = false
|
|
431
|
+
@uses_whole_number = false
|
|
337
432
|
@uses_unsigned_divide = false
|
|
338
433
|
@uses_unsigned_modulo = false
|
|
339
434
|
@uses_unsigned_power = false
|
|
@@ -350,17 +445,29 @@ class CArray
|
|
|
350
445
|
# this kernel is what answers for them. The codes agree because they
|
|
351
446
|
# are taken from the messages, not counted off.
|
|
352
447
|
@raise_messages = {}
|
|
353
|
-
|
|
448
|
+
pasted_closure.each do |function|
|
|
354
449
|
(function.raise_messages || {}).each do |code, message|
|
|
355
450
|
register_raise(code, message)
|
|
356
451
|
end
|
|
357
452
|
end
|
|
453
|
+
# The intrinsic helpers the body asked for, as [name, storage, cells]
|
|
454
|
+
# -- a list used as a set, the way @clamp_types is. A pasted body's
|
|
455
|
+
# are merged into it by #preamble.
|
|
456
|
+
@intrinsic_needs = []
|
|
457
|
+
# Whether some body pasted into this file clears an array of its own.
|
|
458
|
+
@pasted_clears_a_local_array = false
|
|
358
459
|
@position_temporaries = {}.compare_by_identity
|
|
460
|
+
# The positions a write is being guarded by, by axis, while its body
|
|
461
|
+
# is emitted.
|
|
462
|
+
@guarded_axes = {}.compare_by_identity
|
|
359
463
|
@origin = origin
|
|
360
464
|
@block_source = block_source
|
|
465
|
+
refuse_generated_indices
|
|
361
466
|
end
|
|
362
467
|
|
|
363
|
-
attr_reader :arrays, :reals, :integers, :complexes, :
|
|
468
|
+
attr_reader :arrays, :reals, :integers, :complexes, :unsigned_integers,
|
|
469
|
+
:unsigned_captures,
|
|
470
|
+
:masked, :extent_slots,
|
|
364
471
|
:c_functions, :pasted_functions, :address_functions,
|
|
365
472
|
:address_arrays, :address_parameters,
|
|
366
473
|
# The messages `raise` in the block gave, by the code a cell
|
|
@@ -373,6 +480,14 @@ class CArray
|
|
|
373
480
|
@uses_error_flag
|
|
374
481
|
end
|
|
375
482
|
|
|
483
|
+
# Whether some array the body made is one of the zeroed spellings, which
|
|
484
|
+
# is what `<string.h>` is included for. A pasted function's body is
|
|
485
|
+
# emitted into this file too, and when a function body may make one of
|
|
486
|
+
# its own this has to ask the pasted ones as well.
|
|
487
|
+
def clears_a_local_array?
|
|
488
|
+
@analyzer.clears_a_local_array? || @pasted_clears_a_local_array
|
|
489
|
+
end
|
|
490
|
+
|
|
376
491
|
def generate
|
|
377
492
|
strided = build_body(false)
|
|
378
493
|
contiguous = build_body(true)
|
|
@@ -409,11 +524,17 @@ class CArray
|
|
|
409
524
|
# library handed the address calls without knowing about any of this.
|
|
410
525
|
def generate_function (name, parameter_names, parameter_c_types,
|
|
411
526
|
return_c_type, return_type, error_parameter: false)
|
|
412
|
-
|
|
527
|
+
# Standing alone, a body that turns out to be able to fail is lent
|
|
528
|
+
# under a wrapper (see #lending_wrapper), and the body itself goes
|
|
529
|
+
# under a name of its own. Whether it can fail is known only once it
|
|
530
|
+
# has been emitted, so a recursive call is written to that name from
|
|
531
|
+
# the start and put back to `name` below where no wrapper came.
|
|
532
|
+
body_symbol = "#{name}#{BODY_SUFFIX}"
|
|
533
|
+
@own_symbol = error_parameter ? name : body_symbol
|
|
413
534
|
@in_function = true
|
|
414
535
|
@error_parameter = error_parameter
|
|
536
|
+
@body_reports = false
|
|
415
537
|
@contiguous = true
|
|
416
|
-
@declared_locals = {}
|
|
417
538
|
@temporary_count = 0
|
|
418
539
|
@carried_masks = []
|
|
419
540
|
statements = @analyzer.body.statements
|
|
@@ -422,7 +543,9 @@ class CArray
|
|
|
422
543
|
@returns_nothing = return_type.nil?
|
|
423
544
|
held = @returns_nothing ? statements : statements[0..-2]
|
|
424
545
|
lines = held.map { |statement| emit_statement(statement, " ") }.join
|
|
546
|
+
lines = local_declarations(@analyzer.body, " ") + lines
|
|
425
547
|
value = @returns_nothing ? nil : emit(statements.last, return_type)
|
|
548
|
+
parameter_names = parameter_names.map { |parameter| parameter_c_name(parameter) }
|
|
426
549
|
parameters = parameter_names.zip(parameter_c_types)
|
|
427
550
|
.map { |parameter, type| type.declare(parameter) }
|
|
428
551
|
# Passed whether or not the body turned out to use it, so that a
|
|
@@ -430,48 +553,205 @@ class CArray
|
|
|
430
553
|
# same argument list the definition ends up declaring.
|
|
431
554
|
parameters << "int32_t *#{ERROR_FLAG}" if error_parameter
|
|
432
555
|
parameters = ["void"] if parameters.empty?
|
|
433
|
-
|
|
556
|
+
wrapped = @uses_error_flag && !error_parameter
|
|
557
|
+
unless wrapped || error_parameter
|
|
558
|
+
lines = lines.gsub("#{body_symbol}(", "#{name}(")
|
|
559
|
+
value = value&.gsub("#{body_symbol}(", "#{name}(")
|
|
560
|
+
end
|
|
561
|
+
flag = if wrapped
|
|
562
|
+
error_flag_declaration + error_hook_declaration +
|
|
563
|
+
error_standin_declaration + error_state_accessor(name)
|
|
564
|
+
else
|
|
565
|
+
""
|
|
566
|
+
end
|
|
434
567
|
# Kept apart from the file it is compiled in: the definition alone is
|
|
435
568
|
# what a kernel pastes into its own translation unit, where the
|
|
436
569
|
# includes are already written and the helpers are shared.
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
preamble + flag +
|
|
570
|
+
opening = "#{return_c_type}\n#{name} (#{parameters.join(', ')})\n{\n"
|
|
571
|
+
body = lines + (@returns_nothing ? "}\n" : " return #{value};\n}\n")
|
|
572
|
+
@function_definition = opening + body
|
|
573
|
+
return preamble + flag + opening + body unless wrapped
|
|
574
|
+
preamble + flag +
|
|
575
|
+
"static #{return_c_type}\n#{body_symbol} (#{parameters.join(', ')})\n{\n" +
|
|
576
|
+
standing_gate + body + "\n" +
|
|
577
|
+
lending_wrapper(name, body_symbol, parameter_names, parameters,
|
|
578
|
+
return_c_type)
|
|
579
|
+
end
|
|
580
|
+
|
|
581
|
+
# What the body is defined under when a wrapper is lent in its place.
|
|
582
|
+
# Static, so the object still exports one function.
|
|
583
|
+
BODY_SUFFIX = "_body".freeze
|
|
584
|
+
|
|
585
|
+
# The hook `CFunction#on_error` sets: a C function of one `void *`,
|
|
586
|
+
# and the pointer it is handed. Beside the flag, and only where the
|
|
587
|
+
# flag is, since a body that cannot fail has nothing to call it for.
|
|
588
|
+
ERROR_HOOK = "carray_jit_on_error".freeze
|
|
589
|
+
ERROR_HOOK_DATA = "carray_jit_on_error_data".freeze
|
|
590
|
+
|
|
591
|
+
def error_hook_declaration
|
|
592
|
+
"/* Called once, by the call whose body put the flag up -- so a\n" \
|
|
593
|
+
" library that goes on calling can be told to stop (NLopt's\n" \
|
|
594
|
+
" nlopt_force_stop) rather than be handed stand-ins until it is\n" \
|
|
595
|
+
" done. Set by CFunction#on_error; null until then. Per thread,\n" \
|
|
596
|
+
" for the reason the flag beside it is. */\n" \
|
|
597
|
+
"static #{THREAD_LOCAL}void (*#{ERROR_HOOK}) (void *) = 0;\n" \
|
|
598
|
+
"static #{THREAD_LOCAL}void *#{ERROR_HOOK_DATA} = 0;\n\n"
|
|
599
|
+
end
|
|
600
|
+
|
|
601
|
+
# What a failed call answers C with. Set by the window through
|
|
602
|
+
# `stand_in:`, and 0 until then, which is what this always was.
|
|
603
|
+
ERROR_STANDIN = "carray_jit_standin".freeze
|
|
604
|
+
|
|
605
|
+
def error_standin_declaration
|
|
606
|
+
"/* What a call answers with once the flag stands -- cast to whatever\n" \
|
|
607
|
+
" this body returns. A caller that reads the flag never looks at\n" \
|
|
608
|
+
" it; one that reads the return value as a status does, and only\n" \
|
|
609
|
+
" the caller knows which it is, so the window sets it. 0 until a\n" \
|
|
610
|
+
" window says otherwise, which is what it always answered. */\n" \
|
|
611
|
+
"static #{THREAD_LOCAL}int64_t #{ERROR_STANDIN} = 0;\n\n"
|
|
612
|
+
end
|
|
613
|
+
|
|
614
|
+
# Where a thread's copy of the four above stands. A thread-local has
|
|
615
|
+
# no address the loader can hand out -- `dlsym` answers for the symbol,
|
|
616
|
+
# not for any one thread's copy of it -- so Ruby asks the object itself,
|
|
617
|
+
# from the thread whose window it is.
|
|
618
|
+
#
|
|
619
|
+
# It is the only one of the five that is exported, and it wears the
|
|
620
|
+
# body's own name, so that this file can be linked beside another one
|
|
621
|
+
# generated the same way. One object per body is what the cache makes
|
|
622
|
+
# and what these names once assumed; carray-jit-aot takes the C and
|
|
623
|
+
# links several into one library, where a fixed name is a duplicate
|
|
624
|
+
# symbol and the link fails. The four it answers for are `static` for
|
|
625
|
+
# the same reason -- nothing outside this file reaches them by name,
|
|
626
|
+
# Ruby included, which is what this function is for.
|
|
627
|
+
ERROR_STATE_SUFFIX = "_error_state".freeze
|
|
628
|
+
|
|
629
|
+
# What Ruby looks the accessor up under, for a body compiled as
|
|
630
|
+
# `symbol`.
|
|
631
|
+
def self.error_state_name (symbol)
|
|
632
|
+
"#{symbol}#{ERROR_STATE_SUFFIX}"
|
|
633
|
+
end
|
|
634
|
+
|
|
635
|
+
def error_state_accessor (name)
|
|
636
|
+
"/* The four above, for the calling thread, in that order. Called\n" \
|
|
637
|
+
" from Ruby once per thread that opens a window on this body. */\n" \
|
|
638
|
+
"void\n#{CGenerator.error_state_name(name)} (void **out)\n{\n" \
|
|
639
|
+
" out[0] = (void *) &#{ERROR_FLAG};\n" \
|
|
640
|
+
" out[1] = (void *) &#{ERROR_HOOK};\n" \
|
|
641
|
+
" out[2] = (void *) &#{ERROR_HOOK_DATA};\n" \
|
|
642
|
+
" out[3] = (void *) &#{ERROR_STANDIN};\n}\n\n"
|
|
643
|
+
end
|
|
644
|
+
|
|
645
|
+
# The entry point the address is, for a body that can fail. It turns
|
|
646
|
+
# a call away while the flag stands, as the body's own gate does, and
|
|
647
|
+
# calls the hook when this call is the one that raised it -- which is
|
|
648
|
+
# once, since every call after it is turned away at the top. The hook
|
|
649
|
+
# is outside the body so that each way the flag can go up, a `raise`
|
|
650
|
+
# that leaves early or a division that runs on to the end, is the same
|
|
651
|
+
# one test afterwards.
|
|
652
|
+
#
|
|
653
|
+
# It is also the one place that says what a failed call answers C with.
|
|
654
|
+
# The body has several ways of ending -- a `raise` returns early, a
|
|
655
|
+
# division that had no divisor runs on to the end and returns whatever
|
|
656
|
+
# the zero the helper handed back added up to -- and a caller reading
|
|
657
|
+
# the return value as a status cannot be given a different answer
|
|
658
|
+
# depending on which. So the gates inside the body go on returning
|
|
659
|
+
# zero to their own caller, which is this function or the body itself
|
|
660
|
+
# recursing, and what C is told is decided here, once, from the flag.
|
|
661
|
+
def lending_wrapper (name, body_symbol, parameter_names, parameters,
|
|
662
|
+
return_c_type)
|
|
663
|
+
arguments = parameter_names.join(", ")
|
|
664
|
+
if @returns_nothing
|
|
665
|
+
"#{return_c_type}\n#{name} (#{parameters.join(', ')})\n{\n" \
|
|
666
|
+
" if ( #{ERROR_FLAG} ) return;\n" \
|
|
667
|
+
" #{body_symbol}(#{arguments});\n" \
|
|
668
|
+
" if ( #{ERROR_FLAG} && #{ERROR_HOOK} ) " \
|
|
669
|
+
"#{ERROR_HOOK}(#{ERROR_HOOK_DATA});\n}\n"
|
|
670
|
+
else
|
|
671
|
+
standin = "(#{return_c_type}) #{ERROR_STANDIN}"
|
|
672
|
+
"#{return_c_type}\n#{name} (#{parameters.join(', ')})\n{\n" \
|
|
673
|
+
" if ( #{ERROR_FLAG} ) return #{standin};\n" \
|
|
674
|
+
" #{return_c_type} value = #{body_symbol}(#{arguments});\n" \
|
|
675
|
+
" if ( #{ERROR_FLAG} ) {\n" \
|
|
676
|
+
" if ( #{ERROR_HOOK} ) #{ERROR_HOOK}(#{ERROR_HOOK_DATA});\n" \
|
|
677
|
+
" return #{standin};\n" \
|
|
678
|
+
" }\n" \
|
|
679
|
+
" return value;\n}\n"
|
|
680
|
+
end
|
|
441
681
|
end
|
|
442
682
|
|
|
443
683
|
attr_reader :function_definition
|
|
444
684
|
|
|
685
|
+
# A body standing on its own does no more work once its flag stands.
|
|
686
|
+
#
|
|
687
|
+
# The `!ERROR_FLAG` at each `raise` keeps a later report from overwriting
|
|
688
|
+
# an earlier one, and that is enough for a caller that stops: a kernel
|
|
689
|
+
# ends its loop, and `CFunction#call` was one call to begin with. A
|
|
690
|
+
# library handed the address does not stop. It calls again, and without
|
|
691
|
+
# this the body skips the report it has already made and answers as
|
|
692
|
+
# though nothing had happened -- so an adaptive routine converges on
|
|
693
|
+
# values that are fiction, which is worse than a wrong answer because it
|
|
694
|
+
# looks like a right one.
|
|
695
|
+
#
|
|
696
|
+
# What the gate says is that the body does no more work. It does not
|
|
697
|
+
# say what the call is worth: the flag is the contract, and the zero
|
|
698
|
+
# goes to this body's own caller -- the lending wrapper, or the body
|
|
699
|
+
# itself recursing -- rather than to C, which is told whatever the
|
|
700
|
+
# window chose. A `void` body has no zero to offer and leaves its
|
|
701
|
+
# out-parameters alone, which is the same refusal to work.
|
|
702
|
+
#
|
|
703
|
+
# It goes in the file and not in the definition, so that the form a
|
|
704
|
+
# kernel pastes cannot pick it up whichever way the two forms are
|
|
705
|
+
# generated. A kernel reads its slot once, after a loop it stopped
|
|
706
|
+
# itself, and has no use for it.
|
|
707
|
+
def standing_gate
|
|
708
|
+
return "" unless @uses_error_flag && !@error_parameter
|
|
709
|
+
" if ( #{ERROR_FLAG} ) #{@returns_nothing ? "return;" : "return 0;"}\n\n"
|
|
710
|
+
end
|
|
711
|
+
|
|
445
712
|
# What the body took from the preamble, so that a definition pasted
|
|
446
713
|
# somewhere else can be given the same helpers. Only the ones a pasted
|
|
447
714
|
# function can want are here: the rest -- the index check, the two
|
|
448
715
|
# flooring helpers -- report through the error flag, and a function that
|
|
449
716
|
# touches the flag is not pasted at all.
|
|
450
717
|
def helper_needs
|
|
451
|
-
{ :
|
|
718
|
+
{ :intrinsics => @intrinsic_needs.dup,
|
|
719
|
+
:clears_a_local_array => clears_a_local_array?,
|
|
720
|
+
:integer_power => @uses_integer_power,
|
|
721
|
+
:integer_abs => @uses_integer_abs,
|
|
722
|
+
:whole_number => @uses_whole_number,
|
|
452
723
|
:unsigned_divide => @uses_unsigned_divide,
|
|
453
724
|
:unsigned_modulo => @uses_unsigned_modulo,
|
|
454
725
|
:unsigned_power => @uses_unsigned_power,
|
|
455
726
|
:floor_modulo_float => @uses_floor_modulo_float,
|
|
456
727
|
:real_arg => @uses_real_arg,
|
|
728
|
+
:real_power_complex => @uses_real_power_complex,
|
|
729
|
+
:clamp => @clamp_types.dup,
|
|
730
|
+
:gamma => @gamma_types.dup,
|
|
457
731
|
:complex => @complex_helpers.dup,
|
|
458
732
|
:index_check => @uses_index_check,
|
|
459
733
|
:floor_divide => @uses_floor_divide,
|
|
460
|
-
:floor_modulo => @uses_floor_modulo
|
|
734
|
+
:floor_modulo => @uses_floor_modulo,
|
|
735
|
+
:shift => @uses_shift }
|
|
461
736
|
end
|
|
462
737
|
|
|
463
|
-
#
|
|
464
|
-
#
|
|
465
|
-
#
|
|
466
|
-
#
|
|
467
|
-
#
|
|
738
|
+
# Per compiled object and per thread, and zeroed by whoever is about
|
|
739
|
+
# to look -- the same discipline the kernel's own error slot keeps. It
|
|
740
|
+
# is written only where the value returned is already a stand-in, so a
|
|
741
|
+
# caller that never looks is no worse off than C leaves it.
|
|
742
|
+
#
|
|
743
|
+
# `_Thread_local` because a window is one thread's: the object is shared
|
|
744
|
+
# whether or not anybody meant to share it -- two blocks with the same
|
|
745
|
+
# text compile to one body -- and a single flag between two threads is a
|
|
746
|
+
# window neither of them can read. It costs about half a nanosecond a
|
|
747
|
+
# call, measured, and only a body that can fail has one at all.
|
|
468
748
|
def error_flag_declaration
|
|
469
749
|
"/* Standing at 1 when a division had no divisor, and at the code of\n" \
|
|
470
750
|
" a `raise` in the body where one was reached -- the numbers a\n" \
|
|
471
751
|
" kernel reports through its own slot, with the same meanings. A\n" \
|
|
472
752
|
" subscript on a pointer parameter is not checked here and does not\n" \
|
|
473
753
|
" appear: it is the caller's business, as it is in C. */\n" \
|
|
474
|
-
"int32_t #{ERROR_FLAG} = 0;\n\n"
|
|
754
|
+
"static #{THREAD_LOCAL}int32_t #{ERROR_FLAG} = 0;\n\n"
|
|
475
755
|
end
|
|
476
756
|
|
|
477
757
|
# The same kernel, wrapped so carray can drive it.
|
|
@@ -547,27 +827,66 @@ class CArray
|
|
|
547
827
|
# A pasted body wants the same helpers here that it had in its own
|
|
548
828
|
# file, and it is emitted below them, so this is asked before any of
|
|
549
829
|
# them is written out.
|
|
550
|
-
|
|
830
|
+
pasted_closure.each do |function|
|
|
551
831
|
needs = function.helpers || {}
|
|
552
832
|
@uses_integer_power ||= needs[:integer_power]
|
|
833
|
+
@uses_integer_abs ||= needs[:integer_abs]
|
|
834
|
+
@uses_whole_number ||= needs[:whole_number]
|
|
553
835
|
@uses_unsigned_divide ||= needs[:unsigned_divide]
|
|
554
836
|
@uses_unsigned_modulo ||= needs[:unsigned_modulo]
|
|
555
837
|
@uses_unsigned_power ||= needs[:unsigned_power]
|
|
556
838
|
@uses_floor_modulo_float ||= needs[:floor_modulo_float]
|
|
557
839
|
@uses_real_arg ||= needs[:real_arg]
|
|
840
|
+
@uses_real_power_complex ||= needs[:real_power_complex]
|
|
841
|
+
@clamp_types |= needs[:clamp] || []
|
|
842
|
+
@gamma_types |= needs[:gamma] || []
|
|
558
843
|
@complex_helpers |= needs[:complex] || []
|
|
559
844
|
@uses_index_check ||= needs[:index_check]
|
|
560
845
|
@uses_floor_divide ||= needs[:floor_divide]
|
|
561
846
|
@uses_floor_modulo ||= needs[:floor_modulo]
|
|
847
|
+
@uses_shift ||= needs[:shift]
|
|
848
|
+
# A union rather than an or: two bodies may want a `sort` of one
|
|
849
|
+
# type and length and a `sum` of another, and the preamble owes one
|
|
850
|
+
# helper for each. The same shape `:clamp` and `:gamma` take.
|
|
851
|
+
@intrinsic_needs |= needs[:intrinsics] || []
|
|
852
|
+
# And whether anything anywhere in this file clears an array of its
|
|
853
|
+
# own, which is what `<string.h>` is included for. A pasted body's
|
|
854
|
+
# `memset` is in this translation unit, so it is this file's header.
|
|
855
|
+
@pasted_clears_a_local_array ||= !!needs[:clears_a_local_array]
|
|
562
856
|
end
|
|
563
|
-
|
|
564
|
-
|
|
857
|
+
# `stddef.h` is here for the signature's sake rather than the body's:
|
|
858
|
+
# a declaration may be written with `size_t` or `ptrdiff_t`, and that
|
|
859
|
+
# is the header those come from. `stdio.h` happens to declare
|
|
860
|
+
# `size_t` as well, which is why the omission went unnoticed until
|
|
861
|
+
# `ptrdiff_t` was written down.
|
|
862
|
+
text = +"#include <stdint.h>\n#include <stddef.h>\n" \
|
|
863
|
+
"#include <math.h>\n#include <complex.h>\n" \
|
|
864
|
+
"#include <stdio.h>\n"
|
|
865
|
+
# `memset`, and only where a body clears an array of its own. A
|
|
866
|
+
# kernel that clears nothing generates what it always did.
|
|
867
|
+
text << "#include <string.h>\n" if clears_a_local_array?
|
|
868
|
+
# malloc and free, for the arrays this kernel allocates. libc, the
|
|
869
|
+
# same ground `memset` above stands on: it needs no GVL and touches
|
|
870
|
+
# no Ruby value, which is what kept `ALLOCV` out of here.
|
|
871
|
+
text << "#include <stdlib.h>\n" unless heap_arrays.empty?
|
|
872
|
+
text << "\n"
|
|
565
873
|
unless @address_functions.empty?
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
text <<
|
|
874
|
+
if @in_function
|
|
875
|
+
# A body standing on its own calls a borrowed function by its
|
|
876
|
+
# name, so what it needs is that function's own declaration --
|
|
877
|
+
# the one its author wrote -- and a linker.
|
|
878
|
+
text << "/* Declared where they are called from, and resolved " \
|
|
879
|
+
"by the linker\n or the loader. */\n"
|
|
880
|
+
@address_functions.each_value do |function|
|
|
881
|
+
text << "#{function.prototype};\n"
|
|
882
|
+
end
|
|
883
|
+
else
|
|
884
|
+
text << "/* The C functions the block called. They arrive as\n" \
|
|
885
|
+
" addresses rather than by linkage, so nothing here says\n" \
|
|
886
|
+
" which library they came from. */\n"
|
|
887
|
+
@address_functions.each_key do |name|
|
|
888
|
+
text << @address_functions.fetch(name).c_declaration(c_function_type_name(name)) << "\n"
|
|
889
|
+
end
|
|
571
890
|
end
|
|
572
891
|
text << "\n"
|
|
573
892
|
end
|
|
@@ -589,14 +908,15 @@ class CArray
|
|
|
589
908
|
computing wide and narrowing afterwards stops agreeing with
|
|
590
909
|
computing narrow. */
|
|
591
910
|
static inline float
|
|
592
|
-
carray_jit_floor_modulo_float (float numerator, float denominator)
|
|
911
|
+
carray_jit_floor_modulo_float (float numerator, float denominator, int32_t *error)
|
|
593
912
|
{
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
913
|
+
if ( denominator == 0 ) {
|
|
914
|
+
if ( error ) *error = 1;
|
|
915
|
+
return 0;
|
|
597
916
|
}
|
|
598
|
-
|
|
599
|
-
|
|
917
|
+
float remainder = fmodf(numerator, denominator);
|
|
918
|
+
if ( remainder != 0 && (remainder < 0) != (denominator < 0) ) {
|
|
919
|
+
remainder += denominator;
|
|
600
920
|
}
|
|
601
921
|
return remainder;
|
|
602
922
|
}
|
|
@@ -677,6 +997,56 @@ class CArray
|
|
|
677
997
|
|
|
678
998
|
C
|
|
679
999
|
end
|
|
1000
|
+
if @uses_whole_number
|
|
1001
|
+
text << <<~C
|
|
1002
|
+
/* `floor`, `ceil`, `round` and `truncate` on a Float answer an
|
|
1003
|
+
Integer in Ruby, and a NaN or an infinity has none to answer:
|
|
1004
|
+
Ruby raises FloatDomainError. A number past int64 has one, but
|
|
1005
|
+
not in an int64_t, where the cast is undefined -- CArray raises
|
|
1006
|
+
RangeError storing such an Integer, and so does this. The real
|
|
1007
|
+
one is for a value that stays a number: 1e20.floor into a
|
|
1008
|
+
float64 cell is 1e20 in Ruby, and does not pass through int64.
|
|
1009
|
+
Adding zero clears the sign of a zero: (-0.3).ceil is the
|
|
1010
|
+
Integer 0, which has none, where ceil(-0.3) is -0.0. */
|
|
1011
|
+
static inline double
|
|
1012
|
+
carray_jit_whole_real (double value, int32_t *error)
|
|
1013
|
+
{
|
|
1014
|
+
if ( isnan(value) ) {
|
|
1015
|
+
if ( error ) *error = #{FLOAT_NAN_CODE};
|
|
1016
|
+
}
|
|
1017
|
+
else if ( isinf(value) ) {
|
|
1018
|
+
if ( error ) *error = value > 0 ? #{FLOAT_INFINITY_CODE} : #{FLOAT_NEGATIVE_INFINITY_CODE};
|
|
1019
|
+
}
|
|
1020
|
+
return value + 0.0;
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
static inline int64_t
|
|
1024
|
+
carray_jit_whole_int64 (double value, int32_t *error)
|
|
1025
|
+
{
|
|
1026
|
+
if ( ! ( value >= -9223372036854775808.0 && value < 9223372036854775808.0 ) ) {
|
|
1027
|
+
carray_jit_whole_real(value, error);
|
|
1028
|
+
if ( error && ! isnan(value) && ! isinf(value) ) *error = #{INTEGER_RANGE_CODE};
|
|
1029
|
+
return 0;
|
|
1030
|
+
}
|
|
1031
|
+
return (int64_t) value;
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
C
|
|
1035
|
+
end
|
|
1036
|
+
if @uses_integer_abs
|
|
1037
|
+
text << <<~C
|
|
1038
|
+
/* An integer's magnitude, without llabs: that is <stdlib.h>'s,
|
|
1039
|
+
which a kernel includes only when it allocates, and it leaves
|
|
1040
|
+
INT64_MIN undefined. Negated through uint64_t, INT64_MIN
|
|
1041
|
+
wraps to itself -- the wrap an int64 cell has everywhere else. */
|
|
1042
|
+
static inline int64_t
|
|
1043
|
+
carray_jit_integer_abs (int64_t value)
|
|
1044
|
+
{
|
|
1045
|
+
return value < 0 ? (int64_t) (0 - (uint64_t) value) : value;
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
C
|
|
1049
|
+
end
|
|
680
1050
|
COMPLEX_HELPERS.each do |name, definition|
|
|
681
1051
|
[false, true].each do |narrow|
|
|
682
1052
|
next unless @complex_helpers.include?([name, narrow])
|
|
@@ -684,6 +1054,76 @@ class CArray
|
|
|
684
1054
|
: definition) << "\n"
|
|
685
1055
|
end
|
|
686
1056
|
end
|
|
1057
|
+
@gamma_types.each do |type|
|
|
1058
|
+
suffix, c_type, gamma, modf = GAMMA_C_TYPES.fetch(type)
|
|
1059
|
+
table = (1..GAMMA_TABLE_LIMIT).map { |whole|
|
|
1060
|
+
format_float(Math.gamma(whole.to_f), type)
|
|
1061
|
+
}
|
|
1062
|
+
text << <<~C
|
|
1063
|
+
/* `Math.gamma` is tgamma with Ruby's two answers around it: a
|
|
1064
|
+
whole number up to #{GAMMA_TABLE_LIMIT} is answered from the table Ruby
|
|
1065
|
+
answers it from, and a negative whole number -- where tgamma
|
|
1066
|
+
gives a NaN -- is the domain error Ruby raises. The values
|
|
1067
|
+
are the ones the Ruby that compiled this holds. */
|
|
1068
|
+
static const #{c_type} carray_jit_gamma_table_#{suffix}[#{GAMMA_TABLE_LIMIT}] = {
|
|
1069
|
+
#{table.each_slice(4).map { |row| row.join(", ") }.join(",\n ")}
|
|
1070
|
+
};
|
|
1071
|
+
|
|
1072
|
+
static inline #{c_type}
|
|
1073
|
+
carray_jit_gamma_#{suffix} (#{c_type} x, int32_t *error)
|
|
1074
|
+
{
|
|
1075
|
+
if ( isinf(x) ) {
|
|
1076
|
+
if ( signbit(x) ) {
|
|
1077
|
+
if ( error ) *error = 5;
|
|
1078
|
+
return 0;
|
|
1079
|
+
}
|
|
1080
|
+
return x;
|
|
1081
|
+
}
|
|
1082
|
+
#{c_type} whole;
|
|
1083
|
+
if ( #{modf}(x, &whole) == 0 ) {
|
|
1084
|
+
if ( whole < 0 ) {
|
|
1085
|
+
if ( error ) *error = 5;
|
|
1086
|
+
return 0;
|
|
1087
|
+
}
|
|
1088
|
+
if ( 0 < whole && whole <= #{GAMMA_TABLE_LIMIT} ) {
|
|
1089
|
+
return carray_jit_gamma_table_#{suffix}[(int)whole - 1];
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
return #{gamma}(x);
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
C
|
|
1096
|
+
end
|
|
1097
|
+
text << intrinsic_helpers
|
|
1098
|
+
@clamp_types.each do |type|
|
|
1099
|
+
suffix, c_type, has_nan = CLAMP_C_TYPES.fetch(type)
|
|
1100
|
+
refusal = lambda { |test, code|
|
|
1101
|
+
" if ( #{test} ) {\n" \
|
|
1102
|
+
" if ( error ) *error = #{code};\n" \
|
|
1103
|
+
" return value;\n" \
|
|
1104
|
+
" }\n"
|
|
1105
|
+
}
|
|
1106
|
+
tests = +""
|
|
1107
|
+
tests << refusal.call("isnan(low) || isnan(high)", 4) if has_nan
|
|
1108
|
+
tests << refusal.call("low > high", 3)
|
|
1109
|
+
tests << refusal.call("isnan(value)", 4) if has_nan
|
|
1110
|
+
text << <<~C
|
|
1111
|
+
/* `x.clamp(low, high)`, and the two things Ruby raises
|
|
1112
|
+
ArgumentError for: bounds the wrong way round, and a
|
|
1113
|
+
comparison that cannot be made because a NaN is in it. The
|
|
1114
|
+
order is Ruby's -- the bounds are ordered against each other
|
|
1115
|
+
before the value is looked at. */
|
|
1116
|
+
static inline #{c_type}
|
|
1117
|
+
carray_jit_clamp_#{suffix} (#{c_type} value, #{c_type} low, #{c_type} high, int32_t *error)
|
|
1118
|
+
{
|
|
1119
|
+
#{tests.chomp}
|
|
1120
|
+
if ( value < low ) return low;
|
|
1121
|
+
if ( value > high ) return high;
|
|
1122
|
+
return value;
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
C
|
|
1126
|
+
end
|
|
687
1127
|
if @uses_real_arg
|
|
688
1128
|
text << <<~C
|
|
689
1129
|
/* The argument of a real number, which Ruby reads off the sign
|
|
@@ -698,6 +1138,33 @@ class CArray
|
|
|
698
1138
|
|
|
699
1139
|
C
|
|
700
1140
|
end
|
|
1141
|
+
if @uses_real_power_complex
|
|
1142
|
+
text << <<~C
|
|
1143
|
+
/* A real power read as complex. Ruby answers a negative base
|
|
1144
|
+
raised to a power that is not an integer with a Complex, in
|
|
1145
|
+
polar form: (-x)**y at the angle y*pi, exact on the axes.
|
|
1146
|
+
Anything else is the real power. */
|
|
1147
|
+
static double _Complex
|
|
1148
|
+
carray_jit_real_power_complex (double x, double y)
|
|
1149
|
+
{
|
|
1150
|
+
double fi, fr, a;
|
|
1151
|
+
if ( ! ( x < 0.0 ) || y == round(y) ) return CMPLX(pow(x, y), 0.0);
|
|
1152
|
+
a = pow(-x, y);
|
|
1153
|
+
fr = modf(y, &fi);
|
|
1154
|
+
if ( fr == 0.5 || fr == -0.5 ) {
|
|
1155
|
+
int up = ( fr == 0.5 );
|
|
1156
|
+
if ( ( modf(fi / 2.0, &fi) != fr ) ^ up ) a = -a;
|
|
1157
|
+
return CMPLX(0.0, a);
|
|
1158
|
+
}
|
|
1159
|
+
#if defined(__APPLE__)
|
|
1160
|
+
return CMPLX(a * __cospi(y), a * __sinpi(y));
|
|
1161
|
+
#else
|
|
1162
|
+
return CMPLX(a * cos(y * M_PI), a * sin(y * M_PI));
|
|
1163
|
+
#endif
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
C
|
|
1167
|
+
end
|
|
701
1168
|
if @uses_clamp
|
|
702
1169
|
text << <<~C
|
|
703
1170
|
/* A window that falls off the array reads the nearest cell it
|
|
@@ -754,10 +1221,15 @@ class CArray
|
|
|
754
1221
|
/* Ruby's `%` floors with its division: the remainder carries the
|
|
755
1222
|
sign of the divisor, where C's `%` and fmod carry the sign of
|
|
756
1223
|
the dividend. So add the divisor back when the remainder is
|
|
757
|
-
non-zero and disagrees with it in sign
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
1224
|
+
non-zero and disagrees with it in sign. A zero remainder keeps
|
|
1225
|
+
fmod's sign, the dividend's, as Ruby's Float#% does: -4.0 % 2.0
|
|
1226
|
+
is -0.0. (CArray's own `%` gives it the divisor's sign instead;
|
|
1227
|
+
a block means what it means in Ruby.)
|
|
1228
|
+
|
|
1229
|
+
A zero divisor is Ruby's rather than CArray's: `3.0 % 0.0` raises
|
|
1230
|
+
ZeroDivisionError in Ruby, a float divisor as much as an integer
|
|
1231
|
+
one, where fmod and CArray answer NaN. `3.0 / 0.0` is not that
|
|
1232
|
+
case -- an infinity in Ruby as in C -- and is left alone. */
|
|
761
1233
|
static inline int64_t
|
|
762
1234
|
carray_jit_floor_modulo (int64_t numerator, int64_t denominator, int32_t *error)
|
|
763
1235
|
{
|
|
@@ -765,6 +1237,7 @@ class CArray
|
|
|
765
1237
|
if ( error ) *error = 1;
|
|
766
1238
|
return 0;
|
|
767
1239
|
}
|
|
1240
|
+
if ( denominator == -1 ) return 0;
|
|
768
1241
|
int64_t remainder = numerator % denominator;
|
|
769
1242
|
if ( remainder != 0 && ((remainder < 0) != (denominator < 0)) ) {
|
|
770
1243
|
remainder += denominator;
|
|
@@ -773,20 +1246,69 @@ class CArray
|
|
|
773
1246
|
}
|
|
774
1247
|
|
|
775
1248
|
static inline double
|
|
776
|
-
carray_jit_floor_modulo_real (double numerator, double denominator)
|
|
1249
|
+
carray_jit_floor_modulo_real (double numerator, double denominator, int32_t *error)
|
|
777
1250
|
{
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
1251
|
+
if ( denominator == 0 ) {
|
|
1252
|
+
if ( error ) *error = 1;
|
|
1253
|
+
return 0;
|
|
781
1254
|
}
|
|
782
|
-
|
|
783
|
-
|
|
1255
|
+
double remainder = fmod(numerator, denominator);
|
|
1256
|
+
if ( remainder != 0 && (remainder < 0) != (denominator < 0) ) {
|
|
1257
|
+
remainder += denominator;
|
|
784
1258
|
}
|
|
785
1259
|
return remainder;
|
|
786
1260
|
}
|
|
787
1261
|
|
|
788
1262
|
C
|
|
789
1263
|
end
|
|
1264
|
+
if @uses_shift
|
|
1265
|
+
text << <<~C
|
|
1266
|
+
/* Integer#<< and #>>: a negative count shifts the other way, and a
|
|
1267
|
+
count of 64 or more shifts every bit out -- to 0, or to -1 for a
|
|
1268
|
+
negative value shifted right. C leaves both undefined, and a
|
|
1269
|
+
left shift of a negative value too, so the left shift is made in
|
|
1270
|
+
uint64_t. A count held unsigned is brought into int64_t range
|
|
1271
|
+
first; past 64 they all mean the same. */
|
|
1272
|
+
static inline int64_t
|
|
1273
|
+
carray_jit_shift_count (uint64_t count)
|
|
1274
|
+
{
|
|
1275
|
+
return count > 64 ? 64 : (int64_t) count;
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
static inline int64_t
|
|
1279
|
+
carray_jit_shift_left (int64_t value, int64_t count)
|
|
1280
|
+
{
|
|
1281
|
+
if ( count >= 64 ) return 0;
|
|
1282
|
+
if ( count <= -64 ) return value < 0 ? -1 : 0;
|
|
1283
|
+
if ( count < 0 ) return value >> -count;
|
|
1284
|
+
return (int64_t) ((uint64_t) value << count);
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
static inline int64_t
|
|
1288
|
+
carray_jit_shift_right (int64_t value, int64_t count)
|
|
1289
|
+
{
|
|
1290
|
+
if ( count >= 64 ) return value < 0 ? -1 : 0;
|
|
1291
|
+
if ( count <= -64 ) return 0;
|
|
1292
|
+
if ( count < 0 ) return (int64_t) ((uint64_t) value << -count);
|
|
1293
|
+
return value >> count;
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
static inline uint64_t
|
|
1297
|
+
carray_jit_unsigned_shift_left (uint64_t value, int64_t count)
|
|
1298
|
+
{
|
|
1299
|
+
if ( count >= 64 || count <= -64 ) return 0;
|
|
1300
|
+
return count < 0 ? value >> -count : value << count;
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
static inline uint64_t
|
|
1304
|
+
carray_jit_unsigned_shift_right (uint64_t value, int64_t count)
|
|
1305
|
+
{
|
|
1306
|
+
if ( count >= 64 || count <= -64 ) return 0;
|
|
1307
|
+
return count < 0 ? value << -count : value >> count;
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
C
|
|
1311
|
+
end
|
|
790
1312
|
if @uses_floor_divide
|
|
791
1313
|
text << <<~C
|
|
792
1314
|
/* Ruby and CArray floor integer division and give the remainder the
|
|
@@ -805,6 +1327,9 @@ class CArray
|
|
|
805
1327
|
if ( error ) *error = 1;
|
|
806
1328
|
return 0;
|
|
807
1329
|
}
|
|
1330
|
+
/* INT64_MIN / -1 does not fit, and x86 traps on it: the
|
|
1331
|
+
quotient is the negation wrapped, as CArray answers it. */
|
|
1332
|
+
if ( denominator == -1 ) return (int64_t) (0 - (uint64_t) numerator);
|
|
808
1333
|
int64_t quotient = numerator / denominator;
|
|
809
1334
|
if ( numerator % denominator != 0 && ((numerator < 0) != (denominator < 0)) ) {
|
|
810
1335
|
quotient -= 1;
|
|
@@ -814,9 +1339,40 @@ class CArray
|
|
|
814
1339
|
|
|
815
1340
|
C
|
|
816
1341
|
end
|
|
1342
|
+
text << random_source
|
|
817
1343
|
text << pasted_definitions
|
|
818
1344
|
end
|
|
819
1345
|
|
|
1346
|
+
# The generators the block drew from, as CArray wrote them.
|
|
1347
|
+
#
|
|
1348
|
+
# This is the COMPLEX_HELPERS path and not the pasted-body one: what
|
|
1349
|
+
# CArray hands over is a run of `static inline` functions, not one
|
|
1350
|
+
# definition to be given a `static` and a name. It goes in verbatim --
|
|
1351
|
+
# reading it here to check it would be this gem deciding what CArray's
|
|
1352
|
+
# generator is, which is the drift the shared text exists to prevent.
|
|
1353
|
+
#
|
|
1354
|
+
# Only when a draw was written, and once however many generators drew:
|
|
1355
|
+
# two CArray::Rng of the same kind are the same C over different
|
|
1356
|
+
# state.
|
|
1357
|
+
def random_source
|
|
1358
|
+
kinds = @analyzer.random_names.map { |name|
|
|
1359
|
+
@randoms.fetch(name).generator
|
|
1360
|
+
}.uniq.sort
|
|
1361
|
+
return "" if kinds.empty?
|
|
1362
|
+
# What every generator's text wants beside it and none of them owns.
|
|
1363
|
+
# Once, ahead of them: two generators in this file would otherwise
|
|
1364
|
+
# define it twice.
|
|
1365
|
+
text = +"/* What a draw needs beside a generator, from\n" \
|
|
1366
|
+
" CArray::Rng::COMMON_SOURCE. */\n" +
|
|
1367
|
+
CArray::Rng::COMMON_SOURCE + "\n"
|
|
1368
|
+
text + kinds.map { |kind|
|
|
1369
|
+
"/* #{kind}, from CArray::Rng::SOURCE -- the same text CArray\n" \
|
|
1370
|
+
" compiled, so a kernel continues the sequence `random!` left\n" \
|
|
1371
|
+
" off at rather than agreeing with it by construction. */\n" +
|
|
1372
|
+
CArray::Rng::SOURCE.fetch(kind) + "\n"
|
|
1373
|
+
}.join
|
|
1374
|
+
end
|
|
1375
|
+
|
|
820
1376
|
# The bodies of the functions written with `jit_function`, put in this
|
|
821
1377
|
# translation unit as statics and called by name. An address the
|
|
822
1378
|
# compiler cannot see through is a call it cannot inline, and the loop
|
|
@@ -829,18 +1385,49 @@ class CArray
|
|
|
829
1385
|
# one. The name carries the digest of the body, so two of them are the
|
|
830
1386
|
# same function, and one is pasted once however many names the block
|
|
831
1387
|
# reached it by.
|
|
1388
|
+
# Every function whose definition ends up in this file: the ones the
|
|
1389
|
+
# block named, and the ones those call. Helpers and raise messages
|
|
1390
|
+
# are asked of all of them, since a body pasted three deep reports
|
|
1391
|
+
# through the same slot and wants the same preamble.
|
|
1392
|
+
def pasted_closure
|
|
1393
|
+
found = {}
|
|
1394
|
+
walk = lambda do |function|
|
|
1395
|
+
# A borrowed function has no definition to paste; it is declared.
|
|
1396
|
+
next unless function.pasted?
|
|
1397
|
+
next if found[function.symbol]
|
|
1398
|
+
found[function.symbol] = function
|
|
1399
|
+
(function.dependencies || []).each { |called| walk.call(called) }
|
|
1400
|
+
end
|
|
1401
|
+
@pasted_functions.each_value { |function| walk.call(function) }
|
|
1402
|
+
found.values
|
|
1403
|
+
end
|
|
1404
|
+
|
|
1405
|
+
# A pasted body may itself call a function compiled here, which it
|
|
1406
|
+
# reaches by symbol -- so that one is pasted too, and ahead of it, or
|
|
1407
|
+
# the call would be to a name this file has not defined yet. The same
|
|
1408
|
+
# `seen` covers both: one definition per symbol however many ways it
|
|
1409
|
+
# was reached.
|
|
832
1410
|
def pasted_definitions
|
|
833
1411
|
seen = {}
|
|
834
1412
|
text = +""
|
|
835
1413
|
@pasted_functions.each_value do |function|
|
|
836
|
-
|
|
837
|
-
seen[function.name] = true
|
|
838
|
-
text << "/* #{function} */\n" \
|
|
839
|
-
"static #{function.definition}\n"
|
|
1414
|
+
text << paste_definition(function, seen)
|
|
840
1415
|
end
|
|
841
1416
|
text
|
|
842
1417
|
end
|
|
843
1418
|
|
|
1419
|
+
def paste_definition (function, seen)
|
|
1420
|
+
return "" unless function.pasted?
|
|
1421
|
+
return "" if seen[function.symbol]
|
|
1422
|
+
seen[function.symbol] = true
|
|
1423
|
+
text = +""
|
|
1424
|
+
(function.dependencies || []).each do |called|
|
|
1425
|
+
text << paste_definition(called, seen)
|
|
1426
|
+
end
|
|
1427
|
+
text << "/* #{function} */\n" \
|
|
1428
|
+
"static #{function.definition}\n"
|
|
1429
|
+
end
|
|
1430
|
+
|
|
844
1431
|
# One compiled object holds both loops and picks between them once,
|
|
845
1432
|
# outside the loop. The contiguous form indexes a typed pointer along
|
|
846
1433
|
# the innermost axis, which the compiler can vectorise; the strided form
|
|
@@ -848,7 +1435,7 @@ class CArray
|
|
|
848
1435
|
def dispatcher
|
|
849
1436
|
tests = @arrays.map { |array|
|
|
850
1437
|
"strides[#{@stride_offsets.fetch(array) + array_rank(array) - 1}] == " \
|
|
851
|
-
"(int64_t) sizeof(#{storage_c_type(array)})"
|
|
1438
|
+
"(int64_t) sizeof(#{storage_c_type(array_storage(array))})"
|
|
852
1439
|
}
|
|
853
1440
|
# A kernel reaching no array cell has nothing to be contiguous about
|
|
854
1441
|
# -- it can only be one whose work is a call, the arrays it touches
|
|
@@ -878,7 +1465,6 @@ class CArray
|
|
|
878
1465
|
|
|
879
1466
|
def build_body (contiguous)
|
|
880
1467
|
@contiguous = contiguous
|
|
881
|
-
@declared_locals = {}
|
|
882
1468
|
@temporary_count = 0
|
|
883
1469
|
@carried_masks = []
|
|
884
1470
|
# Whether this body has a way to report -- asked of the statements
|
|
@@ -886,10 +1472,202 @@ class CArray
|
|
|
886
1472
|
# kernel carries every extent of its arrays so the border rule has
|
|
887
1473
|
# them, which says nothing about whether a cell can fail.
|
|
888
1474
|
@body_reports = false
|
|
1475
|
+
indent = " " + " " * @rank
|
|
889
1476
|
statements = @analyzer.body.statements.map { |statement|
|
|
890
|
-
emit_statement(statement,
|
|
1477
|
+
emit_statement(statement, indent)
|
|
1478
|
+
}.join
|
|
1479
|
+
"{\n" + declarations + heap_prologue + loop_open +
|
|
1480
|
+
local_declarations(@analyzer.body, indent) + statements +
|
|
1481
|
+
loop_close + heap_epilogue + "}\n"
|
|
1482
|
+
end
|
|
1483
|
+
|
|
1484
|
+
# The arrays this kernel allocates, in the order the block declared
|
|
1485
|
+
# them, as [C name, storage, shape].
|
|
1486
|
+
#
|
|
1487
|
+
# Gathered from every scope rather than from the one being emitted: the
|
|
1488
|
+
# allocation is at the function's entry, outside the cell loop, so a
|
|
1489
|
+
# pointer declared for an array the block made inside an inner loop
|
|
1490
|
+
# still stands at the top of the function. Two sibling loops that each
|
|
1491
|
+
# write `w` are two bindings and so two pointers, which is what keeps
|
|
1492
|
+
# them the two arrays Ruby says they are.
|
|
1493
|
+
def heap_arrays
|
|
1494
|
+
@heap_arrays ||= begin
|
|
1495
|
+
found = []
|
|
1496
|
+
walk = lambda do |node|
|
|
1497
|
+
if node.respond_to?(:array_declarations) && node.array_declarations
|
|
1498
|
+
node.array_declarations.each do |name, binding, storage, shape, heap|
|
|
1499
|
+
next unless heap
|
|
1500
|
+
entry = [local_c_name(name, binding), storage, shape]
|
|
1501
|
+
found << entry unless found.include?(entry)
|
|
1502
|
+
end
|
|
1503
|
+
end
|
|
1504
|
+
node.children.each { |child| walk.call(child) if child.is_a?(Node) }
|
|
1505
|
+
end
|
|
1506
|
+
walk.call(@analyzer.body)
|
|
1507
|
+
found
|
|
1508
|
+
end
|
|
1509
|
+
end
|
|
1510
|
+
|
|
1511
|
+
# One allocation per array at the entry, before the loop: the cells,
|
|
1512
|
+
# and the shadow beside them where the kernel carries masks.
|
|
1513
|
+
#
|
|
1514
|
+
# The lengths come first and stand in `const int64_t` of their own,
|
|
1515
|
+
# because each is read three times over -- to check it, to size the
|
|
1516
|
+
# allocation, and at every subscript on that axis -- and the expression
|
|
1517
|
+
# behind it may be anything the block wrote. A length that is not a
|
|
1518
|
+
# whole number of cells above zero is reported rather than allocated,
|
|
1519
|
+
# which is what the same shape written as a number is refused for as
|
|
1520
|
+
# the block is read. So is a length whose cells would not fit in a
|
|
1521
|
+
# `size_t`: the multiplication into `malloc` would wrap, and a small
|
|
1522
|
+
# allocation answered for a huge one is the worst of the outcomes.
|
|
1523
|
+
#
|
|
1524
|
+
# That is asked of the lengths before they are multiplied, one axis at
|
|
1525
|
+
# a time against what the axes before it leave of the limit. Asked of
|
|
1526
|
+
# the product, it came too late: two lengths of 2**32 multiply to a
|
|
1527
|
+
# number that has already wrapped to zero, which fits, and every
|
|
1528
|
+
# subscript was then checked against its own axis and let through
|
|
1529
|
+
# into an allocation of nothing. The limit is `int64_t` as well as
|
|
1530
|
+
# `size_t`, since that is what the cells are counted in.
|
|
1531
|
+
def heap_prologue
|
|
1532
|
+
return "" if heap_arrays.empty?
|
|
1533
|
+
lines = +""
|
|
1534
|
+
heap_arrays.each do |variable, storage, shape|
|
|
1535
|
+
shape.each_with_index do |extent, axis|
|
|
1536
|
+
next if extent.is_a?(Integer)
|
|
1537
|
+
lines << " const int64_t #{heap_extent_name(variable, axis)} = " \
|
|
1538
|
+
"#{emit(extent.node, :int64)};\n"
|
|
1539
|
+
end
|
|
1540
|
+
room = "(uint64_t) (SIZE_MAX / sizeof(#{storage_c_type(storage)}))"
|
|
1541
|
+
lines << " const uint64_t #{heap_limit_name(variable)} =\n" \
|
|
1542
|
+
" #{room} < (uint64_t) INT64_MAX ? #{room} : (uint64_t) INT64_MAX;\n"
|
|
1543
|
+
lengths = heap_extent_texts(variable, shape)
|
|
1544
|
+
tests = shape.each_with_index.filter_map { |extent, axis|
|
|
1545
|
+
next if extent.is_a?(Integer)
|
|
1546
|
+
"#{heap_extent_name(variable, axis)} >= 1"
|
|
1547
|
+
} + lengths.each_index.map { |axis|
|
|
1548
|
+
left = [heap_limit_name(variable)] +
|
|
1549
|
+
lengths.first(axis).map { |length| "(uint64_t) #{length}" }
|
|
1550
|
+
"(uint64_t) #{lengths[axis]} <= #{left.join(' / ')}"
|
|
1551
|
+
}
|
|
1552
|
+
lines << " const int #{heap_fits_name(variable)} =\n" \
|
|
1553
|
+
" #{tests.join("\n && ")};\n"
|
|
1554
|
+
next if literal_shape?(shape)
|
|
1555
|
+
lines << " const int64_t #{heap_cells_name(variable)} = " \
|
|
1556
|
+
"#{heap_fits_name(variable)} ? #{lengths.join(' * ')} : 0;\n"
|
|
1557
|
+
end
|
|
1558
|
+
heap_arrays.each do |variable, storage, _shape|
|
|
1559
|
+
lines << " #{storage_c_type(storage)} *#{variable} = NULL;\n"
|
|
1560
|
+
lines << " uint8_t *#{local_mask_name(variable)} = NULL;\n" if @masked
|
|
1561
|
+
end
|
|
1562
|
+
unfit = heap_arrays.map { |variable, _storage, _shape|
|
|
1563
|
+
"!#{heap_fits_name(variable)}"
|
|
1564
|
+
}
|
|
1565
|
+
lines << " if ( #{unfit.join(' || ')} ) {\n" \
|
|
1566
|
+
" if ( error ) *error = #{SHAPE_CODE};\n" \
|
|
1567
|
+
" return;\n" \
|
|
1568
|
+
" }\n"
|
|
1569
|
+
heap_arrays.each do |variable, storage, shape|
|
|
1570
|
+
cells = local_array_cells_text(variable, shape)
|
|
1571
|
+
lines << " #{variable} = malloc(sizeof(#{storage_c_type(storage)}) " \
|
|
1572
|
+
"* (size_t) #{cells});\n"
|
|
1573
|
+
next unless @masked
|
|
1574
|
+
lines << " #{local_mask_name(variable)} = malloc((size_t) #{cells});\n"
|
|
1575
|
+
end
|
|
1576
|
+
held = heap_arrays.flat_map { |variable, _storage, _shape|
|
|
1577
|
+
[variable] + (@masked ? [local_mask_name(variable)] : [])
|
|
1578
|
+
}
|
|
1579
|
+
lines << " if ( #{held.map { |name| "!#{name}" }.join(' || ')} ) {\n"
|
|
1580
|
+
held.each { |name| lines << " free(#{name});\n" }
|
|
1581
|
+
lines << " if ( error ) *error = #{MEMORY_CODE};\n" \
|
|
1582
|
+
" return;\n" \
|
|
1583
|
+
" }\n\n"
|
|
1584
|
+
lines
|
|
1585
|
+
end
|
|
1586
|
+
|
|
1587
|
+
# Every way out of a kernel that allocated: the fall-through at the end
|
|
1588
|
+
# of the loop, and the one `return` a `raise` emits, which stands
|
|
1589
|
+
# inside it. Counted rather than assumed -- a leak here is a leak per
|
|
1590
|
+
# call, and the sweep calls once per chunk.
|
|
1591
|
+
def heap_epilogue
|
|
1592
|
+
heap_release(" ")
|
|
1593
|
+
end
|
|
1594
|
+
|
|
1595
|
+
def heap_release (indent)
|
|
1596
|
+
return "" if heap_arrays.empty?
|
|
1597
|
+
heap_arrays.map { |variable, _storage, _shape|
|
|
1598
|
+
"#{indent}free(#{variable});\n" +
|
|
1599
|
+
(@masked ? "#{indent}free(#{local_mask_name(variable)});\n" : "")
|
|
1600
|
+
}.join
|
|
1601
|
+
end
|
|
1602
|
+
|
|
1603
|
+
def heap_extent_name (variable, axis)
|
|
1604
|
+
"#{variable}__extent#{axis}"
|
|
1605
|
+
end
|
|
1606
|
+
|
|
1607
|
+
def heap_cells_name (variable)
|
|
1608
|
+
"#{variable}__cells"
|
|
1609
|
+
end
|
|
1610
|
+
|
|
1611
|
+
def heap_limit_name (variable)
|
|
1612
|
+
"#{variable}__limit"
|
|
1613
|
+
end
|
|
1614
|
+
|
|
1615
|
+
def heap_fits_name (variable)
|
|
1616
|
+
"#{variable}__fits"
|
|
1617
|
+
end
|
|
1618
|
+
|
|
1619
|
+
def heap_extent_texts (variable, shape)
|
|
1620
|
+
shape.each_with_index.map { |extent, axis|
|
|
1621
|
+
extent.is_a?(Integer) ? extent.to_s : heap_extent_name(variable, axis)
|
|
1622
|
+
}
|
|
1623
|
+
end
|
|
1624
|
+
|
|
1625
|
+
def literal_shape? (shape)
|
|
1626
|
+
shape.all? { |extent| extent.is_a?(Integer) }
|
|
1627
|
+
end
|
|
1628
|
+
|
|
1629
|
+
# The locals a scope holds, declared at the head of its block with no
|
|
1630
|
+
# value: every assignment below is a plain one, wherever it stands --
|
|
1631
|
+
# inside a `while` or an arm of an `if` included -- and nothing is read
|
|
1632
|
+
# before it is written, which the typing has already made sure of.
|
|
1633
|
+
def local_declarations (scope, indent)
|
|
1634
|
+
scope.declarations.map { |name, binding, type|
|
|
1635
|
+
variable = local_c_name(name, binding)
|
|
1636
|
+
"#{indent}#{COMPUTATION_C_TYPES.fetch(type)} #{variable};\n" +
|
|
1637
|
+
(@masked ? "#{indent}uint8_t #{local_mask_name(variable)};\n" : "")
|
|
1638
|
+
}.join + local_array_declarations(scope, indent)
|
|
1639
|
+
end
|
|
1640
|
+
|
|
1641
|
+
# The arrays a scope holds, declared at the head of its block beside the
|
|
1642
|
+
# locals. The length is the one the block wrote, so the C array is a
|
|
1643
|
+
# real one -- a fixed-size automatic object, not a pointer into
|
|
1644
|
+
# anything -- and its cells are addressed by constants.
|
|
1645
|
+
#
|
|
1646
|
+
# What clears the zeroed ones is emitted where the statement stands
|
|
1647
|
+
# rather than here: in Ruby a fresh array is made at that line each time
|
|
1648
|
+
# it runs, so a loop's body starts from cleared cells on every pass.
|
|
1649
|
+
def local_array_declarations (scope, indent)
|
|
1650
|
+
scope.array_declarations.map { |name, binding, storage, shape, heap|
|
|
1651
|
+
# One the kernel allocates is a pointer standing at the top of the
|
|
1652
|
+
# function, declared and freed with the allocation (see
|
|
1653
|
+
# #heap_prologue), so the scope it belongs to declares nothing.
|
|
1654
|
+
next "" if heap
|
|
1655
|
+
# One flat run of cells, whatever the rank: the subscripts are
|
|
1656
|
+
# folded to a row-major offset with the strides baked in (see
|
|
1657
|
+
# #local_array_reference), and a flat array is also what a C
|
|
1658
|
+
# function's pointer parameter is handed. `double m[3][4]` would
|
|
1659
|
+
# be the same bytes and a second spelling to keep in step.
|
|
1660
|
+
variable = local_c_name(name, binding)
|
|
1661
|
+
cells = shape.inject(1, :*)
|
|
1662
|
+
# Where the kernel carries masks, a shadow of one byte a cell
|
|
1663
|
+
# beside the cells -- the same arrangement a scalar local has, one
|
|
1664
|
+
# byte beside its value, spread over the cells. A cell of a local
|
|
1665
|
+
# array then reads like a cell of a captured one: the value, and
|
|
1666
|
+
# whether it is there.
|
|
1667
|
+
"#{indent}#{storage_c_type(storage)} #{variable}[#{cells}];\n" +
|
|
1668
|
+
(@masked ?
|
|
1669
|
+
"#{indent}uint8_t #{local_mask_name(variable)}[#{cells}];\n" : "")
|
|
891
1670
|
}.join
|
|
892
|
-
"{\n" + declarations + loop_open + statements + loop_close + "}\n"
|
|
893
1671
|
end
|
|
894
1672
|
|
|
895
1673
|
def array_rank (array)
|
|
@@ -930,38 +1708,44 @@ class CArray
|
|
|
930
1708
|
end
|
|
931
1709
|
end
|
|
932
1710
|
@reals.each_with_index do |name, index|
|
|
933
|
-
lines << " const double #{
|
|
1711
|
+
lines << " const double #{bare_name(name)} = reals[#{index}];\n"
|
|
934
1712
|
end
|
|
935
1713
|
@complexes.each_with_index do |name, index|
|
|
936
1714
|
slot = @reals.size + 2 * index
|
|
937
|
-
lines << " const double _Complex #{
|
|
1715
|
+
lines << " const double _Complex #{bare_name(name)} = " \
|
|
938
1716
|
"CMPLX(reals[#{slot}], reals[#{slot + 1}]);\n"
|
|
939
1717
|
end
|
|
940
1718
|
@integers.each_with_index do |name, index|
|
|
941
|
-
lines << " const int64_t #{
|
|
1719
|
+
lines << " const int64_t #{bare_name(name)} = integers[#{index}];\n"
|
|
1720
|
+
end
|
|
1721
|
+
# The cast is the whole of how a uint64 capture travels: the caller
|
|
1722
|
+
# packed the value's bits into the slot, and reading them as what they
|
|
1723
|
+
# are is what puts the value back.
|
|
1724
|
+
@unsigned_integers.each_with_index do |name, index|
|
|
1725
|
+
lines << " const uint64_t #{bare_name(name)} = " \
|
|
1726
|
+
"(uint64_t) integers[#{@integers.size + index}];\n"
|
|
942
1727
|
end
|
|
943
1728
|
@extent_slots.each_with_index do |(array, axis), slot|
|
|
944
1729
|
lines << " const int64_t #{extent_name(array, axis)} = " \
|
|
945
|
-
"integers[#{@integers.size + slot}];\n"
|
|
1730
|
+
"integers[#{@integers.size + @unsigned_integers.size + slot}];\n"
|
|
946
1731
|
end
|
|
947
1732
|
@address_functions.each_key.with_index do |name, index|
|
|
948
|
-
lines << " const #{c_function_type_name(name)} #{
|
|
1733
|
+
lines << " const #{c_function_type_name(name)} #{bare_name(name)} = " \
|
|
949
1734
|
"(#{c_function_type_name(name)}) functions[#{index}];\n"
|
|
950
1735
|
end
|
|
951
1736
|
@address_arrays.each_with_index do |array, index|
|
|
952
1737
|
# Typed by the array rather than by the parameter it will be passed
|
|
953
1738
|
# to: the caller has already checked that the two agree, and the
|
|
954
1739
|
# array is the one that owns the memory.
|
|
955
|
-
|
|
956
|
-
|
|
1740
|
+
storage = storage_c_type(array_storage(array))
|
|
1741
|
+
lines << " #{storage} *const #{bare_name(array)} = " \
|
|
1742
|
+
"(#{storage} *) data[#{index}];\n"
|
|
957
1743
|
end
|
|
958
1744
|
lines.empty? ? "" : lines.join + "\n"
|
|
959
1745
|
end
|
|
960
1746
|
|
|
961
|
-
# Which way each axis runs is
|
|
962
|
-
#
|
|
963
|
-
# particular order, and any other order would read what was never
|
|
964
|
-
# written.
|
|
1747
|
+
# Which way each axis runs is the extent's step, and the loop runs that
|
|
1748
|
+
# order; nothing here checks it against the cells the body reads.
|
|
965
1749
|
# Each axis carries a start, a limit and a step. The step is a compiled
|
|
966
1750
|
# constant when it is one, because `i++` is what lets the loop vectorise
|
|
967
1751
|
# and that is the case nearly every kernel is in.
|
|
@@ -997,6 +1781,20 @@ class CArray
|
|
|
997
1781
|
def emit_statement (statement, indent)
|
|
998
1782
|
case statement
|
|
999
1783
|
when Assignment then emit_assignment(statement, indent)
|
|
1784
|
+
when ParallelAssignment then
|
|
1785
|
+
# The values, and then the writes. They are ordinary statements by
|
|
1786
|
+
# the time they reach here -- the analyzer made them so -- and the
|
|
1787
|
+
# order they are in is the whole of what a parallel assignment
|
|
1788
|
+
# means.
|
|
1789
|
+
statement.statements.map { |inner|
|
|
1790
|
+
emit_statement(inner, indent)
|
|
1791
|
+
}.join
|
|
1792
|
+
when IntrinsicStatement then emit_intrinsic_statement(statement, indent)
|
|
1793
|
+
when LocalArrayDeclaration then emit_local_array_clearing(statement, indent)
|
|
1794
|
+
when LocalArrayWrite then guarded(statement, indent) { |inner|
|
|
1795
|
+
emit_local_array_write(statement, inner) }
|
|
1796
|
+
when LocalArrayMaskWrite then guarded(statement, indent) { |inner|
|
|
1797
|
+
emit_local_array_mask_write(statement, inner) }
|
|
1000
1798
|
when ElementWrite then guarded(statement, indent) { |inner|
|
|
1001
1799
|
emit_element_write(statement, inner) }
|
|
1002
1800
|
when MaskWrite then guarded(statement, indent) { |inner|
|
|
@@ -1019,6 +1817,33 @@ class CArray
|
|
|
1019
1817
|
|
|
1020
1818
|
# 1 and 2 are the divisor that was not there and the subscript that ran
|
|
1021
1819
|
# off its array. A `raise` in the block takes a code from here up.
|
|
1820
|
+
# What a kernel says when it could not start: a shape it worked out
|
|
1821
|
+
# that is no shape, and an allocation that failed. Negative, because
|
|
1822
|
+
# the positive codes from RAISE_CODE_FLOOR up are a digest of a
|
|
1823
|
+
# `raise` message and a fixed one among them could collide; nothing
|
|
1824
|
+
# reaches for a negative code, so the fixed ones are kept down here,
|
|
1825
|
+
# the kernel's own.
|
|
1826
|
+
SHAPE_CODE = -1
|
|
1827
|
+
MEMORY_CODE = -2
|
|
1828
|
+
|
|
1829
|
+
# And what a rounding reports where Ruby has no Integer to answer, or
|
|
1830
|
+
# has one an int64 cannot hold. Negative for the same reason: fixed
|
|
1831
|
+
# codes, which a `raise` digest must not land on.
|
|
1832
|
+
FLOAT_NAN_CODE = -3
|
|
1833
|
+
FLOAT_INFINITY_CODE = -4
|
|
1834
|
+
FLOAT_NEGATIVE_INFINITY_CODE = -5
|
|
1835
|
+
INTEGER_RANGE_CODE = -6
|
|
1836
|
+
|
|
1837
|
+
# The fixed failures a body reports, as the exception each one raises.
|
|
1838
|
+
# A kernel and a compiled function both look here, so the two say the
|
|
1839
|
+
# same thing for the same cell.
|
|
1840
|
+
FIXED_FAILURES = {
|
|
1841
|
+
FLOAT_NAN_CODE => [FloatDomainError, "NaN"],
|
|
1842
|
+
FLOAT_INFINITY_CODE => [FloatDomainError, "Infinity"],
|
|
1843
|
+
FLOAT_NEGATIVE_INFINITY_CODE => [FloatDomainError, "-Infinity"],
|
|
1844
|
+
INTEGER_RANGE_CODE => [RangeError, "a rounded Float is past the range of int64"],
|
|
1845
|
+
}.freeze
|
|
1846
|
+
|
|
1022
1847
|
RAISE_CODE_FLOOR = 3
|
|
1023
1848
|
|
|
1024
1849
|
# The code is taken from the message rather than counted off as messages
|
|
@@ -1067,10 +1892,13 @@ class CArray
|
|
|
1067
1892
|
# A function returns whatever it returns, whatever happened -- the
|
|
1068
1893
|
# arrangement its C caller is left with, and the one the division
|
|
1069
1894
|
# helper already keeps by returning zero from a divide it refused.
|
|
1070
|
-
# The value is not the answer; the flag says so.
|
|
1895
|
+
# The value is not the answer; the flag says so. For a body lent
|
|
1896
|
+
# out, this zero reaches the wrapper rather than C, and the wrapper
|
|
1897
|
+
# answers C with what the window chose.
|
|
1071
1898
|
leaving = @in_function && !@returns_nothing ? "return 0;" : "return;"
|
|
1072
1899
|
"#{indent}if ( #{tests.join(' && ')} ) {\n" \
|
|
1073
|
-
"#{indent} #{report}\n"
|
|
1900
|
+
"#{indent} #{report}\n" +
|
|
1901
|
+
heap_release("#{indent} ") +
|
|
1074
1902
|
"#{indent} #{leaving}\n" \
|
|
1075
1903
|
"#{indent}}\n"
|
|
1076
1904
|
end
|
|
@@ -1229,17 +2057,28 @@ class CArray
|
|
|
1229
2057
|
# report -- but writing outside it cannot: the report would arrive after
|
|
1230
2058
|
# the damage. So a scatter works its positions out first, and writes
|
|
1231
2059
|
# only if every one of them is inside.
|
|
2060
|
+
#
|
|
2061
|
+
# Each position carries the text of the extent it is tested against
|
|
2062
|
+
# rather than an array and an axis to look one up from. An operand's
|
|
2063
|
+
# extent is a variable the kernel was handed, and there are extents that
|
|
2064
|
+
# are not one of those; what the test needs is the text either way.
|
|
1232
2065
|
def guarded (write, indent)
|
|
1233
2066
|
positions = scattered_positions(write)
|
|
1234
2067
|
return yield(indent) if positions.empty?
|
|
1235
2068
|
|
|
1236
2069
|
lines = "#{indent}{\n"
|
|
1237
|
-
|
|
2070
|
+
# Held by axis as well as by node: an axis whose length the kernel
|
|
2071
|
+
# works out is tested here even where the position is an index, and
|
|
2072
|
+
# an index is not a node the reference can look itself up by.
|
|
2073
|
+
held = {}
|
|
2074
|
+
positions.each_with_index do |(temporary, node, _extent), axis|
|
|
1238
2075
|
lines << "#{indent} const int64_t #{temporary} = #{emit(node, :int64)};\n"
|
|
1239
2076
|
@position_temporaries[node] = temporary
|
|
2077
|
+
held[positions_axis(write, axis)] = temporary
|
|
1240
2078
|
end
|
|
1241
|
-
|
|
1242
|
-
|
|
2079
|
+
@guarded_axes[write] = held
|
|
2080
|
+
test = positions.map { |temporary, _node, extent|
|
|
2081
|
+
"#{temporary} >= 0 && #{temporary} < #{extent}"
|
|
1243
2082
|
}.join(" && ")
|
|
1244
2083
|
lines << "#{indent} if ( #{test} ) {\n"
|
|
1245
2084
|
lines << yield("#{indent} ")
|
|
@@ -1247,19 +2086,74 @@ class CArray
|
|
|
1247
2086
|
lines << "#{indent} if ( #{error_argument} ) *#{error_argument} = 2;\n"
|
|
1248
2087
|
lines << "#{indent} }\n"
|
|
1249
2088
|
lines << "#{indent}}\n"
|
|
1250
|
-
positions.each { |_, node, _
|
|
2089
|
+
positions.each { |_, node, _| @position_temporaries.delete(node) }
|
|
2090
|
+
@guarded_axes.delete(write)
|
|
1251
2091
|
lines
|
|
1252
2092
|
end
|
|
1253
2093
|
|
|
2094
|
+
# Which axis the nth guarded position belongs to.
|
|
2095
|
+
def positions_axis (write, nth)
|
|
2096
|
+
guarded_axes_of(write).fetch(nth)
|
|
2097
|
+
end
|
|
2098
|
+
|
|
2099
|
+
def guarded_axes_of (write)
|
|
2100
|
+
write_subscripts(write).each_with_index.filter_map { |(index, offset), axis|
|
|
2101
|
+
axis if guarded_axis?(write, index, offset, axis)
|
|
2102
|
+
}
|
|
2103
|
+
end
|
|
2104
|
+
|
|
2105
|
+
# [temporary, position, extent text] for each axis of a write that is
|
|
2106
|
+
# addressed at a position only the running kernel knows.
|
|
1254
2107
|
def scattered_positions (write)
|
|
1255
2108
|
subscripts = write_subscripts(write)
|
|
1256
2109
|
subscripts.each_with_index.filter_map { |(index, offset), axis|
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
2110
|
+
# An axis whose length the kernel works out has nothing that was
|
|
2111
|
+
# settled as the block was read -- a number, an index, an index
|
|
2112
|
+
# with an offset, all of them -- so every write onto one works its
|
|
2113
|
+
# position out here and is tested before it lands.
|
|
2114
|
+
next unless guarded_axis?(write, index, offset, axis)
|
|
2115
|
+
position = if index.nil? && offset.is_a?(Node) then offset
|
|
2116
|
+
elsif index.nil? then IntegerLiteral.new(offset)
|
|
2117
|
+
elsif offset.is_a?(Node) then offset
|
|
2118
|
+
else index_position_node(index, offset)
|
|
2119
|
+
end
|
|
2120
|
+
[next_temporary("position"), position, write_extent(write, axis)]
|
|
1260
2121
|
}
|
|
1261
2122
|
end
|
|
1262
2123
|
|
|
2124
|
+
# `k`, `k + 1`: the position an index and its offset stand for, as a
|
|
2125
|
+
# node, so that a write onto an axis the kernel sized can work it out
|
|
2126
|
+
# into a temporary and test it the way a scatter does.
|
|
2127
|
+
def index_position_node (index, offset)
|
|
2128
|
+
read = IndexVariable.new(index, 0)
|
|
2129
|
+
read.type = :int64
|
|
2130
|
+
return read if offset.zero?
|
|
2131
|
+
built = BinaryOperation.new(offset.negative? ? :- : :+, read,
|
|
2132
|
+
IntegerLiteral.new(offset.abs))
|
|
2133
|
+
built.type = :int64
|
|
2134
|
+
built
|
|
2135
|
+
end
|
|
2136
|
+
|
|
2137
|
+
def sized_at_runtime? (write, axis)
|
|
2138
|
+
write.respond_to?(:shape) && !write.shape.fetch(axis).is_a?(Integer)
|
|
2139
|
+
end
|
|
2140
|
+
|
|
2141
|
+
def guarded_axis? (write, index, offset, axis)
|
|
2142
|
+
return true if sized_at_runtime?(write, axis)
|
|
2143
|
+
index.nil? && offset.is_a?(Node) && !Analyzer.fixed_subscript?(offset)
|
|
2144
|
+
end
|
|
2145
|
+
|
|
2146
|
+
# The extent an axis of a write is tested against. An operand's is a
|
|
2147
|
+
# variable the kernel was handed; an array the body made carries the
|
|
2148
|
+
# length the block wrote, and there is no variable to name.
|
|
2149
|
+
def write_extent (write, axis)
|
|
2150
|
+
case write
|
|
2151
|
+
when LocalArrayWrite, LocalArrayMaskWrite
|
|
2152
|
+
local_array_extent_text(write, axis)
|
|
2153
|
+
else extent_name(write.array, axis)
|
|
2154
|
+
end
|
|
2155
|
+
end
|
|
2156
|
+
|
|
1263
2157
|
# `out[i] = UNDEF` marks the cell missing. The bytes underneath are out
|
|
1264
2158
|
# of contract, so there is nothing to store into them.
|
|
1265
2159
|
def emit_mask_only_write (write, indent)
|
|
@@ -1267,8 +2161,23 @@ class CArray
|
|
|
1267
2161
|
end
|
|
1268
2162
|
|
|
1269
2163
|
# The loop a reduction runs in, inside one cell of the outer loops.
|
|
2164
|
+
#
|
|
2165
|
+
# A failure that reports and carries on -- a division handed back a
|
|
2166
|
+
# zero, a computed index handed back cell 0 -- is stopped here, at the
|
|
2167
|
+
# head of the next pass, in a function as in a kernel. The flag is
|
|
2168
|
+
# spelled three ways: a kernel's slot, the flag standing in a function's
|
|
2169
|
+
# own object, and the pointer a pasted body is handed, which a kernel
|
|
2170
|
+
# passes as null under a masked cell and so is asked about first.
|
|
2171
|
+
#
|
|
2172
|
+
# Called once the loop's body is emitted, since the body is what says
|
|
2173
|
+
# whether there is anything to report.
|
|
1270
2174
|
def inner_loop_guard (indent)
|
|
1271
|
-
|
|
2175
|
+
return "" unless @body_reports
|
|
2176
|
+
flag = if !@in_function then "*error"
|
|
2177
|
+
elsif @error_parameter then "#{ERROR_FLAG} && *#{ERROR_FLAG}"
|
|
2178
|
+
else ERROR_FLAG
|
|
2179
|
+
end
|
|
2180
|
+
"#{indent} if ( #{flag} ) break;\n"
|
|
1272
2181
|
end
|
|
1273
2182
|
|
|
1274
2183
|
# The same shape the bounded loop gets, minus the counter -- including
|
|
@@ -1294,13 +2203,13 @@ class CArray
|
|
|
1294
2203
|
@carried_masks =
|
|
1295
2204
|
@masked ? (outer + [emit_mask(loop_node.condition)]).uniq : outer
|
|
1296
2205
|
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
text += loop_node.statements.map { |statement|
|
|
2206
|
+
condition = emit(loop_node.condition, :boolean)
|
|
2207
|
+
body = loop_node.statements.map { |statement|
|
|
1300
2208
|
emit_statement(statement, indent + " ")
|
|
1301
2209
|
}.join
|
|
1302
2210
|
@carried_masks = outer
|
|
1303
|
-
|
|
2211
|
+
"#{indent}while (#{condition}) {\n" + inner_loop_guard(indent) +
|
|
2212
|
+
body + "#{indent}}\n"
|
|
1304
2213
|
end
|
|
1305
2214
|
|
|
1306
2215
|
def emit_inner_loop (loop_node, indent)
|
|
@@ -1308,13 +2217,54 @@ class CArray
|
|
|
1308
2217
|
return emit_split_reduction(loop_node, accumulation, indent) if accumulation
|
|
1309
2218
|
|
|
1310
2219
|
index = loop_node.index
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
2220
|
+
step = loop_node.step
|
|
2221
|
+
inner = indent + " "
|
|
2222
|
+
prelude, from, trips = loop_trips(loop_node, inner)
|
|
2223
|
+
count = "#{index}__t"
|
|
2224
|
+
body = local_declarations(loop_node, inner + " ") +
|
|
2225
|
+
loop_node.statements.map { |statement|
|
|
2226
|
+
emit_statement(statement, inner + " ")
|
|
2227
|
+
}.join
|
|
2228
|
+
"#{indent}{\n" + prelude +
|
|
2229
|
+
"#{inner}for (uint64_t #{count} = 0; #{count} < #{trips}; #{count}++) {\n" \
|
|
2230
|
+
"#{inner} int64_t #{index} = (int64_t) ((uint64_t) #{from} + " \
|
|
2231
|
+
"#{count} * (uint64_t) INT64_C(#{step}));\n" +
|
|
2232
|
+
inner_loop_guard(inner) + body + "#{inner}}\n" + "#{indent}}\n"
|
|
2233
|
+
end
|
|
2234
|
+
|
|
2235
|
+
# How many passes a loop makes, counted before it starts and in uint64.
|
|
2236
|
+
# Counting by comparing the index with the end overflows at the ends of
|
|
2237
|
+
# int64: an inclusive range ends at its last index plus one, and
|
|
2238
|
+
# `(MAX-2..MAX).each` ended at MAX + 1, which is no int64 at all. The
|
|
2239
|
+
# analyzer writes an inclusive end, and a `step` loop's end, as that
|
|
2240
|
+
# plus or minus one; read back here, the last index is used as it is.
|
|
2241
|
+
#
|
|
2242
|
+
# Returns [the C declaring the first index, the end and the count,
|
|
2243
|
+
# the first index's C name, the count's C name].
|
|
2244
|
+
def loop_trips (loop_node, inner)
|
|
2245
|
+
index = loop_node.index
|
|
2246
|
+
step = loop_node.step
|
|
2247
|
+
stride = step.abs
|
|
2248
|
+
to = loop_node.to
|
|
2249
|
+
last = to.is_a?(BinaryOperation) &&
|
|
2250
|
+
to.operator == (step.positive? ? :+ : :-) &&
|
|
2251
|
+
to.right.is_a?(IntegerLiteral) && to.right.value == 1 ? to.left : nil
|
|
2252
|
+
from_name, end_name, trips_name = "#{index}__from", "#{index}__end", "#{index}__trips"
|
|
2253
|
+
ahead = step.positive? ? "(uint64_t) #{end_name} - (uint64_t) #{from_name}" :
|
|
2254
|
+
"(uint64_t) #{from_name} - (uint64_t) #{end_name}"
|
|
2255
|
+
trips =
|
|
2256
|
+
if last
|
|
2257
|
+
reached = step.positive? ? "#{from_name} <= #{end_name}" : "#{from_name} >= #{end_name}"
|
|
2258
|
+
"(#{reached}) ? (#{ahead}) / #{stride}u + 1 : 0"
|
|
2259
|
+
else
|
|
2260
|
+
reached = step.positive? ? "#{from_name} < #{end_name}" : "#{from_name} > #{end_name}"
|
|
2261
|
+
"(#{reached}) ? (#{ahead} - 1) / #{stride}u + 1 : 0"
|
|
2262
|
+
end
|
|
2263
|
+
prelude =
|
|
2264
|
+
"#{inner}const int64_t #{from_name} = #{emit(loop_node.from, :int64)};\n" \
|
|
2265
|
+
"#{inner}const int64_t #{end_name} = #{emit(last || to, :int64)};\n" \
|
|
2266
|
+
"#{inner}const uint64_t #{trips_name} = #{trips};\n"
|
|
2267
|
+
[prelude, from_name, trips_name]
|
|
1318
2268
|
end
|
|
1319
2269
|
|
|
1320
2270
|
# The loop is a reduction when its whole body is one local folding a
|
|
@@ -1329,14 +2279,20 @@ class CArray
|
|
|
1329
2279
|
# A masked accumulator carries a mask beside its value, and a partial
|
|
1330
2280
|
# sum would need one each. Left out rather than half-done.
|
|
1331
2281
|
return nil if @masked
|
|
2282
|
+
# The split walks the index a round at a time from one end, so it is
|
|
2283
|
+
# written for a loop that counts by one. A stride or a downward
|
|
2284
|
+
# sweep keeps the serial loop -- which costs nothing that was there
|
|
2285
|
+
# before, those loops having been unwritable until now.
|
|
2286
|
+
return nil unless loop_node.step == 1
|
|
1332
2287
|
return nil unless loop_node.statements.size == 1
|
|
1333
2288
|
|
|
1334
2289
|
assignment = loop_node.statements.first
|
|
1335
2290
|
return nil unless assignment.is_a?(Assignment)
|
|
1336
|
-
|
|
1337
|
-
#
|
|
1338
|
-
# value rather than starting one.
|
|
1339
|
-
return nil unless
|
|
2291
|
+
local = assignment.local
|
|
2292
|
+
# Live when this loop was entered, at this type and binding, so the
|
|
2293
|
+
# fold continues a value rather than starting one.
|
|
2294
|
+
return nil unless loop_node.entering&.[](assignment.name) ==
|
|
2295
|
+
[assignment.type, assignment.binding]
|
|
1340
2296
|
|
|
1341
2297
|
expression = assignment.expression
|
|
1342
2298
|
return nil unless expression.is_a?(BinaryOperation)
|
|
@@ -1351,9 +2307,9 @@ class CArray
|
|
|
1351
2307
|
return nil if literal_extent(loop_node)&.<(PARTIAL_ACCUMULATORS)
|
|
1352
2308
|
|
|
1353
2309
|
left, right = expression.left, expression.right
|
|
1354
|
-
if accumulator?(left,
|
|
2310
|
+
if accumulator?(left, local) && !mentions_local?(right, local)
|
|
1355
2311
|
[assignment, operator]
|
|
1356
|
-
elsif accumulator?(right,
|
|
2312
|
+
elsif accumulator?(right, local) && !mentions_local?(left, local)
|
|
1357
2313
|
[assignment, operator]
|
|
1358
2314
|
end
|
|
1359
2315
|
end
|
|
@@ -1376,13 +2332,14 @@ class CArray
|
|
|
1376
2332
|
end
|
|
1377
2333
|
end
|
|
1378
2334
|
|
|
1379
|
-
|
|
1380
|
-
|
|
2335
|
+
# `local` is [name, binding]: the variable, not how it is spelled.
|
|
2336
|
+
def accumulator? (node, local)
|
|
2337
|
+
node.is_a?(LocalRead) && node.local == local
|
|
1381
2338
|
end
|
|
1382
2339
|
|
|
1383
|
-
def mentions_local? (node,
|
|
1384
|
-
return true if accumulator?(node,
|
|
1385
|
-
node.children.any? { |child| mentions_local?(child,
|
|
2340
|
+
def mentions_local? (node, local)
|
|
2341
|
+
return true if accumulator?(node, local)
|
|
2342
|
+
node.children.any? { |child| mentions_local?(child, local) }
|
|
1386
2343
|
end
|
|
1387
2344
|
|
|
1388
2345
|
# The fold, run as PARTIAL_ACCUMULATORS chains instead of one, with the
|
|
@@ -1392,38 +2349,50 @@ class CArray
|
|
|
1392
2349
|
# the operator's identity, so it is folded in exactly once. Each chain
|
|
1393
2350
|
# keeps the term's own operand order -- what is licensed here is the
|
|
1394
2351
|
# order the *iterations* are grouped in, not the order within one.
|
|
2352
|
+
# `k++`, `k--`, or the stride written out.
|
|
2353
|
+
def stride_step (index, step)
|
|
2354
|
+
case step
|
|
2355
|
+
when 1 then "#{index}++"
|
|
2356
|
+
when -1 then "#{index}--"
|
|
2357
|
+
else step.positive? ? "#{index} += #{step}" : "#{index} -= #{-step}"
|
|
2358
|
+
end
|
|
2359
|
+
end
|
|
2360
|
+
|
|
1395
2361
|
def emit_split_reduction (loop_node, accumulation, indent)
|
|
1396
2362
|
assignment, operator = accumulation
|
|
1397
|
-
name = assignment.
|
|
2363
|
+
name = local_c_name(assignment.name, assignment.binding)
|
|
1398
2364
|
type = assignment.type
|
|
1399
2365
|
index = loop_node.index
|
|
1400
2366
|
base = "#{index}__base"
|
|
1401
|
-
|
|
1402
|
-
|
|
2367
|
+
# A lane belongs to the accumulator, so its C name comes the way every
|
|
2368
|
+
# local's does.
|
|
2369
|
+
lanes = (0...PARTIAL_ACCUMULATORS).map { |lane|
|
|
2370
|
+
local_c_name(assignment.name, assignment.binding, lane)
|
|
2371
|
+
}
|
|
1403
2372
|
identity = (operator == :* ? ONES : ZEROES).fetch(type)
|
|
1404
2373
|
|
|
1405
2374
|
inner = indent + " "
|
|
1406
|
-
|
|
1407
|
-
text
|
|
1408
|
-
text += "#{inner}
|
|
2375
|
+
prelude, from, trips = loop_trips(loop_node, inner)
|
|
2376
|
+
text = "#{indent}{\n" + prelude
|
|
2377
|
+
text += "#{inner}uint64_t #{base} = 0;\n"
|
|
1409
2378
|
text += "#{inner}#{COMPUTATION_C_TYPES.fetch(type)} #{lanes.first} = #{name}" +
|
|
1410
2379
|
lanes.drop(1).map { |lane| ", #{lane} = #{identity}" }.join + ";\n"
|
|
1411
|
-
|
|
2380
|
+
rounds = lanes.each_with_index.map { |lane, offset|
|
|
2381
|
+
term = lane_expression(assignment.expression, assignment.local, offset)
|
|
2382
|
+
"#{inner} {\n" \
|
|
2383
|
+
"#{inner} const int64_t #{index} = (int64_t) ((uint64_t) #{from} + #{base} + #{offset});\n" \
|
|
2384
|
+
"#{inner} #{lane} = #{emit(term, type)};\n" \
|
|
2385
|
+
"#{inner} }\n"
|
|
2386
|
+
}.join
|
|
2387
|
+
text += "#{inner}for (; #{trips} - #{base} >= #{PARTIAL_ACCUMULATORS}; " \
|
|
1412
2388
|
"#{base} += #{PARTIAL_ACCUMULATORS}) {\n"
|
|
1413
|
-
text += inner_loop_guard(inner)
|
|
1414
|
-
lanes.each_with_index do |lane, offset|
|
|
1415
|
-
term = lane_expression(assignment.expression, name, lane)
|
|
1416
|
-
text += "#{inner} {\n"
|
|
1417
|
-
text += "#{inner} const int64_t #{index} = #{base} + #{offset};\n"
|
|
1418
|
-
text += "#{inner} #{lane} = #{emit(term, type)};\n"
|
|
1419
|
-
text += "#{inner} }\n"
|
|
1420
|
-
end
|
|
2389
|
+
text += inner_loop_guard(inner) + rounds
|
|
1421
2390
|
text += "#{inner}}\n"
|
|
1422
2391
|
text += "#{inner}#{name} = #{combine_partials(lanes, operator)};\n"
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
text +=
|
|
1426
|
-
text +=
|
|
2392
|
+
tail = emit_statement(assignment, inner + " ")
|
|
2393
|
+
text += "#{inner}for (; #{base} < #{trips}; #{base}++) {\n"
|
|
2394
|
+
text += "#{inner} const int64_t #{index} = (int64_t) ((uint64_t) #{from} + #{base});\n"
|
|
2395
|
+
text += inner_loop_guard(inner) + tail
|
|
1427
2396
|
text += "#{inner}}\n"
|
|
1428
2397
|
text + "#{indent}}\n"
|
|
1429
2398
|
end
|
|
@@ -1431,11 +2400,12 @@ class CArray
|
|
|
1431
2400
|
# The same term, folding into one chain instead of into the
|
|
1432
2401
|
# accumulator. The accumulator appears once and at the top, which is
|
|
1433
2402
|
# what reduction_accumulation checked, so only that operand moves.
|
|
1434
|
-
def lane_expression (expression,
|
|
2403
|
+
def lane_expression (expression, local, lane)
|
|
1435
2404
|
operands = [expression.left, expression.right].map { |operand|
|
|
1436
|
-
next operand unless accumulator?(operand,
|
|
2405
|
+
next operand unless accumulator?(operand, local)
|
|
1437
2406
|
read = LocalRead.new(operand.name, operand.location)
|
|
1438
|
-
read.
|
|
2407
|
+
read.binding = operand.binding
|
|
2408
|
+
read.lane = lane
|
|
1439
2409
|
read.type = operand.type
|
|
1440
2410
|
read
|
|
1441
2411
|
}
|
|
@@ -1481,26 +2451,28 @@ class CArray
|
|
|
1481
2451
|
end
|
|
1482
2452
|
|
|
1483
2453
|
def emit_assignment (assignment, indent)
|
|
1484
|
-
name = assignment.
|
|
2454
|
+
name = local_c_name(assignment.name, assignment.binding)
|
|
1485
2455
|
type = assignment.type
|
|
1486
|
-
declared = @declared_locals.key?(name)
|
|
1487
|
-
@declared_locals[name] = type
|
|
1488
2456
|
|
|
1489
2457
|
# A local carries a mask alongside its value, so that reading it later
|
|
1490
|
-
# is the same as reading what it was computed from
|
|
2458
|
+
# is the same as reading what it was computed from -- and, as a cell
|
|
2459
|
+
# written here would, the masks of the branches this assignment stands
|
|
2460
|
+
# in. An `if` or a `while` taken on a missing cell was decided by
|
|
2461
|
+
# bytes that mean nothing, so what it writes means nothing either,
|
|
2462
|
+
# wherever it writes it. Without this the rule held for
|
|
2463
|
+
# `out[i] = 1.0` and not for `v = 1.0; out[i] = v`, which is the
|
|
2464
|
+
# spelling a search over a row takes.
|
|
1491
2465
|
mask = if @masked
|
|
1492
|
-
"#{indent}#{
|
|
1493
|
-
"#{
|
|
2466
|
+
"#{indent}#{local_mask_name(name)} = " \
|
|
2467
|
+
"#{combine_masks(@carried_masks + [emit_mask(assignment.expression)])};\n"
|
|
1494
2468
|
else
|
|
1495
2469
|
""
|
|
1496
2470
|
end
|
|
1497
2471
|
|
|
1498
2472
|
if assignment.expression.is_a?(Conditional)
|
|
1499
|
-
|
|
1500
|
-
lines + emit_conditional_statement(assignment.expression, name, type, indent) + mask
|
|
2473
|
+
emit_conditional_statement(assignment.expression, name, type, indent) + mask
|
|
1501
2474
|
else
|
|
1502
|
-
|
|
1503
|
-
"#{indent}#{prefix}#{name} = #{emit(assignment.expression, type)};\n" + mask
|
|
2475
|
+
"#{indent}#{name} = #{emit(assignment.expression, type)};\n" + mask
|
|
1504
2476
|
end
|
|
1505
2477
|
end
|
|
1506
2478
|
|
|
@@ -1519,6 +2491,213 @@ class CArray
|
|
|
1519
2491
|
subscripts || @analyzer.index_names.map { |name| [name, 0] }
|
|
1520
2492
|
end
|
|
1521
2493
|
|
|
2494
|
+
# `sum(w)` / `min(w)` / `max(w)`: a call to a helper in the preamble.
|
|
2495
|
+
#
|
|
2496
|
+
# A call rather than a loop unrolled at the statement, because these
|
|
2497
|
+
# stand in expressions -- `max(w) - min(w)` is one line with two of them
|
|
2498
|
+
# -- and an expression has nowhere to put a loop. What it costs is
|
|
2499
|
+
# nothing: the helper is `static inline` and the length is a literal, so
|
|
2500
|
+
# the compiler has the same code in front of it either way.
|
|
2501
|
+
def intrinsic_reference (node)
|
|
2502
|
+
need(node.intrinsic, node.storage, nil)
|
|
2503
|
+
"#{intrinsic_helper_name(node.intrinsic, node.storage)}" \
|
|
2504
|
+
"(#{local_c_name(node.name, node.binding)}, " \
|
|
2505
|
+
"#{local_array_extent_text(node, 0)})"
|
|
2506
|
+
end
|
|
2507
|
+
|
|
2508
|
+
# A length the kernel works out cannot choose a network -- the choice
|
|
2509
|
+
# is made as the C is written and a network is a fixed run of
|
|
2510
|
+
# comparators -- so it sorts by the helper that takes its length as an
|
|
2511
|
+
# argument, which is the insertion sort.
|
|
2512
|
+
def emit_intrinsic_statement (statement, indent)
|
|
2513
|
+
cells = statement.shape.first
|
|
2514
|
+
name = local_c_name(statement.name, statement.binding)
|
|
2515
|
+
# One cell is in order already, and a network of no comparators is
|
|
2516
|
+
# nothing to call.
|
|
2517
|
+
return "" if cells == 1
|
|
2518
|
+
unless cells.is_a?(Integer)
|
|
2519
|
+
need(:sort, statement.storage, nil)
|
|
2520
|
+
return "#{indent}#{intrinsic_helper_name(:sort, statement.storage)}" \
|
|
2521
|
+
"(#{name}, #{local_array_extent_text(statement, 0)});\n"
|
|
2522
|
+
end
|
|
2523
|
+
if SortingNetworks.covers?(cells)
|
|
2524
|
+
need(:sort, statement.storage, cells)
|
|
2525
|
+
"#{indent}#{intrinsic_helper_name(:sort, statement.storage)}" \
|
|
2526
|
+
"_#{cells}(#{name});\n"
|
|
2527
|
+
else
|
|
2528
|
+
need(:sort, statement.storage, nil)
|
|
2529
|
+
"#{indent}#{intrinsic_helper_name(:sort, statement.storage)}" \
|
|
2530
|
+
"(#{name}, #{cells});\n"
|
|
2531
|
+
end
|
|
2532
|
+
end
|
|
2533
|
+
|
|
2534
|
+
def intrinsic_helper_name (intrinsic, storage)
|
|
2535
|
+
"carray_jit_#{intrinsic}_#{storage}"
|
|
2536
|
+
end
|
|
2537
|
+
|
|
2538
|
+
# What the preamble has to write out. A list used as a set, the way
|
|
2539
|
+
# `@clamp_types` and `@gamma_types` are, so that two sorts of one type
|
|
2540
|
+
# and length are one helper.
|
|
2541
|
+
def need (intrinsic, storage, cells)
|
|
2542
|
+
entry = [intrinsic, storage, cells]
|
|
2543
|
+
@intrinsic_needs << entry unless @intrinsic_needs.include?(entry)
|
|
2544
|
+
end
|
|
2545
|
+
|
|
2546
|
+
# `w = CArray.double(9)` where the statement stands. The declaration is
|
|
2547
|
+
# at the head of the block; what is left here is what Ruby does at this
|
|
2548
|
+
# line, which for the zeroed spellings is a fresh array of zeros.
|
|
2549
|
+
#
|
|
2550
|
+
# `sizeof` rather than a byte count written out: the length is already
|
|
2551
|
+
# in the declaration, and one place for it is one place to be wrong.
|
|
2552
|
+
# The shadow is cleared with the cells, and a cleared shadow byte is
|
|
2553
|
+
# "not missing": a fresh array in Ruby holds zeros, which are values
|
|
2554
|
+
# that are there. The `empty` spelling says nothing about either, so
|
|
2555
|
+
# its shadow is whatever the stack held -- the same bargain its cells
|
|
2556
|
+
# are under, and the same promise the block made by writing `empty`.
|
|
2557
|
+
def emit_local_array_clearing (declaration, indent)
|
|
2558
|
+
return "" unless declaration.zeroed
|
|
2559
|
+
name = local_c_name(declaration.name, declaration.binding)
|
|
2560
|
+
# `sizeof` answers for an array and not for a pointer, so one the
|
|
2561
|
+
# kernel allocated is cleared by the count it was allocated with.
|
|
2562
|
+
if declaration.heap
|
|
2563
|
+
cells = local_array_cells_text(name, declaration.shape)
|
|
2564
|
+
bytes = "sizeof(#{storage_c_type(declaration.storage)}) * " \
|
|
2565
|
+
"(size_t) #{cells}"
|
|
2566
|
+
return "#{indent}memset(#{name}, 0, #{bytes});\n" +
|
|
2567
|
+
(@masked ?
|
|
2568
|
+
"#{indent}memset(#{local_mask_name(name)}, 0, " \
|
|
2569
|
+
"(size_t) #{cells});\n" : "")
|
|
2570
|
+
end
|
|
2571
|
+
"#{indent}memset(#{name}, 0, sizeof #{name});\n" +
|
|
2572
|
+
(@masked ?
|
|
2573
|
+
"#{indent}memset(#{local_mask_name(name)}, 0, " \
|
|
2574
|
+
"sizeof #{local_mask_name(name)});\n" : "")
|
|
2575
|
+
end
|
|
2576
|
+
|
|
2577
|
+
# `w[k] = UNDEF`: the shadow byte alone. The cell's bytes are out of
|
|
2578
|
+
# contract once it is marked, so there is nothing to store into them.
|
|
2579
|
+
def emit_local_array_mask_write (write, indent)
|
|
2580
|
+
"#{indent}#{local_array_reference(write, :mask)} = 1;\n"
|
|
2581
|
+
end
|
|
2582
|
+
|
|
2583
|
+
def emit_local_array_write (write, indent)
|
|
2584
|
+
target = local_array_reference(write)
|
|
2585
|
+
expression = write.expression
|
|
2586
|
+
# A cell of a local array carries a mask beside it the way a scalar
|
|
2587
|
+
# local carries one beside its value, and by the same rule: what the
|
|
2588
|
+
# expression written into it carried, and the branches the write
|
|
2589
|
+
# stands in. A write reached because a missing cell said so was
|
|
2590
|
+
# reached for no reason, whichever of the three it writes.
|
|
2591
|
+
mask = if @masked
|
|
2592
|
+
"#{indent}#{local_array_reference(write, :mask)} = " \
|
|
2593
|
+
"#{combine_masks(@carried_masks + [emit_mask(write.expression)])};\n"
|
|
2594
|
+
else
|
|
2595
|
+
""
|
|
2596
|
+
end
|
|
2597
|
+
if expression.is_a?(Conditional)
|
|
2598
|
+
temporary = next_temporary
|
|
2599
|
+
type = expression.type
|
|
2600
|
+
"#{indent}#{COMPUTATION_C_TYPES.fetch(type)} #{temporary};\n" +
|
|
2601
|
+
emit_conditional_statement(expression, temporary, type, indent) +
|
|
2602
|
+
"#{indent}#{target} = " \
|
|
2603
|
+
"#{cast_to_storage(temporary, type, write.storage)};\n" + mask
|
|
2604
|
+
else
|
|
2605
|
+
type = complex_storage_of_power(expression, write.storage) || expression.type
|
|
2606
|
+
value = cast_to_storage(emit(expression, type), type, write.storage)
|
|
2607
|
+
"#{indent}#{target} = #{value};\n" + mask
|
|
2608
|
+
end
|
|
2609
|
+
end
|
|
2610
|
+
|
|
2611
|
+
# `w[(k) + 1]`, and for a rank above one the row-major offset with the
|
|
2612
|
+
# strides folded in -- the shape is written in the block, so they are
|
|
2613
|
+
# constants rather than numbers the kernel is handed.
|
|
2614
|
+
# `:mask` asks for the shadow byte beside the cell rather than the
|
|
2615
|
+
# cell: the same position, in the array beside it. It reports no index
|
|
2616
|
+
# of its own, for the reason a captured array's mask read does not --
|
|
2617
|
+
# the value's read asks the same question a moment later, and one
|
|
2618
|
+
# report is the one the cell deserves.
|
|
2619
|
+
def local_array_reference (node, kind = :data)
|
|
2620
|
+
name = local_c_name(node.name, node.binding)
|
|
2621
|
+
name = local_mask_name(name) if kind == :mask
|
|
2622
|
+
terms = node.subscripts.each_with_index.map { |(index, offset), axis|
|
|
2623
|
+
position = local_array_position(node, index, offset, axis,
|
|
2624
|
+
reporting: kind != :mask)
|
|
2625
|
+
stride = local_array_stride_text(node, axis)
|
|
2626
|
+
stride == "1" ? position : "(#{position}) * #{stride}"
|
|
2627
|
+
}
|
|
2628
|
+
"#{name}[#{terms.join(' + ')}]"
|
|
2629
|
+
end
|
|
2630
|
+
|
|
2631
|
+
# How long an axis of a local array is, as the C says it: the number
|
|
2632
|
+
# the block wrote, or the name the kernel worked the length out into.
|
|
2633
|
+
def local_array_extent_text (node, axis)
|
|
2634
|
+
extent = node.shape.fetch(axis)
|
|
2635
|
+
return extent.to_s if extent.is_a?(Integer)
|
|
2636
|
+
heap_extent_name(local_c_name(node.name, node.binding), axis)
|
|
2637
|
+
end
|
|
2638
|
+
|
|
2639
|
+
# And how many cells it has altogether, for the allocation and the
|
|
2640
|
+
# clearing.
|
|
2641
|
+
def local_array_cells_text (variable, shape)
|
|
2642
|
+
return shape.inject(1, :*).to_s if literal_shape?(shape)
|
|
2643
|
+
heap_cells_name(variable)
|
|
2644
|
+
end
|
|
2645
|
+
|
|
2646
|
+
# How far one step along an axis moves: the cells of every axis inside
|
|
2647
|
+
# it, which are numbers where the block wrote numbers and the lengths
|
|
2648
|
+
# the kernel worked out where it did not.
|
|
2649
|
+
def local_array_stride_text (node, axis)
|
|
2650
|
+
inner = node.shape[(axis + 1)..]
|
|
2651
|
+
return inner.inject(1, :*).to_s if literal_shape?(inner)
|
|
2652
|
+
inner.each_with_index.map { |extent, offset|
|
|
2653
|
+
extent.is_a?(Integer) ? extent.to_s :
|
|
2654
|
+
heap_extent_name(local_c_name(node.name, node.binding),
|
|
2655
|
+
axis + 1 + offset)
|
|
2656
|
+
}.join(" * ")
|
|
2657
|
+
end
|
|
2658
|
+
|
|
2659
|
+
# An axis the analyzer settled carries an index and a literal offset,
|
|
2660
|
+
# and the C says so with nothing around it. One it could not settle
|
|
2661
|
+
# carries the expression, and the check is here.
|
|
2662
|
+
def local_array_position (node, index, offset, axis, reporting: true)
|
|
2663
|
+
settled = node.shape.fetch(axis).is_a?(Integer)
|
|
2664
|
+
# The write around this has already worked the position out and
|
|
2665
|
+
# tested it.
|
|
2666
|
+
guarded = @guarded_axes[node]
|
|
2667
|
+
return guarded.fetch(axis) if guarded&.key?(axis)
|
|
2668
|
+
if index.nil?
|
|
2669
|
+
return offset.to_s unless offset.is_a?(Node)
|
|
2670
|
+
# A position that is a number is settled against a length that is
|
|
2671
|
+
# one. Against a length the kernel works out it is not: 3 is
|
|
2672
|
+
# inside an array of 4 cells and outside one of 2, so it is checked
|
|
2673
|
+
# where the cell is reached like any other.
|
|
2674
|
+
return emit(offset, :int64) if settled &&
|
|
2675
|
+
Analyzer.fixed_subscript?(offset)
|
|
2676
|
+
# A write has already worked this position out and tested it.
|
|
2677
|
+
held = @position_temporaries[offset]
|
|
2678
|
+
return held if held
|
|
2679
|
+
@uses_index_check = true
|
|
2680
|
+
return "carray_jit_index(#{emit(offset, :int64)}, " \
|
|
2681
|
+
"#{local_array_extent_text(node, axis)}, " \
|
|
2682
|
+
"#{reporting ? error_argument : '(int32_t *) 0'})"
|
|
2683
|
+
end
|
|
2684
|
+
# An index on an axis whose length the kernel works out was not
|
|
2685
|
+
# settled either: (a) had no number to compare the loop's reach
|
|
2686
|
+
# against, so the reach is tested here.
|
|
2687
|
+
unless settled
|
|
2688
|
+
@uses_index_check = true
|
|
2689
|
+
position = if offset.zero? then index.to_s
|
|
2690
|
+
elsif offset.negative? then "#{index} - #{-offset}"
|
|
2691
|
+
else "#{index} + #{offset}"
|
|
2692
|
+
end
|
|
2693
|
+
return "carray_jit_index(#{position}, " \
|
|
2694
|
+
"#{local_array_extent_text(node, axis)}, " \
|
|
2695
|
+
"#{reporting ? error_argument : '(int32_t *) 0'})"
|
|
2696
|
+
end
|
|
2697
|
+
return index.to_s if offset.zero?
|
|
2698
|
+
offset.negative? ? "#{index} - #{-offset}" : "#{index} + #{offset}"
|
|
2699
|
+
end
|
|
2700
|
+
|
|
1522
2701
|
def emit_element_write (write, indent)
|
|
1523
2702
|
unless @masked
|
|
1524
2703
|
@masked_flag = nil
|
|
@@ -1547,13 +2726,15 @@ class CArray
|
|
|
1547
2726
|
when ElementRead
|
|
1548
2727
|
cell_reference(node.array, node.subscripts, :mask)
|
|
1549
2728
|
when LocalRead
|
|
1550
|
-
local_mask_name(node.
|
|
2729
|
+
local_mask_name(local_c_name(node.name, node.binding, node.lane))
|
|
2730
|
+
when LocalArrayRead
|
|
2731
|
+
local_array_reference(node, :mask)
|
|
1551
2732
|
when Conditional
|
|
1552
2733
|
branches = "#{emit(node.condition, :boolean)} ? " \
|
|
1553
2734
|
"#{emit_mask(node.consequent)} : #{emit_mask(node.alternative)}"
|
|
1554
2735
|
combine_masks([emit_mask(node.condition), "(#{branches})"])
|
|
1555
2736
|
when IntegerLiteral, FloatLiteral, IndexVariable, CaptureRead, MaskTest,
|
|
1556
|
-
BoundsValue, ZeroLike
|
|
2737
|
+
LocalArrayMaskTest, PointerTest, BoundsValue, ZeroLike
|
|
1557
2738
|
"0"
|
|
1558
2739
|
else
|
|
1559
2740
|
combine_masks(node.children.map { |child| emit_mask(child) })
|
|
@@ -1578,14 +2759,33 @@ class CArray
|
|
|
1578
2759
|
type = expression.type
|
|
1579
2760
|
"#{indent}#{COMPUTATION_C_TYPES.fetch(type)} #{temporary};\n" +
|
|
1580
2761
|
emit_conditional_statement(expression, temporary, type, indent) +
|
|
1581
|
-
"#{indent}#{target} =
|
|
2762
|
+
"#{indent}#{target} = " \
|
|
2763
|
+
"#{cast_to_storage(temporary, type, array_storage(write.array))};\n"
|
|
1582
2764
|
else
|
|
1583
|
-
|
|
1584
|
-
|
|
2765
|
+
storage = array_storage(write.array)
|
|
2766
|
+
real = REAL_STORAGE_TYPES[storage]
|
|
2767
|
+
if real && rounded_real?(expression, real)
|
|
2768
|
+
return "#{indent}#{target} = #{emit(expression, real)};\n"
|
|
2769
|
+
end
|
|
2770
|
+
type = complex_storage_of_power(expression, storage) || expression.type
|
|
2771
|
+
value = cast_to_storage(emit(expression, type), type, storage)
|
|
1585
2772
|
"#{indent}#{target} = #{value};\n"
|
|
1586
2773
|
end
|
|
1587
2774
|
end
|
|
1588
2775
|
|
|
2776
|
+
# A real power stored in a complex cell is read as complex; see
|
|
2777
|
+
# complex_read_power?.
|
|
2778
|
+
COMPLEX_STORAGE_TYPES = { "cmplx128" => :complex, "cmplx64" => :float_complex }.freeze
|
|
2779
|
+
|
|
2780
|
+
def complex_storage_of_power (expression, storage)
|
|
2781
|
+
type = COMPLEX_STORAGE_TYPES[storage]
|
|
2782
|
+
type if type && complex_read_power?(expression, type)
|
|
2783
|
+
end
|
|
2784
|
+
|
|
2785
|
+
# A float cell, by what it computes in: the storage a rounding written
|
|
2786
|
+
# straight into keeps as a real number (see #rounded_real?).
|
|
2787
|
+
REAL_STORAGE_TYPES = { "float64" => :double, "float32" => :float }.freeze
|
|
2788
|
+
|
|
1589
2789
|
# A conditional in statement position becomes if/else rather than a
|
|
1590
2790
|
# ternary; it reads far better when the branches are long.
|
|
1591
2791
|
def emit_conditional_statement (node, target, type, indent)
|
|
@@ -1596,31 +2796,230 @@ class CArray
|
|
|
1596
2796
|
"#{indent}}\n"
|
|
1597
2797
|
end
|
|
1598
2798
|
|
|
1599
|
-
|
|
1600
|
-
|
|
2799
|
+
# What a cell is held in, and the casts between that and the type the
|
|
2800
|
+
# kernel computes in.
|
|
2801
|
+
#
|
|
2802
|
+
# Each is asked about a storage type rather than about an array's name.
|
|
2803
|
+
# An operand's storage type is looked up under its name, but not every
|
|
2804
|
+
# array a body reaches is an operand -- so a helper that did the lookup
|
|
2805
|
+
# itself could only answer for the ones that are, and the answer for
|
|
2806
|
+
# anything else would be a KeyError out of the middle of code
|
|
2807
|
+
# generation. The name that has one is looked up by #array_storage at
|
|
2808
|
+
# the call site.
|
|
2809
|
+
def storage_c_type (storage)
|
|
2810
|
+
STORAGE_C_TYPES.fetch(storage)
|
|
2811
|
+
end
|
|
2812
|
+
|
|
2813
|
+
# ---- the intrinsics, written into the preamble ----
|
|
2814
|
+
|
|
2815
|
+
# The accumulator `min` and `max` start from over an integer array,
|
|
2816
|
+
# where starting at the limit of the type rather than at the first cell
|
|
2817
|
+
# saves asking whether a cell has been seen yet. A floating array
|
|
2818
|
+
# starts at NaN instead, which any number displaces -- see
|
|
2819
|
+
# #intrinsic_fold_helper.
|
|
2820
|
+
INTRINSIC_LIMITS = {
|
|
2821
|
+
:int64 => ["INT64_MAX", "INT64_MIN"],
|
|
2822
|
+
:uint64 => ["UINT64_MAX", "0"],
|
|
2823
|
+
}.freeze
|
|
2824
|
+
|
|
2825
|
+
def computation_c_type_of (storage)
|
|
2826
|
+
COMPUTATION_C_TYPES.fetch(TypeAssignment.storage_type(storage))
|
|
2827
|
+
end
|
|
2828
|
+
|
|
2829
|
+
def floating_storage? (storage)
|
|
2830
|
+
type = TypeAssignment.storage_type(storage)
|
|
2831
|
+
[:float, :double].include?(type)
|
|
2832
|
+
end
|
|
2833
|
+
|
|
2834
|
+
def intrinsic_helpers
|
|
2835
|
+
return "" if @intrinsic_needs.empty?
|
|
2836
|
+
text = +""
|
|
2837
|
+
%i[sum min max].each do |intrinsic|
|
|
2838
|
+
@intrinsic_needs.select { |kind, _, _| kind == intrinsic }
|
|
2839
|
+
.map { |_, storage, _| storage }.uniq.each do |storage|
|
|
2840
|
+
text << intrinsic_fold_helper(intrinsic, storage)
|
|
2841
|
+
end
|
|
2842
|
+
end
|
|
2843
|
+
sorted = @intrinsic_needs.select { |kind, _, _| kind == :sort }
|
|
2844
|
+
sorted.map { |_, storage, _| storage }.uniq.each do |storage|
|
|
2845
|
+
text << intrinsic_order_helper(storage)
|
|
2846
|
+
end
|
|
2847
|
+
sorted.each do |_, storage, cells|
|
|
2848
|
+
text << (cells ? intrinsic_network_helper(storage, cells)
|
|
2849
|
+
: intrinsic_insertion_helper(storage))
|
|
2850
|
+
end
|
|
2851
|
+
text
|
|
2852
|
+
end
|
|
2853
|
+
|
|
2854
|
+
def intrinsic_fold_helper (intrinsic, storage)
|
|
2855
|
+
cell = storage_c_type(storage)
|
|
2856
|
+
result = computation_c_type_of(storage)
|
|
2857
|
+
type = TypeAssignment.storage_type(storage)
|
|
2858
|
+
if intrinsic == :sum
|
|
2859
|
+
return <<~C
|
|
2860
|
+
/* `sum(w)`, added in index order from the first cell: what the
|
|
2861
|
+
same loop written out in Ruby adds, in the same order, so the
|
|
2862
|
+
last bit is the same one. No partial sums -- a local array is
|
|
2863
|
+
small enough that splitting buys nothing and would change the
|
|
2864
|
+
answer. */
|
|
2865
|
+
static inline #{result}
|
|
2866
|
+
carray_jit_sum_#{storage} (const #{cell} *w, int64_t n)
|
|
2867
|
+
{
|
|
2868
|
+
#{result} total = 0;
|
|
2869
|
+
for ( int64_t k = 0; k < n; k++ ) total += w[k];
|
|
2870
|
+
return total;
|
|
2871
|
+
}
|
|
2872
|
+
|
|
2873
|
+
C
|
|
2874
|
+
end
|
|
2875
|
+
if floating_storage?(storage)
|
|
2876
|
+
comparison = intrinsic == :min ? "<" : ">"
|
|
2877
|
+
return <<~C
|
|
2878
|
+
/* `#{intrinsic}(w)`, which is what CArray's own #{intrinsic} answers.
|
|
2879
|
+
A cell takes the accumulator only by beating it outright, or by
|
|
2880
|
+
being a number where the accumulator is still NaN: so a NaN is
|
|
2881
|
+
skipped wherever it stands, an array of nothing but NaN comes
|
|
2882
|
+
back NaN, and of two cells that compare equal the first is kept
|
|
2883
|
+
-- which is how CArray keeps 0.0 or -0.0, whichever came first.
|
|
2884
|
+
fmin and fmax answer the same but for that: C leaves which zero
|
|
2885
|
+
they give up to the library. Starting at NaN is what lets the
|
|
2886
|
+
first cell in take the accumulator without a seen-yet test. */
|
|
2887
|
+
static inline #{result}
|
|
2888
|
+
carray_jit_#{intrinsic}_#{storage} (const #{cell} *w, int64_t n)
|
|
2889
|
+
{
|
|
2890
|
+
#{result} best = NAN;
|
|
2891
|
+
for ( int64_t k = 0; k < n; k++ ) {
|
|
2892
|
+
if ( w[k] #{comparison} best || best != best ) best = w[k];
|
|
2893
|
+
}
|
|
2894
|
+
return best;
|
|
2895
|
+
}
|
|
2896
|
+
|
|
2897
|
+
C
|
|
2898
|
+
end
|
|
2899
|
+
limit = INTRINSIC_LIMITS.fetch(type)[intrinsic == :min ? 0 : 1]
|
|
2900
|
+
comparison = intrinsic == :min ? "<" : ">"
|
|
2901
|
+
<<~C
|
|
2902
|
+
/* `#{intrinsic}(w)`, which is what CArray's own #{intrinsic} answers.
|
|
2903
|
+
An integer type has no NaN, so the limit this starts from is only
|
|
2904
|
+
ever an answer for an array with no cells. */
|
|
2905
|
+
static inline #{result}
|
|
2906
|
+
carray_jit_#{intrinsic}_#{storage} (const #{cell} *w, int64_t n)
|
|
2907
|
+
{
|
|
2908
|
+
#{result} best = #{limit};
|
|
2909
|
+
for ( int64_t k = 0; k < n; k++ ) best = (w[k] #{comparison} best) ? w[k] : best;
|
|
2910
|
+
return best;
|
|
2911
|
+
}
|
|
2912
|
+
|
|
2913
|
+
C
|
|
2914
|
+
end
|
|
2915
|
+
|
|
2916
|
+
# The order the two sorts put the cells in, in one place so that the
|
|
2917
|
+
# network and the loop cannot disagree about it.
|
|
2918
|
+
#
|
|
2919
|
+
# A NaN comes after every number and ties with another NaN, which makes
|
|
2920
|
+
# this a total preorder -- and a comparator network sorts under any of
|
|
2921
|
+
# those, so the finite cells come out ascending with the NaNs behind
|
|
2922
|
+
# them. That is where CArray's own sort puts them.
|
|
2923
|
+
#
|
|
2924
|
+
# The min / max folds above skip a NaN, which drops a cell rather than
|
|
2925
|
+
# moving it. A fold wants that; a sort has to put every cell somewhere.
|
|
2926
|
+
def intrinsic_order_helper (storage)
|
|
2927
|
+
cell = storage_c_type(storage)
|
|
2928
|
+
nan_term = floating_storage?(storage) ? " || (a != a && b == b)" : ""
|
|
2929
|
+
<<~C
|
|
2930
|
+
/* Whether `a` belongs after `b` once the cells are in order.#{
|
|
2931
|
+
floating_storage?(storage) ?
|
|
2932
|
+
"\n A NaN belongs after every number, and two NaNs tie." : ""} */
|
|
2933
|
+
static inline int
|
|
2934
|
+
carray_jit_after_#{storage} (#{cell} a, #{cell} b)
|
|
2935
|
+
{
|
|
2936
|
+
return (b < a)#{nan_term};
|
|
2937
|
+
}
|
|
2938
|
+
|
|
2939
|
+
/* One compare-exchange: the smaller of the two ends up in `low`.
|
|
2940
|
+
Written as two selects rather than a branch, so the sequence a
|
|
2941
|
+
network lays out has nothing in it to predict. */
|
|
2942
|
+
static inline void
|
|
2943
|
+
carray_jit_cx_#{storage} (#{cell} *low, #{cell} *high)
|
|
2944
|
+
{
|
|
2945
|
+
#{cell} a = *low, b = *high;
|
|
2946
|
+
int swap = carray_jit_after_#{storage}(a, b);
|
|
2947
|
+
*low = swap ? b : a;
|
|
2948
|
+
*high = swap ? a : b;
|
|
2949
|
+
}
|
|
2950
|
+
|
|
2951
|
+
C
|
|
2952
|
+
end
|
|
2953
|
+
|
|
2954
|
+
def intrinsic_network_helper (storage, cells)
|
|
2955
|
+
network = SortingNetworks.for(cells)
|
|
2956
|
+
pairs = network.map { |low, high|
|
|
2957
|
+
" carray_jit_cx_#{storage}(&w[#{low}], &w[#{high}]);"
|
|
2958
|
+
}.join("\n")
|
|
2959
|
+
<<~C
|
|
2960
|
+
/* `sort(w)` for #{cells} cells: a comparator network, which is the
|
|
2961
|
+
same #{network.size} compare-exchanges every time in the same
|
|
2962
|
+
order -- no branch, nothing to predict, and the smallest number
|
|
2963
|
+
of comparisons known for this length. */
|
|
2964
|
+
static inline void
|
|
2965
|
+
carray_jit_sort_#{storage}_#{cells} (#{storage_c_type(storage)} *w)
|
|
2966
|
+
{
|
|
2967
|
+
#{pairs}
|
|
2968
|
+
}
|
|
2969
|
+
|
|
2970
|
+
C
|
|
2971
|
+
end
|
|
2972
|
+
|
|
2973
|
+
def intrinsic_insertion_helper (storage)
|
|
2974
|
+
cell = storage_c_type(storage)
|
|
2975
|
+
<<~C
|
|
2976
|
+
/* `sort(w)` past the length the networks cover: an insertion sort,
|
|
2977
|
+
which is a loop. The tables would keep growing and what a
|
|
2978
|
+
network buys shrinks -- long before this there are more
|
|
2979
|
+
comparators than the machine has registers. */
|
|
2980
|
+
static inline void
|
|
2981
|
+
carray_jit_sort_#{storage} (#{cell} *w, int64_t n)
|
|
2982
|
+
{
|
|
2983
|
+
for ( int64_t k = 1; k < n; k++ ) {
|
|
2984
|
+
#{cell} value = w[k];
|
|
2985
|
+
int64_t j = k - 1;
|
|
2986
|
+
while ( j >= 0 && carray_jit_after_#{storage}(w[j], value) ) {
|
|
2987
|
+
w[j + 1] = w[j];
|
|
2988
|
+
j--;
|
|
2989
|
+
}
|
|
2990
|
+
w[j + 1] = value;
|
|
2991
|
+
}
|
|
2992
|
+
}
|
|
2993
|
+
|
|
2994
|
+
C
|
|
2995
|
+
end
|
|
2996
|
+
|
|
2997
|
+
# The storage type of an operand, by the name the block reached it by.
|
|
2998
|
+
def array_storage (array)
|
|
2999
|
+
@storage_types.fetch(array)
|
|
1601
3000
|
end
|
|
1602
3001
|
|
|
1603
|
-
def cast_to_storage (text, type,
|
|
3002
|
+
def cast_to_storage (text, type, storage)
|
|
1604
3003
|
# Storing into a boolean array normalises: CArray's boolean is a byte
|
|
1605
3004
|
# holding 0 or 1, and a kernel is not the place that invariant stops
|
|
1606
3005
|
# being true.
|
|
1607
|
-
return "(uint8_t)((#{text}) ? 1 : 0)" if boolean_storage?(
|
|
1608
|
-
target = storage_c_type(
|
|
3006
|
+
return "(uint8_t)((#{text}) ? 1 : 0)" if boolean_storage?(storage)
|
|
3007
|
+
target = storage_c_type(storage)
|
|
1609
3008
|
return text if COMPUTATION_C_TYPES.fetch(type) == target
|
|
1610
3009
|
"(#{target})(#{text})"
|
|
1611
3010
|
end
|
|
1612
3011
|
|
|
1613
3012
|
# The cast that takes a stored cell to the type the kernel computes in,
|
|
1614
3013
|
# or nil where the two already agree.
|
|
1615
|
-
def widening_cast (
|
|
3014
|
+
def widening_cast (storage, type)
|
|
1616
3015
|
return nil if type == :boolean
|
|
1617
3016
|
wanted = COMPUTATION_C_TYPES.fetch(type)
|
|
1618
|
-
return nil if storage_c_type(
|
|
3017
|
+
return nil if storage_c_type(storage) == wanted
|
|
1619
3018
|
"(#{wanted})"
|
|
1620
3019
|
end
|
|
1621
3020
|
|
|
1622
|
-
def boolean_storage? (
|
|
1623
|
-
|
|
3021
|
+
def boolean_storage? (storage)
|
|
3022
|
+
storage == "boolean"
|
|
1624
3023
|
end
|
|
1625
3024
|
|
|
1626
3025
|
# A Ruby name is not always a C one: `Foo::TABLE` names one thing in
|
|
@@ -1630,6 +3029,152 @@ class CArray
|
|
|
1630
3029
|
name.to_s.gsub("::", "__")
|
|
1631
3030
|
end
|
|
1632
3031
|
|
|
3032
|
+
# The identifier each name is written as where it is written on its own,
|
|
3033
|
+
# as a variable of the kernel's body.
|
|
3034
|
+
#
|
|
3035
|
+
# `c_name` is not enough there. The kernel's own parameters are `data`,
|
|
3036
|
+
# `error`, `strides`, `bounds` -- ordinary words a block may perfectly
|
|
3037
|
+
# well have used for a capture -- and C keeps a list of its own;
|
|
3038
|
+
# `RESERVED_NAMES` is already both, and was already consulted for an
|
|
3039
|
+
# index. A capture was not consulted about, and a block closing over
|
|
3040
|
+
# something called `data` compiled to C that redeclared the kernel's own
|
|
3041
|
+
# parameter. A leading underscore is C's to reserve as well, which is
|
|
3042
|
+
# what the made-up names here start with. And a C name that starts
|
|
3043
|
+
# `carray_jit_` is the generator's, whoever wrote it in Ruby.
|
|
3044
|
+
#
|
|
3045
|
+
# An index is refused for this rather than moved, because its name is
|
|
3046
|
+
# written in the block and read back in messages. A capture's is not:
|
|
3047
|
+
# what it is called in the C is nobody's business, so it is moved out of
|
|
3048
|
+
# the way instead of the caller being asked to rename a variable in code
|
|
3049
|
+
# that has nothing to do with the kernel.
|
|
3050
|
+
#
|
|
3051
|
+
# Worked out for the whole kernel at once, because where a moved name
|
|
3052
|
+
# lands is not a question about that name alone: a block closing over
|
|
3053
|
+
# both `data` and `carray_jit_name_data` would otherwise have the first
|
|
3054
|
+
# move onto the second. So every name that stays is claimed first, and
|
|
3055
|
+
# a move that would collide keeps going.
|
|
3056
|
+
#
|
|
3057
|
+
# Only a name that had to move moves, so the C a reader sees is what it
|
|
3058
|
+
# was. A decorated name (`p_a`, `a_s0`, `m_a`) could not collide with a
|
|
3059
|
+
# parameter or a keyword and is left alone.
|
|
3060
|
+
def bare_names
|
|
3061
|
+
@bare_names ||= begin
|
|
3062
|
+
names = (@reals + @integers + @complexes + @unsigned_integers +
|
|
3063
|
+
@address_arrays + @address_functions.keys).uniq
|
|
3064
|
+
taken = names.map { |name| c_name(name) }
|
|
3065
|
+
names.each_with_object({}) do |name, found|
|
|
3066
|
+
text = c_name(name)
|
|
3067
|
+
if RESERVED_NAMES.include?(name.to_sym) || text.start_with?("_") ||
|
|
3068
|
+
text.start_with?("carray_jit_")
|
|
3069
|
+
# Not the bare `carray_jit_` prefix: `carray_jit_error` is the
|
|
3070
|
+
# error flag, and a capture called `error` would land on it.
|
|
3071
|
+
text = "carray_jit_name_#{text}"
|
|
3072
|
+
text = "#{text}_" while taken.include?(text)
|
|
3073
|
+
taken << text
|
|
3074
|
+
end
|
|
3075
|
+
found[name] = text
|
|
3076
|
+
end
|
|
3077
|
+
end
|
|
3078
|
+
end
|
|
3079
|
+
|
|
3080
|
+
def bare_name (name)
|
|
3081
|
+
bare_names.fetch(name) { c_name(name) }
|
|
3082
|
+
end
|
|
3083
|
+
|
|
3084
|
+
# A parameter the body assigns is a local of the same name from the
|
|
3085
|
+
# top of the body on, so the C parameter it starts from goes under a
|
|
3086
|
+
# name of its own -- one only this generator writes. Every other
|
|
3087
|
+
# name is what it always was.
|
|
3088
|
+
def parameter_c_name (name)
|
|
3089
|
+
return bare_name(name) unless @analyzer.assigned_parameters.include?(name.to_sym)
|
|
3090
|
+
"carray_jit_parameter_#{c_name(name)}"
|
|
3091
|
+
end
|
|
3092
|
+
|
|
3093
|
+
# The names this generator writes of its own, beside the kernel's
|
|
3094
|
+
# parameters and C's keywords: every decoration of every array the
|
|
3095
|
+
# kernel reaches, at every axis it has, and a borrowed function's type.
|
|
3096
|
+
# All of them, whether or not this kernel ends up writing each one, so
|
|
3097
|
+
# that the set is settled before anything is emitted.
|
|
3098
|
+
def decorated_names
|
|
3099
|
+
@decorated_names ||=
|
|
3100
|
+
@arrays.flat_map { |array|
|
|
3101
|
+
[pointer_name(array), mask_pointer_name(array)] +
|
|
3102
|
+
(0...array_rank(array)).flat_map { |axis|
|
|
3103
|
+
[stride_name(array, axis), mask_stride_name(array, axis),
|
|
3104
|
+
extent_name(array, axis)]
|
|
3105
|
+
}
|
|
3106
|
+
} + @address_functions.keys.map { |name| c_function_type_name(name) }
|
|
3107
|
+
end
|
|
3108
|
+
|
|
3109
|
+
# Whether a name is one the generator writes, or could. Everything it
|
|
3110
|
+
# makes up that is not a parameter, a keyword or a decoration has `__`
|
|
3111
|
+
# in it or starts `carray_jit_` -- the suffixes a local's bindings take,
|
|
3112
|
+
# an inner index's second name, the temporaries -- so those two are
|
|
3113
|
+
# taken whole.
|
|
3114
|
+
def generated_name? (text)
|
|
3115
|
+
RESERVED_NAMES.include?(text.to_sym) || text.include?("__") ||
|
|
3116
|
+
text.start_with?("carray_jit_") || decorated_names.include?(text)
|
|
3117
|
+
end
|
|
3118
|
+
|
|
3119
|
+
# The C name of each local the block assigns, by its Ruby name. A local
|
|
3120
|
+
# is ordinary Ruby and may be called anything, including a name the
|
|
3121
|
+
# generator writes -- `a_n0`, `error`, `int`, `x__2` -- and a local of
|
|
3122
|
+
# that name declared in the kernel's body hid what it named. So it is
|
|
3123
|
+
# moved, to `carray_jit_name<K>_<name>`, K counting the moved locals from
|
|
3124
|
+
# 1 in the order the body first assigns them, so the C -- and the cache
|
|
3125
|
+
# key it is part of -- is the same every time. One that does not
|
|
3126
|
+
# collide keeps the name the block gave it, which is what a reader of
|
|
3127
|
+
# the C looks for.
|
|
3128
|
+
#
|
|
3129
|
+
# The number is what keeps two moved locals apart once a suffix is on
|
|
3130
|
+
# them. A Ruby identifier cannot start with a digit, so the digits after
|
|
3131
|
+
# `carray_jit_name` run to the next `_`, and a different K is a different
|
|
3132
|
+
# C name whatever `__2`, `__p0` or `__mask` follows. A C name starting
|
|
3133
|
+
# `carray_jit_` is the generator's, so no capture is spelled like one.
|
|
3134
|
+
def local_c_names
|
|
3135
|
+
@local_c_names ||= begin
|
|
3136
|
+
moved = 0
|
|
3137
|
+
assigned_locals(@analyzer.body).uniq.to_h { |name|
|
|
3138
|
+
text = name.to_s
|
|
3139
|
+
text = "carray_jit_name#{moved += 1}_#{text}" if generated_name?(text)
|
|
3140
|
+
[name, text]
|
|
3141
|
+
}
|
|
3142
|
+
end
|
|
3143
|
+
end
|
|
3144
|
+
|
|
3145
|
+
# Every name the body binds, in the order it first binds them. An array
|
|
3146
|
+
# the body made is one of these: it is a local that happens to have
|
|
3147
|
+
# cells, and it is moved off the generator's own names for the same
|
|
3148
|
+
# reason and by the same numbering.
|
|
3149
|
+
def assigned_locals (node)
|
|
3150
|
+
own = case node
|
|
3151
|
+
when Assignment, LocalArrayDeclaration then [node.name]
|
|
3152
|
+
else []
|
|
3153
|
+
end
|
|
3154
|
+
own + node.children.compact.flat_map { |child| assigned_locals(child) }
|
|
3155
|
+
end
|
|
3156
|
+
|
|
3157
|
+
# A local's C variable is its name, moved or not, and then what says
|
|
3158
|
+
# which of its variables: `__2` for its second binding, `__p0` for a
|
|
3159
|
+
# partial accumulator of a split fold -- `carray_jit_name_error__2`.
|
|
3160
|
+
def local_c_name (name, binding, lane = nil)
|
|
3161
|
+
local_c_names.fetch(name) + (binding == 1 ? "" : "__#{binding}") +
|
|
3162
|
+
(lane ? "__p#{lane}" : "")
|
|
3163
|
+
end
|
|
3164
|
+
|
|
3165
|
+
# An index is refused where a local is moved: its name is the block's
|
|
3166
|
+
# text, and the messages speak of it. The parameters and C's keywords
|
|
3167
|
+
# were refused as the block was read; what is refused here needed the
|
|
3168
|
+
# arrays first.
|
|
3169
|
+
def refuse_generated_indices
|
|
3170
|
+
taken = @analyzer.index_names.select { |name| generated_name?(c_name(name)) }
|
|
3171
|
+
return if taken.empty?
|
|
3172
|
+
raise Unsupported.new(
|
|
3173
|
+
"#{taken.map { |name| "`#{name}`" }.join(', ')} " \
|
|
3174
|
+
"#{taken.size == 1 ? 'is a name' : 'are names'} the kernel's own C " \
|
|
3175
|
+
"uses, so #{taken.size == 1 ? 'it cannot be an index' : 'they cannot be indices'}")
|
|
3176
|
+
end
|
|
3177
|
+
|
|
1633
3178
|
def pointer_name (array)
|
|
1634
3179
|
"p_#{c_name(array)}"
|
|
1635
3180
|
end
|
|
@@ -1652,8 +3197,37 @@ class CArray
|
|
|
1652
3197
|
# what the kernel raises. Nothing in the C reaches Ruby to do it: the
|
|
1653
3198
|
# function still returns a number and touches no Ruby value, which is
|
|
1654
3199
|
# what lets its address be handed to a library, or called off the GVL.
|
|
3200
|
+
# The widths `clamp` is emitted for: the suffix its helper takes, the C
|
|
3201
|
+
# type, and whether that type has a NaN to refuse.
|
|
3202
|
+
CLAMP_C_TYPES = {
|
|
3203
|
+
:double => ["double", "double", true],
|
|
3204
|
+
:float => ["float", "float", true],
|
|
3205
|
+
:int64 => ["int64", "int64_t", false],
|
|
3206
|
+
:uint64 => ["uint64", "uint64_t", false],
|
|
3207
|
+
}.freeze
|
|
3208
|
+
|
|
3209
|
+
# The widths `Math.gamma` is emitted for: the helper's suffix, the C
|
|
3210
|
+
# type, and the C function it falls through to.
|
|
3211
|
+
GAMMA_C_TYPES = {
|
|
3212
|
+
:double => ["double", "double", "tgamma", "modf"],
|
|
3213
|
+
:float => ["float", "float", "tgammaf", "modff"],
|
|
3214
|
+
}.freeze
|
|
3215
|
+
|
|
3216
|
+
# Ruby answers `Math.gamma` for a small whole number from a table of
|
|
3217
|
+
# exact values rather than from tgamma, so the table is written into
|
|
3218
|
+
# the C -- filled from the Ruby that is compiling, the way `Math::PI`
|
|
3219
|
+
# is emitted as the double Ruby would have used. Its length is Ruby's
|
|
3220
|
+
# own: the table covers 1 through 23, and tgamma has the rest.
|
|
3221
|
+
GAMMA_TABLE_LIMIT = 23
|
|
3222
|
+
|
|
1655
3223
|
ERROR_FLAG = "carray_jit_error"
|
|
1656
3224
|
|
|
3225
|
+
# The storage class the flag and the hook beside it are declared with.
|
|
3226
|
+
# C11's, which is what every compiler this gem drives is given; MSVC's
|
|
3227
|
+
# `__declspec(thread)` is not written here because the build flags are
|
|
3228
|
+
# already gcc's and cl has never been one of them.
|
|
3229
|
+
THREAD_LOCAL = "_Thread_local ".freeze
|
|
3230
|
+
|
|
1657
3231
|
# A computation type this generator has no C for. Nothing reaches here
|
|
1658
3232
|
# today -- every type TypeAssignment can assign is named at each of the
|
|
1659
3233
|
# sites that calls this -- and that is the point: a type added later
|
|
@@ -1666,18 +3240,19 @@ class CArray
|
|
|
1666
3240
|
end
|
|
1667
3241
|
|
|
1668
3242
|
def error_argument
|
|
3243
|
+
# Asking for the slot is what says this body can report, and every
|
|
3244
|
+
# place that can -- a checked subscript, the division helpers, a
|
|
3245
|
+
# `raise`, a pasted function that takes the flag -- asks here. So the
|
|
3246
|
+
# loops around it learn it from the statements they hold, which are
|
|
3247
|
+
# emitted before the loop's guard is written. A function's loops ask
|
|
3248
|
+
# too: they stop on its flag the way a kernel's stop on the slot.
|
|
3249
|
+
@body_reports = true
|
|
1669
3250
|
if @in_function
|
|
1670
3251
|
@uses_error_flag = true
|
|
1671
3252
|
# A pasted body reports into the kernel's slot, which reaches it as
|
|
1672
3253
|
# a parameter; a standalone one into the flag in its own object.
|
|
1673
3254
|
return @error_parameter ? ERROR_FLAG : "&#{ERROR_FLAG}"
|
|
1674
3255
|
end
|
|
1675
|
-
# Asking for the slot is what says this body can report, and every
|
|
1676
|
-
# place that can -- a checked subscript, the division helpers, a
|
|
1677
|
-
# `raise`, a pasted function that takes the flag -- asks here. So the
|
|
1678
|
-
# loops above learn it from the statements they will hold, which are
|
|
1679
|
-
# emitted before the loop is opened.
|
|
1680
|
-
@body_reports = true
|
|
1681
3256
|
@masked_flag ? "(#{@masked_flag} ? (int32_t *) 0 : error)" : "error"
|
|
1682
3257
|
end
|
|
1683
3258
|
|
|
@@ -1787,7 +3362,7 @@ class CArray
|
|
|
1787
3362
|
return "#{mask_pointer_name(array)}[#{terms.join(' + ')}]"
|
|
1788
3363
|
end
|
|
1789
3364
|
|
|
1790
|
-
type = storage_c_type(array)
|
|
3365
|
+
type = storage_c_type(array_storage(array))
|
|
1791
3366
|
outer = (0...(count - 1)).map { |axis|
|
|
1792
3367
|
index, offset = subscripts[axis]
|
|
1793
3368
|
"(#{index_expression(array, axis, index, offset)}) * " \
|
|
@@ -1810,11 +3385,30 @@ class CArray
|
|
|
1810
3385
|
# Emits `node` so that its value has type `target`, inserting a cast
|
|
1811
3386
|
# only where the type actually changes.
|
|
1812
3387
|
def emit (node, target)
|
|
3388
|
+
note_unsigned_capture(node, target)
|
|
3389
|
+
return emit_complex_read_power(node, target).first if complex_read_power?(node, target)
|
|
3390
|
+
return emit_rounded_real(node, target).first if rounded_real?(node, target)
|
|
1813
3391
|
widen(*emit_raw(node), node.type, target).first
|
|
1814
3392
|
end
|
|
1815
3393
|
|
|
3394
|
+
# A signed capture read where a uint64 is computed -- absorbed into one,
|
|
3395
|
+
# or compared with one. Kernel refuses a negative value for it.
|
|
3396
|
+
def note_unsigned_capture (node, target)
|
|
3397
|
+
return unless node.is_a?(CaptureRead) && target == :uint64 &&
|
|
3398
|
+
@integers.include?(node.name)
|
|
3399
|
+
@unsigned_captures |= [node.name]
|
|
3400
|
+
end
|
|
3401
|
+
|
|
1816
3402
|
def emit_operand (node, target, parent_precedence, right_side = false)
|
|
1817
|
-
|
|
3403
|
+
note_unsigned_capture(node, target)
|
|
3404
|
+
text, precedence =
|
|
3405
|
+
if complex_read_power?(node, target)
|
|
3406
|
+
emit_complex_read_power(node, target)
|
|
3407
|
+
elsif rounded_real?(node, target)
|
|
3408
|
+
emit_rounded_real(node, target)
|
|
3409
|
+
else
|
|
3410
|
+
widen(*emit_raw(node), node.type, target)
|
|
3411
|
+
end
|
|
1818
3412
|
parenthesize(text, precedence, right_side ? parent_precedence + 1 : parent_precedence)
|
|
1819
3413
|
end
|
|
1820
3414
|
|
|
@@ -1822,13 +3416,32 @@ class CArray
|
|
|
1822
3416
|
# most of these on its own, but writing them down is what makes the
|
|
1823
3417
|
# dumped source say where the type changed -- and `int64_t` to
|
|
1824
3418
|
# `double _Complex` is a conversion worth seeing.
|
|
3419
|
+
#
|
|
3420
|
+
# `int64` and `uint64` are the exception the rank cannot express.
|
|
3421
|
+
# They are the same width and differ only in sign, so the rank puts
|
|
3422
|
+
# one above the other and a `uint64` reaching an `int64` context is a
|
|
3423
|
+
# step *down* it -- left uncast, on the reasoning that C would do it
|
|
3424
|
+
# anyway. C does not: in `i < n` with `i` an `int64_t` and `n` a
|
|
3425
|
+
# `size_t`, the conversion C performs is on the other operand, and a
|
|
3426
|
+
# loop counting down from -1 then runs against a bound of 2**64 - 1.
|
|
3427
|
+
# The compiler says so as `-Wsign-compare`, which is the warning every
|
|
3428
|
+
# generated loop over a `size_t` extent carried.
|
|
1825
3429
|
def widen (text, precedence, from, to)
|
|
1826
3430
|
here, there = NUMERIC_RANK[from], NUMERIC_RANK[to]
|
|
1827
|
-
return [text, precedence] if here.nil? || there.nil?
|
|
3431
|
+
return [text, precedence] if here.nil? || there.nil?
|
|
3432
|
+
unless there > here || crosses_sign?(from, to)
|
|
3433
|
+
return [text, precedence]
|
|
3434
|
+
end
|
|
1828
3435
|
["(#{COMPUTATION_C_TYPES.fetch(to)})" \
|
|
1829
3436
|
"#{parenthesize(text, precedence, LEAF_PRECEDENCE)}", UNARY_PRECEDENCE]
|
|
1830
3437
|
end
|
|
1831
3438
|
|
|
3439
|
+
def crosses_sign? (from, to)
|
|
3440
|
+
from != to &&
|
|
3441
|
+
TypeAssignment::INTEGER_TYPES.include?(from) &&
|
|
3442
|
+
TypeAssignment::INTEGER_TYPES.include?(to)
|
|
3443
|
+
end
|
|
3444
|
+
|
|
1832
3445
|
def parenthesize (text, precedence, needed)
|
|
1833
3446
|
precedence < needed ? "(#{text})" : text
|
|
1834
3447
|
end
|
|
@@ -1849,8 +3462,11 @@ class CArray
|
|
|
1849
3462
|
when BoundsValue then ["bounds[#{node.slot}]", LEAF_PRECEDENCE]
|
|
1850
3463
|
when ZeroLike
|
|
1851
3464
|
[ZEROES.fetch(node.type), LEAF_PRECEDENCE]
|
|
1852
|
-
when LocalRead then [node.
|
|
1853
|
-
|
|
3465
|
+
when LocalRead then [local_c_name(node.name, node.binding, node.lane),
|
|
3466
|
+
LEAF_PRECEDENCE]
|
|
3467
|
+
when CaptureRead
|
|
3468
|
+
note_unsigned_capture(node, node.type)
|
|
3469
|
+
[parameter_c_name(node.name), LEAF_PRECEDENCE]
|
|
1854
3470
|
# A read is widened to the type the kernel computes in, because that
|
|
1855
3471
|
# is the type the Ruby loop computes in: reading a float32 cell in
|
|
1856
3472
|
# Ruby gives a Float, and reading an int32 cell gives an Integer that
|
|
@@ -1861,9 +3477,20 @@ class CArray
|
|
|
1861
3477
|
# A boolean cell is a byte holding 0 or 1, so it is already the value
|
|
1862
3478
|
# it stands for. Keeping it that way on the way out is this kernel's
|
|
1863
3479
|
# business, and cast_to_storage does it.
|
|
3480
|
+
when IntrinsicCall
|
|
3481
|
+
[intrinsic_reference(node), LEAF_PRECEDENCE]
|
|
3482
|
+
when LocalArrayRead
|
|
3483
|
+
cell = local_array_reference(node)
|
|
3484
|
+
widening = widening_cast(node.storage, node.type)
|
|
3485
|
+
if widening
|
|
3486
|
+
["#{widening}#{parenthesize(cell, LEAF_PRECEDENCE, UNARY_PRECEDENCE)}",
|
|
3487
|
+
UNARY_PRECEDENCE]
|
|
3488
|
+
else
|
|
3489
|
+
[cell, LEAF_PRECEDENCE]
|
|
3490
|
+
end
|
|
1864
3491
|
when ElementRead
|
|
1865
3492
|
cell = cell_reference(node.array, node.subscripts)
|
|
1866
|
-
widening = widening_cast(node.array, node.type)
|
|
3493
|
+
widening = widening_cast(array_storage(node.array), node.type)
|
|
1867
3494
|
if widening
|
|
1868
3495
|
["#{widening}#{parenthesize(cell, LEAF_PRECEDENCE, UNARY_PRECEDENCE)}",
|
|
1869
3496
|
UNARY_PRECEDENCE]
|
|
@@ -1871,6 +3498,11 @@ class CArray
|
|
|
1871
3498
|
[cell, LEAF_PRECEDENCE]
|
|
1872
3499
|
end
|
|
1873
3500
|
when MaskTest then emit_mask_test(node)
|
|
3501
|
+
when LocalArrayMaskTest then emit_local_array_mask_test(node)
|
|
3502
|
+
when NumericPredicate then emit_numeric_predicate(node)
|
|
3503
|
+
when PointerTest
|
|
3504
|
+
["#{node.name} #{node.null ? '==' : '!='} NULL", PRECEDENCE.fetch(:==)]
|
|
3505
|
+
when Clamp then emit_clamp(node)
|
|
1874
3506
|
when UnaryMinus
|
|
1875
3507
|
operand, precedence = emit_raw(node.operand)
|
|
1876
3508
|
["-#{parenthesize(operand, precedence, UNARY_PRECEDENCE)}", UNARY_PRECEDENCE]
|
|
@@ -1891,7 +3523,11 @@ class CArray
|
|
|
1891
3523
|
when Power then emit_power(node)
|
|
1892
3524
|
when MathCall then emit_math_call(node)
|
|
1893
3525
|
when ArrayAddress
|
|
1894
|
-
[
|
|
3526
|
+
[bare_name(node.array), LEAF_PRECEDENCE]
|
|
3527
|
+
when LocalArrayAddress
|
|
3528
|
+
# The array's own name, which is the address: C decays it at the
|
|
3529
|
+
# call, and there is no buffer for the caller to have packed.
|
|
3530
|
+
[local_c_name(node.name, node.binding), LEAF_PRECEDENCE]
|
|
1895
3531
|
when PointerRead
|
|
1896
3532
|
# A pointer parameter is reached the way C reaches it: contiguous,
|
|
1897
3533
|
# from the address it was handed. No base, no stride, no bounds --
|
|
@@ -1905,7 +3541,8 @@ class CArray
|
|
|
1905
3541
|
# scalars; this one is right here, so the call is direct.
|
|
1906
3542
|
arguments = node.arguments.zip(node.parameters)
|
|
1907
3543
|
.map { |argument, parameter|
|
|
1908
|
-
if argument.is_a?(ArrayAddress)
|
|
3544
|
+
if argument.is_a?(ArrayAddress) ||
|
|
3545
|
+
argument.is_a?(LocalArrayAddress)
|
|
1909
3546
|
emit(argument, :address)
|
|
1910
3547
|
else
|
|
1911
3548
|
emit(argument, parameter.computation)
|
|
@@ -1913,11 +3550,21 @@ class CArray
|
|
|
1913
3550
|
}
|
|
1914
3551
|
arguments << ERROR_FLAG if @error_parameter
|
|
1915
3552
|
["#{@own_symbol}(#{arguments.join(', ')})", LEAF_PRECEDENCE]
|
|
3553
|
+
when RandomDraw
|
|
3554
|
+
# The state is the address of the captured array, which the
|
|
3555
|
+
# declarations above bound to a name; the draw advances it in
|
|
3556
|
+
# place, which is what leaves the generator where the kernel left
|
|
3557
|
+
# it.
|
|
3558
|
+
symbol = CArray::Rng::DRAW_FUNCTIONS
|
|
3559
|
+
.fetch(@randoms.fetch(node.generator).generator)
|
|
3560
|
+
.fetch(node.kind)
|
|
3561
|
+
["#{symbol}(#{bare_name(node.state)})", LEAF_PRECEDENCE]
|
|
1916
3562
|
when CFunctionCall
|
|
1917
3563
|
c_function = @c_functions.fetch(node.name)
|
|
1918
3564
|
arguments = node.arguments.zip(c_function.parameters)
|
|
1919
3565
|
.map { |argument, parameter|
|
|
1920
|
-
if argument.is_a?(ArrayAddress)
|
|
3566
|
+
if argument.is_a?(ArrayAddress) ||
|
|
3567
|
+
argument.is_a?(LocalArrayAddress)
|
|
1921
3568
|
emit(argument, :address)
|
|
1922
3569
|
else
|
|
1923
3570
|
emit(argument, parameter.computation)
|
|
@@ -1925,7 +3572,18 @@ class CArray
|
|
|
1925
3572
|
}
|
|
1926
3573
|
# A pasted body is reached by its symbol; an address by the local
|
|
1927
3574
|
# the declarations bound it to.
|
|
1928
|
-
called = c_function.pasted?
|
|
3575
|
+
called = if c_function.pasted?
|
|
3576
|
+
c_function.symbol
|
|
3577
|
+
elsif @in_function
|
|
3578
|
+
# Borrowed, inside a body that has no `functions` buffer
|
|
3579
|
+
# to take an address in -- so it is called by the name it
|
|
3580
|
+
# has, declared in the preamble, and the linker or the
|
|
3581
|
+
# loader resolves it. `jit_extern` opened the library to
|
|
3582
|
+
# find it, so by here the symbol is in the process.
|
|
3583
|
+
c_function.symbol
|
|
3584
|
+
else
|
|
3585
|
+
bare_name(node.name)
|
|
3586
|
+
end
|
|
1929
3587
|
# And one that can report a failure is handed the slot this kernel
|
|
1930
3588
|
# is watching -- null under a masked cell, where the value written
|
|
1931
3589
|
# is out of contract and a division by zero there was not asked
|
|
@@ -1941,11 +3599,56 @@ class CArray
|
|
|
1941
3599
|
end
|
|
1942
3600
|
|
|
1943
3601
|
# `a[i] == UNDEF` reads the mask byte, never the value.
|
|
3602
|
+
# `Math.gamma` is Ruby's, which is tgamma with two things around it: a
|
|
3603
|
+
# table of exact values for a small integer argument, and the domain
|
|
3604
|
+
# error Ruby raises where tgamma answers with a NaN.
|
|
3605
|
+
def emit_gamma (node)
|
|
3606
|
+
type = node.type
|
|
3607
|
+
unhandled_type(node, "Math.gamma") unless GAMMA_C_TYPES.key?(type)
|
|
3608
|
+
@gamma_types |= [type]
|
|
3609
|
+
suffix, = GAMMA_C_TYPES.fetch(type)
|
|
3610
|
+
["carray_jit_gamma_#{suffix}(#{emit(node.arguments.first, type)}, " \
|
|
3611
|
+
"#{error_argument})", LEAF_PRECEDENCE]
|
|
3612
|
+
end
|
|
3613
|
+
|
|
3614
|
+
# `x.clamp(low, high)` through a helper of its own width, so that each
|
|
3615
|
+
# of the three is computed once and the two failures Ruby raises for
|
|
3616
|
+
# are reported where they happen.
|
|
3617
|
+
def emit_clamp (node)
|
|
3618
|
+
type = node.type
|
|
3619
|
+
unhandled_type(node, "clamp") unless CLAMP_C_TYPES.key?(type)
|
|
3620
|
+
@clamp_types |= [type]
|
|
3621
|
+
["carray_jit_clamp_#{CLAMP_C_TYPES.fetch(type).first}(" \
|
|
3622
|
+
"#{emit(node.value, type)}, #{emit(node.low, type)}, " \
|
|
3623
|
+
"#{emit(node.high, type)}, #{error_argument})", LEAF_PRECEDENCE]
|
|
3624
|
+
end
|
|
3625
|
+
|
|
3626
|
+
# `isnan` and `isfinite` are C's own, and take a number of any width.
|
|
3627
|
+
# A Complex answers `finite?` the way Ruby answers it -- both parts
|
|
3628
|
+
# finite -- through a helper, so that the number is computed once.
|
|
3629
|
+
# An Integer is finite whatever it holds, and goes through `isfinite`
|
|
3630
|
+
# all the same rather than being answered here: the read it stands for
|
|
3631
|
+
# is a read, and dropping it would drop what the read reports.
|
|
3632
|
+
def emit_numeric_predicate (node)
|
|
3633
|
+
type = node.operand.type
|
|
3634
|
+
function = node.name == :nan? ? "isnan" : "isfinite"
|
|
3635
|
+
if complex_type?(type)
|
|
3636
|
+
return emit_helper_call("complex_finite", [emit(node.operand, type)],
|
|
3637
|
+
type == :float_complex)
|
|
3638
|
+
end
|
|
3639
|
+
["#{function}(#{emit(node.operand, type)})", LEAF_PRECEDENCE]
|
|
3640
|
+
end
|
|
3641
|
+
|
|
1944
3642
|
def emit_mask_test (node)
|
|
1945
3643
|
cell = cell_reference(node.array, node.subscripts, :mask)
|
|
1946
3644
|
node.negated ? ["! #{cell}", UNARY_PRECEDENCE] : [cell, LEAF_PRECEDENCE]
|
|
1947
3645
|
end
|
|
1948
3646
|
|
|
3647
|
+
def emit_local_array_mask_test (node)
|
|
3648
|
+
cell = local_array_reference(node, :mask)
|
|
3649
|
+
node.negated ? ["! #{cell}", UNARY_PRECEDENCE] : [cell, LEAF_PRECEDENCE]
|
|
3650
|
+
end
|
|
3651
|
+
|
|
1949
3652
|
# Float#floor and friends hand back an Integer in Ruby, so the C rounds
|
|
1950
3653
|
# and then narrows. An Integer receiver is already there.
|
|
1951
3654
|
def emit_conversion (node)
|
|
@@ -1957,7 +3660,28 @@ class CArray
|
|
|
1957
3660
|
UNARY_PRECEDENCE]
|
|
1958
3661
|
end
|
|
1959
3662
|
return [emit(node.operand, :double), LEAF_PRECEDENCE] if node.result_type == :double
|
|
1960
|
-
|
|
3663
|
+
@uses_whole_number = true
|
|
3664
|
+
["carray_jit_whole_int64(#{node.name}(#{emit(node.operand, :double)}), " \
|
|
3665
|
+
"#{error_argument})", LEAF_PRECEDENCE]
|
|
3666
|
+
end
|
|
3667
|
+
|
|
3668
|
+
# The same rounding where the answer is wanted as a real number: the
|
|
3669
|
+
# Integer Ruby answers is exact however large, and a float cell holds
|
|
3670
|
+
# it as the rounded double already is, so it does not go through int64
|
|
3671
|
+
# and is not held to its range.
|
|
3672
|
+
def rounded_real? (node, target)
|
|
3673
|
+
node.is_a?(Conversion) && node.result_type == :int64 &&
|
|
3674
|
+
node.type == :int64 &&
|
|
3675
|
+
!TypeAssignment::INTEGER_TYPES.include?(node.operand.type) &&
|
|
3676
|
+
[:double, :float].include?(target)
|
|
3677
|
+
end
|
|
3678
|
+
|
|
3679
|
+
def emit_rounded_real (node, target)
|
|
3680
|
+
@uses_whole_number = true
|
|
3681
|
+
text = "carray_jit_whole_real(#{node.name}(#{emit(node.operand, :double)}), " \
|
|
3682
|
+
"#{error_argument})"
|
|
3683
|
+
return [text, LEAF_PRECEDENCE] if target == :double
|
|
3684
|
+
["(float)#{text}", UNARY_PRECEDENCE]
|
|
1961
3685
|
end
|
|
1962
3686
|
|
|
1963
3687
|
# creal and cimag are the way out of the complex type; conj stays in it.
|
|
@@ -1982,6 +3706,24 @@ class CArray
|
|
|
1982
3706
|
end
|
|
1983
3707
|
end
|
|
1984
3708
|
|
|
3709
|
+
# A real power read where a complex is wanted -- stored in a complex
|
|
3710
|
+
# cell or joined with a complex. pow answers a negative base raised to
|
|
3711
|
+
# a power that is not an integer with NaN; Ruby answers with a Complex,
|
|
3712
|
+
# and as the value is going to be complex anyway, so does the kernel.
|
|
3713
|
+
# Read as a real, the power stays pow's.
|
|
3714
|
+
def complex_read_power? (node, target)
|
|
3715
|
+
node.is_a?(Power) && [:double, :float].include?(node.type) &&
|
|
3716
|
+
complex_type?(target)
|
|
3717
|
+
end
|
|
3718
|
+
|
|
3719
|
+
def emit_complex_read_power (node, target)
|
|
3720
|
+
@uses_real_power_complex = true
|
|
3721
|
+
text = "carray_jit_real_power_complex(#{emit(node.base, :double)}, " \
|
|
3722
|
+
"#{emit(node.exponent, :double)})"
|
|
3723
|
+
return [text, LEAF_PRECEDENCE] if target == :complex
|
|
3724
|
+
["(float _Complex)#{text}", UNARY_PRECEDENCE]
|
|
3725
|
+
end
|
|
3726
|
+
|
|
1985
3727
|
# An integer power is squared out rather than sent through pow, whose
|
|
1986
3728
|
# double result would not be the exact integer Ruby gives.
|
|
1987
3729
|
#
|
|
@@ -2018,7 +3760,7 @@ class CArray
|
|
|
2018
3760
|
LEAF_PRECEDENCE]
|
|
2019
3761
|
end
|
|
2020
3762
|
|
|
2021
|
-
# cabs, fabs and
|
|
3763
|
+
# cabs, fabs and an integer helper are three rather than one because C has
|
|
2022
3764
|
# no generic for them, so this names each type instead of letting one of
|
|
2023
3765
|
# them be what a type it has not heard of falls into.
|
|
2024
3766
|
def emit_absolute_value (node)
|
|
@@ -2033,8 +3775,10 @@ class CArray
|
|
|
2033
3775
|
case node.type
|
|
2034
3776
|
when :double then ["fabs(#{emit(node.operand, :double)})", LEAF_PRECEDENCE]
|
|
2035
3777
|
when :float then ["fabsf(#{emit(node.operand, :float)})", LEAF_PRECEDENCE]
|
|
2036
|
-
when :int64
|
|
2037
|
-
|
|
3778
|
+
when :int64
|
|
3779
|
+
@uses_integer_abs = true
|
|
3780
|
+
["carray_jit_integer_abs(#{emit(node.operand, :int64)})", LEAF_PRECEDENCE]
|
|
3781
|
+
# An unsigned number is its own magnitude, and a signed helper would take it
|
|
2038
3782
|
# through a signed type on the way.
|
|
2039
3783
|
when :uint64 then [emit(node.operand, :uint64), LEAF_PRECEDENCE]
|
|
2040
3784
|
else unhandled_type(node, "abs")
|
|
@@ -2051,6 +3795,8 @@ class CArray
|
|
|
2051
3795
|
return emit_integer_division(node) if node.operator == :/ &&
|
|
2052
3796
|
TypeAssignment::INTEGER_TYPES.include?(node.type)
|
|
2053
3797
|
return emit_modulo(node) if node.operator == :%
|
|
3798
|
+
return emit_shift(node) if [:<<, :>>].include?(node.operator) &&
|
|
3799
|
+
TypeAssignment::INTEGER_TYPES.include?(node.type)
|
|
2054
3800
|
return emit_complex_binary(node) if complex_type?(node.type)
|
|
2055
3801
|
|
|
2056
3802
|
operand_type =
|
|
@@ -2090,10 +3836,13 @@ class CArray
|
|
|
2090
3836
|
# and dividing by one divides each part.
|
|
2091
3837
|
#
|
|
2092
3838
|
# Subtraction is the exception: there the zero really is subtracted, in
|
|
2093
|
-
# Ruby as in C, so `z - x` and `x - z` are C's own operator.
|
|
2094
|
-
#
|
|
2095
|
-
#
|
|
2096
|
-
#
|
|
3839
|
+
# Ruby as in C, so `z - x` and `x - z` are C's own operator. `x * z`
|
|
3840
|
+
# is not the exception it looks like: Ruby coerces the x and multiplies
|
|
3841
|
+
# out in full, as it does two Complex numbers, and a full product in
|
|
3842
|
+
# Ruby is safe_mul's part by part -- a zero times an infinity a zero --
|
|
3843
|
+
# where C's `*` recovers from infinities by Annex G's rules. So both
|
|
3844
|
+
# go through complex.c's comp_mul, and `2.0 * Complex(1.0, -0.0)` and
|
|
3845
|
+
# `Complex(1.0, -0.0) * 2.0` still differ, each reproduced its own way.
|
|
2097
3846
|
#
|
|
2098
3847
|
# And a complex division is Smith's method as complex.c writes it,
|
|
2099
3848
|
# which is not what the C library's __divdc3 computes.
|
|
@@ -2114,18 +3863,7 @@ class CArray
|
|
|
2114
3863
|
if complex_type == :float_complex &&
|
|
2115
3864
|
[:*, :/].include?(node.operator) &&
|
|
2116
3865
|
complex_type?(node.left.type) && complex_type?(node.right.type)
|
|
2117
|
-
|
|
2118
|
-
if helper
|
|
2119
|
-
text, = emit_helper_call(*helper, false)
|
|
2120
|
-
else
|
|
2121
|
-
precedence = PRECEDENCE.fetch(node.operator)
|
|
2122
|
-
# Parenthesised, because the cast binds tighter than the operator:
|
|
2123
|
-
# without them it would narrow the left operand and leave the
|
|
2124
|
-
# multiplication to be worked out around it.
|
|
2125
|
-
text = "(#{emit_operand(node.left, :complex, precedence)} " \
|
|
2126
|
-
"#{node.operator} " \
|
|
2127
|
-
"#{emit_operand(node.right, :complex, precedence)})"
|
|
2128
|
-
end
|
|
3866
|
+
text, = emit_helper_call(*mixed_helper(node, :complex), false)
|
|
2129
3867
|
return ["(float _Complex)#{text}", UNARY_PRECEDENCE]
|
|
2130
3868
|
end
|
|
2131
3869
|
narrow = complex_type == :float_complex
|
|
@@ -2138,6 +3876,9 @@ class CArray
|
|
|
2138
3876
|
end
|
|
2139
3877
|
|
|
2140
3878
|
def emit_helper_call (name, arguments, narrow)
|
|
3879
|
+
COMPLEX_HELPER_NEEDS.fetch(name, []).each do |needed|
|
|
3880
|
+
@complex_helpers |= [[needed, narrow]]
|
|
3881
|
+
end
|
|
2141
3882
|
@complex_helpers |= [[name, narrow]]
|
|
2142
3883
|
prefix = narrow ? "carray_jit_f_" : "carray_jit_"
|
|
2143
3884
|
["#{prefix}#{name}(#{arguments.join(', ')})", LEAF_PRECEDENCE]
|
|
@@ -2168,10 +3909,20 @@ class CArray
|
|
|
2168
3909
|
when :*
|
|
2169
3910
|
if real_type?(right)
|
|
2170
3911
|
["complex_mul_real", [emit(left, ctype), emit(right, rtype)]]
|
|
3912
|
+
elsif real_type?(left)
|
|
3913
|
+
["real_multiply_complex", [emit(left, rtype), emit(right, ctype)]]
|
|
3914
|
+
else
|
|
3915
|
+
["complex_multiply", [emit(left, ctype), emit(right, ctype)]]
|
|
2171
3916
|
end
|
|
2172
3917
|
end
|
|
2173
3918
|
end
|
|
2174
3919
|
|
|
3920
|
+
# The helpers that call another, which has to be emitted above them.
|
|
3921
|
+
COMPLEX_HELPER_NEEDS = {
|
|
3922
|
+
"complex_multiply" => ["safe_multiply"],
|
|
3923
|
+
"real_multiply_complex" => ["safe_multiply"],
|
|
3924
|
+
}.freeze
|
|
3925
|
+
|
|
2175
3926
|
def complex_type? (type)
|
|
2176
3927
|
TypeAssignment.complex?(type)
|
|
2177
3928
|
end
|
|
@@ -2220,6 +3971,7 @@ class CArray
|
|
|
2220
3971
|
return ["(float _Complex)#{text}", UNARY_PRECEDENCE]
|
|
2221
3972
|
end
|
|
2222
3973
|
function = node.name.to_s
|
|
3974
|
+
return emit_gamma(node) if function == "gamma"
|
|
2223
3975
|
function += "f" if node.type == :float
|
|
2224
3976
|
arguments = node.arguments.map { |argument| emit(argument, node.type) }
|
|
2225
3977
|
["#{function}(#{arguments.join(', ')})", LEAF_PRECEDENCE]
|
|
@@ -2256,6 +4008,19 @@ class CArray
|
|
|
2256
4008
|
"#{emit(node.right, :int64)}, #{error_argument})", LEAF_PRECEDENCE]
|
|
2257
4009
|
end
|
|
2258
4010
|
|
|
4011
|
+
# A shift is Integer's -- see carray_jit_shift_left.
|
|
4012
|
+
def emit_shift (node)
|
|
4013
|
+
@uses_shift = true
|
|
4014
|
+
direction = node.operator == :<< ? "left" : "right"
|
|
4015
|
+
prefix = node.type == :uint64 ? "carray_jit_unsigned_shift_" : "carray_jit_shift_"
|
|
4016
|
+
count = if node.right.type == :uint64
|
|
4017
|
+
"carray_jit_shift_count(#{emit(node.right, :uint64)})"
|
|
4018
|
+
else
|
|
4019
|
+
emit(node.right, :int64)
|
|
4020
|
+
end
|
|
4021
|
+
["#{prefix}#{direction}(#{emit(node.left, node.type)}, #{count})", LEAF_PRECEDENCE]
|
|
4022
|
+
end
|
|
4023
|
+
|
|
2259
4024
|
# `%` floors, so it is not C's `%` -- see the helper above.
|
|
2260
4025
|
def emit_modulo (node)
|
|
2261
4026
|
if node.type == :uint64
|
|
@@ -2272,11 +4037,11 @@ class CArray
|
|
|
2272
4037
|
if node.type == :float
|
|
2273
4038
|
@uses_floor_modulo_float = true
|
|
2274
4039
|
return ["carray_jit_floor_modulo_float(#{emit(node.left, :float)}, " \
|
|
2275
|
-
"#{emit(node.right, :float)})", LEAF_PRECEDENCE]
|
|
4040
|
+
"#{emit(node.right, :float)}, #{error_argument})", LEAF_PRECEDENCE]
|
|
2276
4041
|
end
|
|
2277
4042
|
unhandled_type(node, "%") unless node.type == :double
|
|
2278
4043
|
["carray_jit_floor_modulo_real(#{emit(node.left, :double)}, " \
|
|
2279
|
-
"#{emit(node.right, :double)})", LEAF_PRECEDENCE]
|
|
4044
|
+
"#{emit(node.right, :double)}, #{error_argument})", LEAF_PRECEDENCE]
|
|
2280
4045
|
end
|
|
2281
4046
|
|
|
2282
4047
|
def power_of_two_shift (node)
|
|
@@ -2286,11 +4051,23 @@ class CArray
|
|
|
2286
4051
|
Math.log2(value).to_i
|
|
2287
4052
|
end
|
|
2288
4053
|
|
|
4054
|
+
# A literal past int64 is a uint64 if it fits one, and refused with the
|
|
4055
|
+
# message a capture of the same value gets if it does not -- written
|
|
4056
|
+
# out, it was a compiler error about a constant too large for its type.
|
|
2289
4057
|
def format_integer (value)
|
|
2290
|
-
|
|
4058
|
+
case TypeAssignment.integer_type(value)
|
|
4059
|
+
when :uint64 then "UINT64_C(#{value})"
|
|
4060
|
+
else "INT64_C(#{value})"
|
|
4061
|
+
end
|
|
2291
4062
|
end
|
|
2292
4063
|
|
|
2293
4064
|
def format_float (value, type = :double)
|
|
4065
|
+
# 1e400 is an infinity in Ruby; written out with %g it is the word
|
|
4066
|
+
# `inf`, which C does not know.
|
|
4067
|
+
if value.infinite?
|
|
4068
|
+
text = value.positive? ? "INFINITY" : "(-INFINITY)"
|
|
4069
|
+
return type == :float ? "((float)#{text})" : text
|
|
4070
|
+
end
|
|
2294
4071
|
# A float literal without the suffix is a double, and one double in an
|
|
2295
4072
|
# expression takes the whole expression with it -- so the suffix is
|
|
2296
4073
|
# what keeps a float32 kernel computing in float.
|
|
@@ -2306,9 +4083,11 @@ class CArray
|
|
|
2306
4083
|
text
|
|
2307
4084
|
end
|
|
2308
4085
|
|
|
4086
|
+
# Spelled with `__`, which is what keeps it out of a block's way: a
|
|
4087
|
+
# local with `__` in its name is moved (see `local_c_names`).
|
|
2309
4088
|
def next_temporary (prefix = "result")
|
|
2310
4089
|
@temporary_count += 1
|
|
2311
|
-
"#{prefix}#{@temporary_count}"
|
|
4090
|
+
"#{prefix}__#{@temporary_count}"
|
|
2312
4091
|
end
|
|
2313
4092
|
|
|
2314
4093
|
end
|