carray-jit 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +10 -0
  3. data/CHANGELOG.md +84 -0
  4. data/LICENSE +21 -0
  5. data/README.md +88 -0
  6. data/bin/carray-jit +194 -0
  7. data/carray-jit.gemspec +41 -0
  8. data/docs/00_Introduction.md +40 -0
  9. data/docs/01_GettingStarted.md +80 -0
  10. data/docs/02_KernelShapes.md +397 -0
  11. data/docs/03_SupportedFeatures.md +595 -0
  12. data/docs/04_Compiling.md +234 -0
  13. data/docs/05_DesignNotes.md +136 -0
  14. data/docs/06_Cheatsheet.md +177 -0
  15. data/examples/README.md +56 -0
  16. data/examples/applications/game_of_life.rb +161 -0
  17. data/examples/applications/heat_equation.rb +117 -0
  18. data/examples/applications/kepler.rb +178 -0
  19. data/examples/applications/mandelbrot.rb +151 -0
  20. data/examples/applications/moving_average.rb +124 -0
  21. data/examples/applications/partial_sums.rb +141 -0
  22. data/examples/applications/point_cloud.rb +110 -0
  23. data/examples/applications/quicksort.rb +118 -0
  24. data/examples/applications/recursion.rb +121 -0
  25. data/examples/applications/relaxation.rb +115 -0
  26. data/examples/applications/sensor_gaps.rb +118 -0
  27. data/examples/applications/sieve.rb +95 -0
  28. data/examples/applications/sobel_edges.rb +80 -0
  29. data/examples/features/01_element_wise.rb +69 -0
  30. data/examples/features/02_stencil.rb +40 -0
  31. data/examples/features/03_recurrence.rb +50 -0
  32. data/examples/features/04_thomas.rb +81 -0
  33. data/examples/features/05_reduction.rb +90 -0
  34. data/examples/features/06_jit_contract.rb +58 -0
  35. data/examples/features/07_masks.rb +55 -0
  36. data/examples/features/08_views.rb +46 -0
  37. data/examples/features/09_inspecting.rb +55 -0
  38. data/examples/features/10_complex.rb +107 -0
  39. data/examples/features/11_c_functions.rb +260 -0
  40. data/examples/features/12_sweep.rb +139 -0
  41. data/examples/features/13_cscalar.rb +80 -0
  42. data/examples/features/14_stencil_window.rb +106 -0
  43. data/examples/features/15_loops.rb +148 -0
  44. data/examples/features/16_raising.rb +69 -0
  45. data/ext/carray_jit_access/carray_jit_access.c +460 -0
  46. data/ext/carray_jit_access/extconf.rb +8 -0
  47. data/lib/carray/jit/analyzer.rb +1847 -0
  48. data/lib/carray/jit/block_reader.rb +139 -0
  49. data/lib/carray/jit/c_function.rb +777 -0
  50. data/lib/carray/jit/c_generator.rb +2305 -0
  51. data/lib/carray/jit/compiler.rb +468 -0
  52. data/lib/carray/jit/errors.rb +37 -0
  53. data/lib/carray/jit/expression.rb +202 -0
  54. data/lib/carray/jit/kernel.rb +509 -0
  55. data/lib/carray/jit/node.rb +573 -0
  56. data/lib/carray/jit/sweep.rb +97 -0
  57. data/lib/carray/jit/type_assignment.rb +811 -0
  58. data/lib/carray/jit/version.rb +5 -0
  59. data/lib/carray/jit.rb +1210 -0
  60. metadata +139 -0
@@ -0,0 +1,573 @@
1
+ class CArray
2
+ module JIT
3
+
4
+ # Typed intermediate representation.
5
+ #
6
+ # Nodes are built untyped by Analyzer and annotated by TypeAssignment,
7
+ # which is why #type is writable. Types are one of :int64, :double,
8
+ # :complex or :boolean -- computation types, deliberately distinct from
9
+ # any array's storage type (see TypeAssignment).
10
+ class Node
11
+
12
+ attr_accessor :type
13
+ attr_reader :location
14
+
15
+ def initialize (location = nil)
16
+ @location = location
17
+ @type = nil
18
+ end
19
+
20
+ def children
21
+ []
22
+ end
23
+
24
+ end
25
+
26
+ class IntegerLiteral < Node
27
+ attr_reader :value
28
+ def initialize (value, location = nil)
29
+ super(location)
30
+ @value = value
31
+ end
32
+ end
33
+
34
+ class FloatLiteral < Node
35
+ attr_reader :value
36
+ def initialize (value, location = nil)
37
+ super(location)
38
+ @value = value
39
+ end
40
+ end
41
+
42
+ # An imaginary literal: `2i`, or the `1i` that turns a real formula
43
+ # complex. Ruby has no literal for a complex number with both parts, so
44
+ # this is always purely imaginary and `1 + 2i` is an addition.
45
+ class ImaginaryLiteral < Node
46
+ attr_reader :value
47
+ def initialize (value, location = nil)
48
+ super(location)
49
+ @value = value
50
+ end
51
+ end
52
+
53
+ # One of the loop indices; `axis` is its position in the index list.
54
+ class IndexVariable < Node
55
+ attr_reader :name, :axis
56
+ def initialize (name, axis, location = nil)
57
+ super(location)
58
+ @name = name
59
+ @axis = axis
60
+ end
61
+ end
62
+
63
+ class LocalRead < Node
64
+ # The C variable this read resolves to. A Ruby local may hold an
65
+ # Integer at one point in the body and a Float at another; a C variable
66
+ # cannot, so each type gets its own.
67
+ attr_accessor :binding_name
68
+ attr_reader :name
69
+ def initialize (name, location = nil)
70
+ super(location)
71
+ @name = name
72
+ end
73
+ end
74
+
75
+ # A scalar the block closed over; passed in as a kernel argument.
76
+ class CaptureRead < Node
77
+ attr_reader :name
78
+ def initialize (name, location = nil)
79
+ super(location)
80
+ @name = name
81
+ end
82
+ end
83
+
84
+ # array[i + 1, j], where each axis is addressed by some index in scope.
85
+ # `subscripts` holds one [index name, offset] pair per axis of the array,
86
+ # which need not be the kernel's own indices: an inner loop introduces
87
+ # index names too, and that is what lets a reduction be written.
88
+ class ElementRead < Node
89
+ attr_reader :array, :subscripts
90
+ def initialize (array, subscripts, location = nil)
91
+ super(location)
92
+ @array = array
93
+ @subscripts = subscripts
94
+ end
95
+ end
96
+
97
+ # array[i] == UNDEF, or its negation.
98
+ #
99
+ # This reads the mask, not the value, which is what makes it different
100
+ # from every other read: a cell tested this way has not had its garbage
101
+ # looked at, so it does not mask what it decides. That distinction is
102
+ # syntactic, which is what lets it be settled here at all.
103
+ class MaskTest < Node
104
+ attr_reader :array, :subscripts, :negated
105
+ def initialize (array, subscripts, negated, location = nil)
106
+ super(location)
107
+ @array = array
108
+ @subscripts = subscripts
109
+ @negated = negated
110
+ end
111
+ end
112
+
113
+ # Zero of whatever type another expression has.
114
+ #
115
+ # A sum has to start from a zero of the summand's type: start it from an
116
+ # integer zero and the accumulator is an integer at its first assignment
117
+ # and a float at its second, which is two variables rather than one, and
118
+ # the sum would never accumulate.
119
+ class ZeroLike < Node
120
+ attr_reader :reference
121
+ def initialize (reference, location = nil)
122
+ super(location)
123
+ @reference = reference
124
+ end
125
+ def children
126
+ [@reference]
127
+ end
128
+ end
129
+
130
+ # An extent the caller passed in, referred to by slot. A contraction
131
+ # derives its ranges from the arrays' own shapes, so they arrive with the
132
+ # other extents rather than as expressions in the block.
133
+ class BoundsValue < Node
134
+ attr_reader :slot
135
+ def initialize (slot, location = nil)
136
+ super(location)
137
+ @slot = slot
138
+ end
139
+ end
140
+
141
+ # (from...to).each { |j| ... }, and n.times { |j| ... }
142
+ #
143
+ # The index it introduces is in scope only inside it, exactly as the block
144
+ # scopes it in Ruby, and can address any axis of any array that is read.
145
+ # Nothing is written through it: a kernel writes the cell its outer
146
+ # indices are on, and the inner loop is what runs within that cell.
147
+ class InnerLoop < Node
148
+ attr_reader :index, :from, :to, :statements
149
+ def initialize (index, from, to, statements, location = nil)
150
+ super(location)
151
+ @index = index
152
+ @from = from
153
+ @to = to
154
+ @statements = statements
155
+ end
156
+ def children
157
+ [@from, @to] + @statements
158
+ end
159
+ end
160
+
161
+ # while cond ... end
162
+ #
163
+ # The loop the bounded one is not: it introduces no index, and how many
164
+ # passes it takes is not written anywhere -- it is whatever the condition
165
+ # says, pass by pass. That is the whole of what it adds, and the whole of
166
+ # what it costs: a kernel with one in it may fail to return, and nothing
167
+ # here can tell whether it will.
168
+ #
169
+ # The condition is re-read at the top of every pass, as Ruby's is. A
170
+ # local it reads must already be a local before the loop -- the condition
171
+ # is walked before the body, so a name the body would introduce is not in
172
+ # scope yet, and says so rather than reading whatever C left there.
173
+ class While < Node
174
+ attr_reader :condition, :statements
175
+ def initialize (condition, statements, location = nil)
176
+ super(location)
177
+ @condition = condition
178
+ @statements = statements
179
+ end
180
+ def children
181
+ [@condition] + @statements
182
+ end
183
+ end
184
+
185
+ # `next` and `break` inside a loop. Neither carries a value: the inner
186
+ # loop's own value is never used, so `break x` would drop x silently.
187
+ class LoopSkip < Node
188
+ def children
189
+ []
190
+ end
191
+ end
192
+
193
+ class LoopStop < Node
194
+ def children
195
+ []
196
+ end
197
+ end
198
+
199
+ # array[i] = UNDEF -- marks the cell missing and leaves its bytes alone.
200
+ class MaskWrite < Node
201
+ attr_reader :array
202
+ def initialize (array, location = nil)
203
+ super(location)
204
+ @array = array
205
+ end
206
+ end
207
+
208
+ # if/else in statement position, with writes inside the branches.
209
+ class Branch < Node
210
+ attr_reader :condition, :consequent, :alternative
211
+ def initialize (condition, consequent, alternative, location = nil)
212
+ super(location)
213
+ @condition = condition
214
+ @consequent = consequent
215
+ @alternative = alternative
216
+ end
217
+ def children
218
+ [@condition] + @consequent + @alternative
219
+ end
220
+ end
221
+
222
+ class BinaryOperation < Node
223
+ attr_reader :operator, :left, :right
224
+ def initialize (operator, left, right, location = nil)
225
+ super(location)
226
+ @operator = operator
227
+ @left = left
228
+ @right = right
229
+ end
230
+ def children
231
+ [@left, @right]
232
+ end
233
+ end
234
+
235
+ # && and ||
236
+ class LogicalOperation < Node
237
+ attr_reader :operator, :left, :right
238
+ def initialize (operator, left, right, location = nil)
239
+ super(location)
240
+ @operator = operator
241
+ @left = left
242
+ @right = right
243
+ end
244
+ def children
245
+ [@left, @right]
246
+ end
247
+ end
248
+
249
+ class LogicalNot < Node
250
+ attr_reader :operand
251
+ def initialize (operand, location = nil)
252
+ super(location)
253
+ @operand = operand
254
+ end
255
+ def children
256
+ [@operand]
257
+ end
258
+ end
259
+
260
+ # abs, which unlike the math.h functions keeps the type it was given.
261
+ class AbsoluteValue < Node
262
+ attr_reader :operand
263
+ def initialize (operand, location = nil)
264
+ super(location)
265
+ @operand = operand
266
+ end
267
+ def children
268
+ [@operand]
269
+ end
270
+ end
271
+
272
+ # Complex(x, y) -- the way into the complex type from two real numbers,
273
+ # and the only one that does not start from a complex array.
274
+ class ComplexBuild < Node
275
+ attr_reader :real, :imaginary
276
+ def initialize (real, imaginary, location = nil)
277
+ super(location)
278
+ @real = real
279
+ @imaginary = imaginary
280
+ end
281
+ def children
282
+ [@real, @imaginary]
283
+ end
284
+ end
285
+
286
+ # real / imag / conjugate / arg. Three of the four take a Complex to a
287
+ # Float, which makes them the way out of the complex type: a kernel that
288
+ # writes into a real array has to pass through one of them.
289
+ #
290
+ # `ruby_name` is the spelling the block used, kept only so that a message
291
+ # about it names the method that was actually written.
292
+ class ComplexPart < Node
293
+ attr_reader :name, :ruby_name, :operand
294
+ def initialize (name, ruby_name, operand, location = nil)
295
+ super(location)
296
+ @name = name
297
+ @ruby_name = ruby_name
298
+ @operand = operand
299
+ end
300
+ def children
301
+ [@operand]
302
+ end
303
+ end
304
+
305
+ # floor / ceil / round / truncate / to_i / to_f, whose result type is a
306
+ # property of the method rather than of the operand.
307
+ class Conversion < Node
308
+ attr_reader :name, :operand, :result_type
309
+ def initialize (name, operand, result_type, location = nil)
310
+ super(location)
311
+ @name = name
312
+ @operand = operand
313
+ @result_type = result_type
314
+ end
315
+ def children
316
+ [@operand]
317
+ end
318
+ end
319
+
320
+ # A captured array handed to a C function whole, as an address.
321
+ #
322
+ # Not an ElementRead: nothing is read here. A kernel addresses an array
323
+ # cell by cell, through a base and a stride; a C function takes the
324
+ # address itself, which does not vary with the cell the kernel is on.
325
+ class ArrayAddress < Node
326
+ attr_reader :array
327
+ def initialize (array, location = nil)
328
+ super(location)
329
+ @array = array
330
+ end
331
+ def children
332
+ []
333
+ end
334
+ end
335
+
336
+ # `p[i]` where `p` is a pointer parameter of a compiled function.
337
+ #
338
+ # Not an ElementRead: an array's cell is reached through a base and a
339
+ # stride the caller supplied, and a pointer parameter is reached the way C
340
+ # reaches it -- contiguous, from the address it was handed. The index is
341
+ # an expression rather than a loop index with an offset, because there is
342
+ # no loop.
343
+ class PointerRead < Node
344
+ attr_reader :name, :index
345
+ def initialize (name, index, location = nil)
346
+ super(location)
347
+ @name = name
348
+ @index = index
349
+ end
350
+ def children
351
+ [@index]
352
+ end
353
+ end
354
+
355
+ # `p[i] = value` through a pointer parameter that was not declared const.
356
+ class PointerWrite < Node
357
+ attr_reader :name, :index, :expression
358
+ def initialize (name, index, expression, location = nil)
359
+ super(location)
360
+ @name = name
361
+ @index = index
362
+ @expression = expression
363
+ end
364
+ def children
365
+ [@index, @expression]
366
+ end
367
+ end
368
+
369
+ # A call to a C function the block closed over. The name is the local
370
+ # the block used, not the symbol: which library it came from is settled
371
+ # before the kernel is built, and the kernel only knows the signature.
372
+ class CFunctionCall < Node
373
+ attr_reader :name, :arguments
374
+ def initialize (name, arguments, location = nil)
375
+ super(location)
376
+ @name = name
377
+ @arguments = arguments
378
+ end
379
+ def children
380
+ @arguments
381
+ end
382
+ end
383
+
384
+ # A call standing where a statement stands, its value dropped.
385
+ #
386
+ # Only a call to a C function may be one. Everything else this compiler
387
+ # can write is a computation, and a computation nobody takes the value of
388
+ # is a line that does nothing -- refused, because writing one is a
389
+ # mistake rather than an intention. A C function is the exception
390
+ # because its parameters can carry an address, so what it did may be
391
+ # somewhere other than in the value it returned.
392
+ class CallStatement < Node
393
+ attr_reader :call
394
+ def initialize (call, location = nil)
395
+ super(location)
396
+ @call = call
397
+ end
398
+ def children
399
+ [@call]
400
+ end
401
+ end
402
+
403
+ # A call to the function being compiled, from inside its own body. The
404
+ # name is the one its declaration gave it, which is how C would spell the
405
+ # call too; the symbol it becomes is the generator's business. What the
406
+ # call takes and returns is the declaration's answer rather than anything
407
+ # inferred, so both travel on the node.
408
+ class RecursiveCall < Node
409
+ attr_reader :name, :arguments, :parameters, :result_type
410
+ def initialize (name, arguments, parameters, result_type, location = nil)
411
+ super(location)
412
+ @name = name
413
+ @arguments = arguments
414
+ @parameters = parameters
415
+ @result_type = result_type
416
+ end
417
+ def children
418
+ @arguments
419
+ end
420
+ end
421
+
422
+ # x ** y
423
+ class Power < Node
424
+ attr_reader :base, :exponent
425
+ def initialize (base, exponent, location = nil)
426
+ super(location)
427
+ @base = base
428
+ @exponent = exponent
429
+ end
430
+ def children
431
+ [@base, @exponent]
432
+ end
433
+ end
434
+
435
+ class BitwiseNot < Node
436
+ attr_reader :operand
437
+ def initialize (operand, location = nil)
438
+ super(location)
439
+ @operand = operand
440
+ end
441
+ def children
442
+ [@operand]
443
+ end
444
+ end
445
+
446
+ class BooleanLiteral < Node
447
+ attr_reader :value
448
+ def initialize (value, location = nil)
449
+ super(location)
450
+ @value = value
451
+ end
452
+ def children
453
+ []
454
+ end
455
+ end
456
+
457
+ class UnaryMinus < Node
458
+ attr_reader :operand
459
+ def initialize (operand, location = nil)
460
+ super(location)
461
+ @operand = operand
462
+ end
463
+ def children
464
+ [@operand]
465
+ end
466
+ end
467
+
468
+ class MathCall < Node
469
+ attr_reader :name, :arguments
470
+ def initialize (name, arguments, location = nil)
471
+ super(location)
472
+ @name = name
473
+ @arguments = arguments
474
+ end
475
+ def children
476
+ @arguments
477
+ end
478
+ end
479
+
480
+ class Conditional < Node
481
+ attr_reader :condition, :consequent, :alternative
482
+ def initialize (condition, consequent, alternative, location = nil)
483
+ super(location)
484
+ @condition = condition
485
+ @consequent = consequent
486
+ @alternative = alternative
487
+ end
488
+ def children
489
+ [@condition, @consequent, @alternative]
490
+ end
491
+ end
492
+
493
+ # A block-local variable: w = ...
494
+ # `printf` in the block, for looking at what a kernel is doing. The
495
+ # format is carried as it was written and rewritten on the way out: C's
496
+ # directives are not Ruby's, and by then each argument's type is known.
497
+ class Print < Node
498
+ attr_reader :template, :arguments
499
+ def initialize (template, arguments, location = nil)
500
+ super(location)
501
+ @template = template
502
+ @arguments = arguments
503
+ end
504
+ def children
505
+ @arguments
506
+ end
507
+ end
508
+
509
+ # `raise "..."` in the block. C has no exception to throw, so the
510
+ # message is not carried out of the kernel: it is registered as it is
511
+ # compiled, the cell writes its code into the error slot the kernel is
512
+ # already watching, and the Ruby side raises when the loop is over. The
513
+ # message is written out rather than computed, because it has to be known
514
+ # at compile time to be registered at all.
515
+ class Raise < Node
516
+ attr_reader :message
517
+ def initialize (message, location = nil)
518
+ super(location)
519
+ @message = message
520
+ end
521
+ def children
522
+ []
523
+ end
524
+ end
525
+
526
+ class Assignment < Node
527
+ attr_accessor :binding_name
528
+ attr_reader :name, :expression
529
+ def initialize (name, expression, location = nil)
530
+ super(location)
531
+ @name = name
532
+ @expression = expression
533
+ end
534
+ def children
535
+ [@expression]
536
+ end
537
+ end
538
+
539
+ # A cell of an array: out[i, j] = ...
540
+ #
541
+ # Writes are always at the cell the loop is on. Writing elsewhere would
542
+ # make the evaluation order a property of the body rather than of the
543
+ # dependencies, which is what lets the order be derived at all.
544
+ class ElementWrite < Node
545
+ # How the left-hand side was indexed. A kernel writes the cell its
546
+ # outer indices are on, so this is normally just those; a contraction
547
+ # reads it to learn which indices are free and which are summed over.
548
+ attr_accessor :subscripts
549
+ attr_reader :array, :expression
550
+ def initialize (array, expression, location = nil, subscripts = nil)
551
+ super(location)
552
+ @array = array
553
+ @expression = expression
554
+ @subscripts = subscripts
555
+ end
556
+ def children
557
+ [@expression]
558
+ end
559
+ end
560
+
561
+ class KernelBody < Node
562
+ attr_reader :statements
563
+ def initialize (statements)
564
+ super(nil)
565
+ @statements = statements
566
+ end
567
+ def children
568
+ @statements
569
+ end
570
+ end
571
+
572
+ end
573
+ end
@@ -0,0 +1,97 @@
1
+ require "fiddle"
2
+
3
+ class CArray
4
+ module JIT
5
+
6
+ # Letting CArray drive the loop instead of driving it here.
7
+ #
8
+ # `ca_call_cslab_N` is CArray's chunked sweep: it acquires the operands,
9
+ # broadcasts them, ORs the inputs' masks and propagates the result, and
10
+ # hands a callback one chunk at a time. A non-alias operand -- a gather,
11
+ # a lazy array, anything the tiers here would have to materialise -- is
12
+ # re-gathered into a ~32KB arena scratch per chunk rather than copied
13
+ # whole, so the input memory peak stops scaling with the operand.
14
+ #
15
+ # That is what this is for, and it is the only thing it is for. A kernel
16
+ # that names an index is not a sweep: it reaches neighbours, chooses an
17
+ # order and runs inner loops, none of which a chunked walk can offer.
18
+ # jit_each is the form with no neighbour in it, which is the same thing
19
+ # as saying it is the form a sweep can drive.
20
+ module Sweep
21
+
22
+ # The sweep is reached by address, the way this library reaches every
23
+ # other C function: CArray's extension is already loaded, so the symbol
24
+ # is there to be found rather than linked against.
25
+ def self.handle
26
+ return @handle if defined?(@handle)
27
+ path = $LOADED_FEATURES.find { |feature|
28
+ File.basename(feature) =~ /\Acarray_ext\.(so|bundle|dylib)\z/
29
+ }
30
+ @handle = path && begin
31
+ Fiddle::Handle.new(path)
32
+ rescue Fiddle::DLError
33
+ nil
34
+ end
35
+ end
36
+
37
+ # True when this CArray is new enough to have the chunked slab family.
38
+ def self.available?
39
+ return @available if defined?(@available)
40
+ @available = !handle.nil? && begin
41
+ handle["ca_call_cslab_1_r"]
42
+ true
43
+ rescue Fiddle::DLError
44
+ false
45
+ end
46
+ end
47
+
48
+ # ca_call_cslab_N_r(func, fsync, rcx0..rcxN-1, userdata)
49
+ #
50
+ # `need_gvl: true` is not a detail. Fiddle releases the GVL for a call
51
+ # unless it is told otherwise, which is right for every other function
52
+ # this library reaches -- a generated kernel is pure C, touches no Ruby
53
+ # value and is the better for running while other threads do. This one
54
+ # is not that: it is CArray's own C, and acquiring the operands attaches
55
+ # them, allocates with `xmalloc`, creates a mask where an output needs
56
+ # one and raises on a shape that will not broadcast. All of that is the
57
+ # Ruby runtime's, and none of it may be done without the GVL. The
58
+ # kernel it calls back into is still pure C, but the GVL is held for the
59
+ # whole call rather than the callback, because Fiddle's choice is made
60
+ # once at the boundary and there is nowhere inside to make it again.
61
+ def self.function (arity)
62
+ @functions ||= {}
63
+ @functions[arity] ||=
64
+ begin
65
+ types = [Fiddle::TYPE_VOIDP] * (arity + 3)
66
+ begin
67
+ Fiddle::Function.new(handle["ca_call_cslab_#{arity}_r"], types,
68
+ Fiddle::TYPE_VOIDP, need_gvl: true)
69
+ rescue ArgumentError
70
+ # A Fiddle too old to be told. It is also too old to have let
71
+ # go of the GVL in the first place -- the keyword and the
72
+ # releasing arrived together -- so what is left here is what
73
+ # that Fiddle would have done anyway.
74
+ Fiddle::Function.new(handle["ca_call_cslab_#{arity}_r"], types,
75
+ Fiddle::TYPE_VOIDP)
76
+ end
77
+ end
78
+ end
79
+
80
+ # The largest arity the family was generated for. Whether a pass can
81
+ # go this way at all is decided in CArray::JIT.sweepable_pass?, before
82
+ # there is a kernel -- because the answer changes what is compiled.
83
+ MAX_ARITY = 7
84
+
85
+ # `arrays` are the operands in the order the kernel addresses them, and
86
+ # `fsync` says which of them it writes -- CArray's own spelling, one
87
+ # character per operand.
88
+ def self.call (slab, fsync, arrays, context)
89
+ function(arrays.size).call(slab, fsync,
90
+ *arrays.map { |array| Fiddle.dlwrap(array) },
91
+ context)
92
+ end
93
+
94
+ end
95
+
96
+ end
97
+ end