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,777 @@
1
+ require "digest"
2
+ require "fiddle"
3
+ require "fiddle/import"
4
+
5
+ class CArray
6
+ module JIT
7
+
8
+ # One parameter or return type, as the prototype spelled it.
9
+ #
10
+ # Fiddle's parser answers what the ABI needs and no more: every pointer
11
+ # comes back as `TYPE_VOIDP`, so `const double *`, `double *` and `void *`
12
+ # are one thing to it. That is enough to *call* a function and not enough
13
+ # to *write* one, which is why the declarator's own text is kept beside
14
+ # the code Fiddle assigned it.
15
+ CType = Struct.new(:text, :fiddle, :computation, :pointer, :array,
16
+ :element, :const) do
17
+ # True for a type that may be written into a signature but holds no
18
+ # value a kernel or a body can compute with.
19
+ def opaque?
20
+ computation.nil?
21
+ end
22
+
23
+ # A pointer to numbers, which a body may index. `void *` is not one:
24
+ # it points at nothing in particular, so it stays a slot.
25
+ def indexable?
26
+ pointer && !element.nil?
27
+ end
28
+
29
+ # C's own declarator puts an array's length where a reader can see it,
30
+ # so `const double coef[3]` and `const double *coef` are different
31
+ # declarations of the same ABI. The length is kept rather than folded
32
+ # away: a subscript into a parameter has no extent behind it unless the
33
+ # declaration carried one, and this compiler's subscripts have always
34
+ # had one.
35
+ def sized?
36
+ array.is_a?(Integer)
37
+ end
38
+
39
+ # A declarator with the name in it, which for an array is not simply
40
+ # the type followed by the name.
41
+ def declare (name)
42
+ return "#{text} #{name}" unless array
43
+ "#{text.sub(/\s*\[.*\]\z/, "")} #{name}[#{array if sized?}]"
44
+ end
45
+
46
+ # @return [String] the type, as C spells it.
47
+ def to_s
48
+ text
49
+ end
50
+ end
51
+
52
+ # A C function a kernel body can call, and that C can be handed back.
53
+ #
54
+ # The binding is borrowed from Fiddle -- `Fiddle::Handle` finds the
55
+ # library and the symbol, and `Fiddle::CParser` decides the ABI code of a
56
+ # type, which is the same path `Fiddle::Importer#extern` takes. The
57
+ # *call* is not borrowed: reaching a function through `Fiddle::Function`
58
+ # costs a few hundred nanoseconds per cell, against single digits from
59
+ # compiled C. Fiddle is asked where the function is; the kernel calls it.
60
+ #
61
+ # The address travels to the kernel in a buffer, the way a captured scalar
62
+ # already does, rather than being linked against. That means no `-l`
63
+ # flag, no library path at compile time, and a compiled kernel that does
64
+ # not depend on which library the function came from -- so `f.call(a)`
65
+ # compiles once and serves every function of that signature.
66
+ class CFunction
67
+
68
+ # @!attribute [r] name
69
+ # @return [String] the function's name, as the prototype gives it.
70
+ # @!attribute [r] prototype
71
+ # @return [String] the C declaration this was named by.
72
+ # @!attribute [r] c_source
73
+ # @return [String, nil] the C compiled for a body written here, or
74
+ # `nil` for one found elsewhere.
75
+ attr_reader :name, :prototype, :return_type, :parameters, :pointer,
76
+ :block, :c_source, :origin, :definition, :helpers,
77
+ # What `raise` in the body said, by the code it reports.
78
+ # The kernel that pastes it answers for these too.
79
+ :raise_messages
80
+
81
+ def initialize (name, prototype, return_type, parameters, pointer,
82
+ block: nil, c_source: nil, origin: nil, error: nil,
83
+ definition: nil, helpers: nil, takes_error: false,
84
+ raise_messages: {})
85
+ @name = name && name.to_sym
86
+ @prototype = prototype
87
+ @return_type = return_type
88
+ @parameters = parameters
89
+ @pointer = pointer
90
+ # A function written in Ruby keeps its block, so that what the kernel
91
+ # runs and what Ruby would compute can be put side by side.
92
+ @block = block
93
+ @c_source = c_source
94
+ # The definition on its own, without the file it was compiled in, and
95
+ # what it wants from a preamble -- what a kernel needs to paste it.
96
+ @definition = definition
97
+ @helpers = helpers
98
+ # True when that definition ends in an `int32_t *`: the body can report
99
+ # a failure, and pasted it reports into the caller's slot rather than
100
+ # into the flag in its own object.
101
+ @takes_error = takes_error
102
+ @raise_messages = raise_messages
103
+ @origin = origin
104
+ # Where the compiled body says a division had no divisor, or a
105
+ # subscript ran off its array. Nil when the body can do neither.
106
+ @error = error
107
+ @function = nil
108
+ end
109
+
110
+ # True for one compiled from a Ruby block rather than bound from a
111
+ # library.
112
+ def compiled?
113
+ !@block.nil?
114
+ end
115
+
116
+ # True for one a kernel can paste into its own C rather than call
117
+ # through a pointer. The body has to be here to paste, which a borrowed
118
+ # function's is not: it arrives as an address and nothing else.
119
+ def pasted?
120
+ compiled? && !@definition.nil?
121
+ end
122
+
123
+ # True when the pasted copy takes the caller's error slot as its last
124
+ # argument. Standing alone the same body reports into the flag in its
125
+ # own object -- `#call` reads that one -- but pasted there is no such
126
+ # object around it, and the failure belongs to the kernel that is
127
+ # running: `1 / 0` in a function called from a kernel raises the
128
+ # ZeroDivisionError the kernel raises for its own.
129
+ def pasted_takes_error?
130
+ pasted? && @takes_error
131
+ end
132
+
133
+ # The signature, without the address.
134
+ def signature
135
+ [@return_type.text, @parameters.map(&:text)]
136
+ end
137
+
138
+ # What a compiled kernel depends on. Two functions that share it share
139
+ # a kernel.
140
+ #
141
+ # For one bound from a library that is the signature alone: the address
142
+ # arrives with the call, so `j0` and `y0` are the same kernel and it is
143
+ # compiled once. For one compiled here it is the signature *and* the
144
+ # symbol, which carries the digest of the body -- the body is on its way
145
+ # into the kernel's own translation unit, and a kernel that has one body
146
+ # pasted into it cannot serve another function that is merely declared
147
+ # the same way. Splitting the cache costs a compile per body; sharing
148
+ # it would hand back the wrong answer, and would do it quietly.
149
+ def kernel_key
150
+ compiled? ? [signature, @name] : signature
151
+ end
152
+
153
+ # @return [Integer] how many arguments the function takes.
154
+ def arity
155
+ @parameters.size
156
+ end
157
+
158
+ # The Fiddle types, for calling it from Ruby.
159
+ def argument_types
160
+ @parameters.map(&:fiddle)
161
+ end
162
+
163
+ # The C spelling of the pointer type, for the typedef a kernel emits.
164
+ def c_declaration (typedef_name)
165
+ "typedef #{@return_type.text} (*#{typedef_name})" \
166
+ "(#{@parameters.map(&:text).join(', ')});"
167
+ end
168
+
169
+ # What a kernel computes the result in.
170
+ def result_type
171
+ computation_of(@return_type, "returns")
172
+ end
173
+
174
+ # What it computes in where the value is dropped. `void` is a return
175
+ # type a call may have and a cell may not, so the question only has an
176
+ # answer in statement position -- which is the one place that asks.
177
+ def discarded_result_type
178
+ @return_type.opaque? ? :void : @return_type.computation
179
+ end
180
+
181
+ # @private
182
+ def argument_result_types
183
+ @parameters.map { |type| computation_of(type, "takes") }
184
+ end
185
+
186
+ # Calling it from Ruby, so that a body means the same thing run either
187
+ # way. Slow -- this is the several-hundred-nanosecond path -- and here
188
+ # for testing and for the odd cell, not for sweeping an array.
189
+ #
190
+ # A pointer to numbers takes a CArray, which is the thing in this
191
+ # library that is a run of numbers with a type. The block sees the
192
+ # array itself and reaches it with `#[]`, the compiled C sees its
193
+ # address and reaches it with a subscript, and `coef[0]` means the same
194
+ # in both -- so the body agrees with itself whichever way it is run.
195
+ def call (*arguments)
196
+ unless arguments.size == @parameters.size
197
+ raise ArgumentError,
198
+ "wrong number of arguments (given #{arguments.size}, " \
199
+ "expected #{@parameters.size})"
200
+ end
201
+ @function ||= Fiddle::Function.new(@pointer, argument_types,
202
+ @return_type.fiddle,
203
+ name: @name.to_s)
204
+ arrays = []
205
+ prepared = arguments.zip(@parameters).map { |argument, type|
206
+ next argument unless type.indexable? && argument.is_a?(CArray)
207
+ buffer = check_array(argument, type)
208
+ arrays << [argument, buffer, type]
209
+ buffer
210
+ }
211
+ clear_error
212
+ Access.open(arrays.map { |_, buffer, _| buffer },
213
+ arrays.map { |_, _, type| !type.const },
214
+ arrays.map { nil }, arrays.map { nil }) do |bases|
215
+ slot = -1
216
+ prepared = prepared.map { |value|
217
+ next value unless arrays.any? { |_, buffer, _| buffer.equal?(value) }
218
+ Fiddle::Pointer.new(bases[slot += 1][:pointer])
219
+ }
220
+ @result = @function.call(*prepared)
221
+ end
222
+ report_error
223
+ # A view was copied to be made contiguous; a writable one is copied
224
+ # back, because the C wrote into the copy.
225
+ arrays.each do |array, buffer, type|
226
+ array[] = buffer unless type.const || array.equal?(buffer)
227
+ end
228
+ @result
229
+ end
230
+
231
+ alias [] call
232
+
233
+ # @return [String] the function's name.
234
+ def to_s
235
+ text = "#{@return_type.text} #{@name}" \
236
+ "(#{@parameters.map(&:text).join(', ')})"
237
+ @origin ? "#{text} at #{@origin}" : text
238
+ end
239
+
240
+ # @return [String] the prototype this was named by.
241
+ def inspect
242
+ "#<CArray::JIT::CFunction #{self}>"
243
+ end
244
+
245
+ private
246
+
247
+ # What a pointer parameter will accept, and what has to be true of it.
248
+ # The length is checked only where the declaration carried one: C's own
249
+ # rule is that an unsized pointer is the caller's responsibility, and
250
+ # writing `coef[3]` is how the caller asks to be checked.
251
+ def clear_error
252
+ @error[0, 4] = [0].pack("l") if @error
253
+ end
254
+
255
+ # What the kernel raises for the same code, since it is the same thing
256
+ # that happened: `6 % 0` is a ZeroDivisionError wherever it is written,
257
+ # and the compiled body cannot raise it itself. A caller reaching the
258
+ # address from C sees the number the helper returned and the flag
259
+ # standing, which is C's own arrangement for a function that has to
260
+ # return something whatever happened.
261
+ def report_error
262
+ return unless @error
263
+ code = @error[0, 4].unpack1("l")
264
+ case code
265
+ when 0 then nil
266
+ when 1 then raise ZeroDivisionError, "divided by 0"
267
+ else
268
+ # `raise "..."` in the body. The message did not come back through
269
+ # the C -- it was registered when this was compiled -- so it is
270
+ # looked up here, and a kernel that pasted the same body looks the
271
+ # same message up under the same code.
272
+ message = @raise_messages[code]
273
+ raise Error, "#{@name} reported #{code}, which is no failure it " \
274
+ "was compiled to report" unless message
275
+ raise RuntimeError, message
276
+ end
277
+ end
278
+
279
+ def check_array (array, type)
280
+ wanted = CDeclaration::DATA_TYPES.fetch(type.element.fiddle)
281
+ unless array.data_type_name == wanted.to_s
282
+ raise Unsupported,
283
+ "`#{type.text}` takes a #{wanted} array, and this one is " \
284
+ "#{array.data_type_name}"
285
+ end
286
+ if type.sized? && array.elements < type.array
287
+ raise Unsupported,
288
+ "`#{type.text}` reads #{type.array} " \
289
+ "#{type.array == 1 ? 'element' : 'elements'}, and this array " \
290
+ "has #{array.elements}"
291
+ end
292
+ if array.has_mask?
293
+ # The same thing the kernel refuses when it hands one of its arrays
294
+ # over: a masked cell's bytes are out of contract, and a C function
295
+ # has no mask to consult, so it would read whatever is underneath.
296
+ # Refusing it here as well is what keeps `f.call` and `f.block.call`
297
+ # the same body run two ways -- the block reaches an UNDEF and says
298
+ # so, and the C would have quietly used the number beneath it.
299
+ raise Unsupported,
300
+ "`#{type.text}` is handed an array carrying a mask, and a " \
301
+ "C function has no mask to read; the values under a mask are " \
302
+ "not values, so `#strip_mask(fill)` is what says what the C " \
303
+ "should see there"
304
+ end
305
+ # A pointer is reached contiguously -- `p[i]` with no stride -- and
306
+ # only an entity is laid out that way, so a view is packed into one
307
+ # for the call and copied back afterwards if the C may write to it.
308
+ #
309
+ # `#to_ca` is not the way to pack one: it answers self for a view as
310
+ # well as for an entity, so reaching for it here handed the C a
311
+ # view's base pointer to walk contiguously, which wrote over its
312
+ # neighbours without a word. `#copy` is the one that always makes an
313
+ # entity.
314
+ Access.classify(array)[:entity] ? array : array.copy
315
+ end
316
+
317
+ def computation_of (type, role)
318
+ return type.computation unless type.opaque?
319
+ raise Unsupported,
320
+ "`#{@name}` #{role} `#{type.text}`, which is a slot in the " \
321
+ "signature rather than a value a kernel can compute with"
322
+ end
323
+
324
+ end
325
+
326
+ # Reads the subset of C declarations a signature may be written in.
327
+ #
328
+ # Fiddle's parser is used for what it is good at -- deciding the ABI code
329
+ # of a type -- and this decides what that parser throws away: whether a
330
+ # parameter is a pointer, and what it was a pointer to. Between them a
331
+ # prototype yields both what is needed to call a function and what is
332
+ # needed to write one.
333
+ # @private
334
+ module CDeclaration
335
+
336
+ PARSER = ::Object.new.extend(Fiddle::CParser)
337
+ private_constant :PARSER
338
+
339
+ # The words a type may be spelled with. `<stdint.h>`'s exact-width
340
+ # names are here because Fiddle knows them; there is no `float64_t`
341
+ # because C has no such type -- its integer types have exact-width
342
+ # aliases and its floating types are `float` and `double`.
343
+ KEYWORDS = %w[
344
+ const unsigned signed void char short int long float double
345
+ int8_t int16_t int32_t int64_t
346
+ uint8_t uint16_t uint32_t uint64_t
347
+ size_t ssize_t ptrdiff_t intptr_t uintptr_t
348
+ ].freeze
349
+
350
+ # Fiddle answers `long double` with the code for `long`, silently, so it
351
+ # is refused by name rather than trusted.
352
+ REFUSED = {
353
+ "long double" => "`long double` is not a type this reads: Fiddle " \
354
+ "reports it as `long`, which would be the wrong " \
355
+ "width without saying so",
356
+ }.freeze
357
+
358
+ # The CArray data type a pointer parameter takes, by the code Fiddle
359
+ # gave its element. This is the table CGenerator::STORAGE_C_TYPES
360
+ # already holds, read the other way round -- nothing new is decided
361
+ # here about how a C type and a CArray type correspond.
362
+ DATA_TYPES = {
363
+ Fiddle::TYPE_DOUBLE => :float64,
364
+ Fiddle::TYPE_FLOAT => :float32,
365
+ Fiddle::TYPE_CHAR => :int8,
366
+ Fiddle::TYPE_UCHAR => :uint8,
367
+ Fiddle::TYPE_SHORT => :int16,
368
+ Fiddle::TYPE_USHORT => :uint16,
369
+ Fiddle::TYPE_INT => :int32,
370
+ Fiddle::TYPE_UINT => :uint32,
371
+ Fiddle::TYPE_LONG => :int64,
372
+ Fiddle::TYPE_LONG_LONG => :int64,
373
+ }.freeze
374
+
375
+ # Codes Fiddle may return, mapped to what a kernel computes them in.
376
+ # Absent means the type may be written down but holds no value a body
377
+ # can compute with. `uint64_t` is absent for the reason CArray has no
378
+ # uint64 array: no computation type holds it without losing a bit.
379
+ COMPUTATION = {
380
+ Fiddle::TYPE_DOUBLE => :double,
381
+ Fiddle::TYPE_FLOAT => :double,
382
+ Fiddle::TYPE_CHAR => :int64,
383
+ Fiddle::TYPE_UCHAR => :int64,
384
+ Fiddle::TYPE_SHORT => :int64,
385
+ Fiddle::TYPE_USHORT => :int64,
386
+ Fiddle::TYPE_INT => :int64,
387
+ Fiddle::TYPE_UINT => :int64,
388
+ Fiddle::TYPE_LONG => :int64,
389
+ Fiddle::TYPE_LONG_LONG => :int64,
390
+ }.freeze
391
+
392
+ module_function
393
+
394
+ # Splits a prototype into [name, return CType, parameter CTypes]. The
395
+ # name is nil for the anonymous form, `double (*)(double)` -- the
396
+ # spelling C already has for the type of a function pointer, which is
397
+ # what this hands out.
398
+ def parse (prototype)
399
+ unless prototype.is_a?(String)
400
+ raise Unsupported,
401
+ "a C prototype is expected, as in `\"double j0(double)\"`; " \
402
+ "got #{prototype.class}"
403
+ end
404
+ text = prototype.strip.sub(/;\z/, "")
405
+ name, return_text, parameter_text = split(text, prototype)
406
+ parameters = split_parameters(parameter_text).map { |part|
407
+ read_type(part, prototype)
408
+ }
409
+ # `f(void)` takes nothing, which is not the same as taking a void.
410
+ parameters = [] if parameters.size == 1 &&
411
+ parameters.first.text == "void"
412
+ [name, read_type(return_text, prototype), parameters]
413
+ end
414
+
415
+ def split (text, prototype)
416
+ if (match = /\A(.+?)\(\s*\*\s*\)\s*\((.*)\)\z/m.match(text))
417
+ [nil, match[1], match[2]]
418
+ elsif (match = /\A(.+?[\s\*])([A-Za-z_]\w*)\s*\((.*)\)\z/m.match(text))
419
+ [match[2], match[1], match[3]]
420
+ else
421
+ raise Unsupported,
422
+ "`#{prototype}` does not read as a C prototype; write it as " \
423
+ "`double j0(double)` to bind one, or `double (*)(double)` " \
424
+ "for one with no name"
425
+ end
426
+ end
427
+
428
+ # Top-level commas only. Nothing in the supported subset nests, so this
429
+ # is a split -- written out so that admitting something that does nest
430
+ # is a change in one place.
431
+ def split_parameters (text)
432
+ return [] if text.strip.empty?
433
+ text.split(",").map(&:strip)
434
+ end
435
+
436
+ # `[const] <keywords> [*] [name] [[]]` is the whole grammar there is.
437
+ # A parameter's own name is dropped: nothing reads it for a bound
438
+ # function, and a compiled one uses the block's parameter names.
439
+ def read_type (text, prototype)
440
+ array = nil
441
+ stripped = text.strip.sub(/\[\s*(\d*)\s*\]\s*\z/) {
442
+ array = $1.empty? ? :unsized : $1.to_i
443
+ ""
444
+ }
445
+ words = stripped.split(/\s+|(?=\*)|(?<=\*)/).reject(&:empty?)
446
+ keywords = []
447
+ keywords << words.shift while words.first && KEYWORDS.include?(words.first)
448
+ pointer = false
449
+ while words.first == "*"
450
+ words.shift
451
+ pointer = true
452
+ end
453
+ # Whatever is left can only be the parameter's own name.
454
+ words.shift if words.first&.match?(/\A[A-Za-z_]\w*\z/)
455
+ if keywords.empty? || !words.empty?
456
+ raise Unsupported,
457
+ "`#{text.strip}` in `#{prototype}` is not a type this reads; " \
458
+ "it takes C's own spellings -- `double`, `int32_t`, " \
459
+ "`const double *` and the like"
460
+ end
461
+ build_type(keywords, pointer, array, text, prototype)
462
+ end
463
+
464
+ def build_type (keywords, pointer, array, text, prototype)
465
+ spelling = keywords.reject { |word| word == "const" }.join(" ")
466
+ if (reason = REFUSED[spelling])
467
+ raise Unsupported, "#{reason} (in `#{prototype}`)"
468
+ end
469
+ # A parameter written as an array is a pointer at the ABI, whatever
470
+ # its declarator says.
471
+ pointer ||= !array.nil?
472
+ written = keywords.join(" ")
473
+ written += if array
474
+ " [#{array if array.is_a?(Integer)}]"
475
+ elsif pointer
476
+ " *"
477
+ else
478
+ ""
479
+ end
480
+ code = fiddle_code(pointer ? "void *" : spelling, text, prototype)
481
+ # What it points at, for a pointer that points at numbers. `void *`
482
+ # has no element, which is what keeps it a slot.
483
+ element = nil
484
+ if pointer && spelling != "void"
485
+ element_code = fiddle_code(spelling, text, prototype)
486
+ if COMPUTATION[element_code]
487
+ element = CType.new(spelling, element_code,
488
+ COMPUTATION[element_code], false, nil, nil,
489
+ false)
490
+ end
491
+ end
492
+ CType.new(written, code, pointer ? nil : COMPUTATION[code], pointer,
493
+ array, element, keywords.include?("const"))
494
+ end
495
+
496
+ def fiddle_code (spelling, text, prototype)
497
+ PARSER.parse_ctype(spelling)
498
+ rescue StandardError
499
+ raise Unsupported,
500
+ "`#{text.strip}` in `#{prototype}` is not a type Fiddle knows. " \
501
+ "C's own spellings are what this takes, and `float64_t` is not " \
502
+ "one of them -- C's integer types have exact-width names, its " \
503
+ "floating types are `float` and `double`"
504
+ end
505
+
506
+ end
507
+
508
+ class << self
509
+
510
+ # A C function someone else compiled, reached by quoting its
511
+ # declaration:
512
+ #
513
+ # j0 = CArray.jit_extern("double j0(double)", from: "libgsl")
514
+ #
515
+ # `from` is where to look -- a path or library name, a Fiddle::Handle,
516
+ # or nothing at all, which searches what the process has already loaded.
517
+ #
518
+ # `extern` is C's own word for a body that lives elsewhere, and that is
519
+ # all this does: it asks Fiddle where the function is. Nothing is
520
+ # compiled here, which is the difference from `jit_function` and the
521
+ # reason the two are not one method with a branch in it.
522
+ #
523
+ # The declaration is C rather than a vocabulary of this compiler's own,
524
+ # because what is declared is a C function and the types it has to meet
525
+ # belong to whatever will call it. `void *params` is the point of the
526
+ # exercise, not an edge of it.
527
+ def extern (prototype, from: nil, &block)
528
+ if block
529
+ raise Unsupported,
530
+ "`jit_extern` finds a function already compiled, so a body " \
531
+ "here would be dropped; compile one with `CArray.jit_function`"
532
+ end
533
+ name, return_type, parameters = CDeclaration.parse(prototype)
534
+ unless name
535
+ raise Unsupported,
536
+ "`#{prototype}` names no function to find; give the name, as " \
537
+ "in `double j0(double)` -- or write the body and compile it " \
538
+ "with `CArray.jit_function`"
539
+ end
540
+ bind_c_function(prototype, name, return_type, parameters, from)
541
+ end
542
+
543
+ # A C function of your own, written in Ruby and compiled here:
544
+ #
545
+ # square = CArray.jit_function("double (*)(double)") { |x| x * x + 1 }
546
+ #
547
+ # `double (*)(double)` is the spelling C already has for the type of a
548
+ # function pointer, which is what this hands out. There is no name
549
+ # because nothing links by name -- the address is what travels -- so a
550
+ # name would have been invented to be looked at once. Writing one
551
+ # anyway is allowed, and becomes the symbol in the compiled object.
552
+ #
553
+ # What comes back is the same object `jit_extern` hands out, so a kernel
554
+ # calls either without knowing which it has; `compiled?` is where the
555
+ # difference is still visible, along with the block, which it keeps.
556
+ def function (prototype, &block)
557
+ unless block
558
+ raise Unsupported,
559
+ "a function is compiled from a block, and none was given -- " \
560
+ "or find one already compiled with " \
561
+ "`CArray.jit_extern(#{prototype.inspect})`"
562
+ end
563
+ name, return_type, parameters = CDeclaration.parse(prototype)
564
+ compile_c_function(prototype, name, return_type, parameters, block)
565
+ end
566
+
567
+ private
568
+
569
+ def bind_c_function (prototype, name, return_type, parameters, from)
570
+ handle = library_handle(from)
571
+ begin
572
+ pointer = handle[name.to_s]
573
+ rescue Fiddle::DLError => error
574
+ raise Unsupported,
575
+ "`#{name}` was not found in " \
576
+ "#{from ? from.inspect : "the loaded libraries"} " \
577
+ "(#{error.message}); pass `from:` to say which library it is in"
578
+ end
579
+ CFunction.new(name, prototype, return_type, parameters, pointer)
580
+ end
581
+
582
+ # The one function a compiled object holds. Nothing looks it up by name
583
+ # -- the address is what travels -- but a profiler and a backtrace print
584
+ # it, and a program handing several of these to a solver would otherwise
585
+ # see one name for all of them. So an anonymous one carries its own
586
+ # digest, and a named one carries the name it was given.
587
+ #
588
+ # Always behind a prefix, though, and that is not tidiness. A name the
589
+ # C already knows is the dangerous case, and the dangerous case is the
590
+ # one that does *not* fail: `double sin(double)` matches math.h's
591
+ # declaration, so the generated file defines libm's `sin` and the
592
+ # shared object exports it -- where symbols are interposable, that
593
+ # replaces sine for whatever loads it later. A mismatched signature is
594
+ # a compile error and would have been noticed; this one would not.
595
+ PREFIX = "carray_jit_"
596
+
597
+ def function_symbol (name, key)
598
+ digest = Digest::SHA256.hexdigest(key.inspect)[0, 12]
599
+ "#{PREFIX}#{name || "function"}_#{digest}"
600
+ end
601
+
602
+ def compile_c_function (prototype, name, return_type, parameters, block)
603
+ node, source, origin = read_block(block)
604
+ key = [source, return_type.text, parameters.map(&:text), name]
605
+ found = function_registry[key]
606
+ return found if found
607
+ function_registry[key] =
608
+ build_c_function(prototype, name, return_type, parameters,
609
+ source, node, origin, block, function_symbol(name, key))
610
+ end
611
+
612
+ def function_registry
613
+ @function_registry ||= {}
614
+ end
615
+
616
+ def build_c_function (prototype, name, return_type, parameters,
617
+ source, node, origin, block, symbol)
618
+ # A function is a function of its parameters. Whatever else the block
619
+ # reaches for is refused, and the reason differs by what it is -- so
620
+ # the captures are looked at before the body is walked, or the body
621
+ # would raise first and say something less useful.
622
+ names = block.parameters.map(&:last)
623
+ unless names.size == parameters.size
624
+ raise Unsupported,
625
+ "`#{prototype}` names #{parameters.size} " \
626
+ "#{parameters.size == 1 ? 'parameter' : 'parameters'}, and " \
627
+ "the block takes #{names.size}"
628
+ end
629
+ refuse_captures(source, node, block, names, name && name.to_sym)
630
+
631
+ # `void` is a return type a body may have: a function whose work is
632
+ # through its pointer parameters has nothing to hand back, and C says
633
+ # so with the word. What it cannot be called in is expression
634
+ # position, which is where the value would have been wanted -- that
635
+ # is the same refusal a borrowed `void` function already gets.
636
+ #
637
+ # Every other return type with no computation behind it is a pointer,
638
+ # which a body cannot produce: there is nothing here to take an
639
+ # address of that would outlive the call.
640
+ returns_nothing = return_type.fiddle == Fiddle::TYPE_VOID &&
641
+ !return_type.pointer
642
+ unless return_type.computation || returns_nothing
643
+ raise Unsupported,
644
+ "`#{prototype}` returns `#{return_type.text}`, which is no " \
645
+ "value a compiled body can produce"
646
+ end
647
+
648
+ # A pointer to numbers is indexable, and `const` says whether it may
649
+ # be written through -- which is the whole of the read/write
650
+ # distinction, spelled the way C spells it.
651
+ pointers = names.zip(parameters).select { |_, type| type.pointer }
652
+ .to_h { |name, type|
653
+ [name, type.indexable? ? !type.const : nil]
654
+ }
655
+ pointer_types = names.zip(parameters).select { |_, type| type.indexable? }
656
+ .to_h { |name, type| [name, type.element.computation] }
657
+
658
+ # A declaration that gave a name puts that name in scope inside its
659
+ # own body, as C does, so the body can call itself. An anonymous one
660
+ # has nothing to call itself by, and gets no recursion.
661
+ analyzer = Analyzer.new(source, node: node, function: true,
662
+ returns: !returns_nothing,
663
+ pointers: pointers,
664
+ recursion: (name && [name.to_sym, parameters,
665
+ return_type.computation]))
666
+ names = analyzer.parameter_names
667
+ # A pointer is not a value however it is spelled: `void *` cannot be
668
+ # reached at all, and one that points at numbers has to be indexed.
669
+ by_name = names.zip(parameters).to_h
670
+ loose = analyzer.scalar_names.find { |name|
671
+ by_name[name] && by_name[name].pointer
672
+ }
673
+ if loose
674
+ type = by_name.fetch(loose)
675
+ raise Unsupported,
676
+ (if type.indexable?
677
+ "`#{loose}` is declared `#{type.text}`, which is a pointer; " \
678
+ "index it, as in `#{loose}[0]`"
679
+ else
680
+ "`#{loose}` is declared `#{type.text}`, which is a slot in " \
681
+ "the signature rather than a value; the body cannot read it"
682
+ end)
683
+ end
684
+ types = names.zip(parameters).reject { |_, type| type.pointer }
685
+ .to_h { |parameter, type| [parameter, type.computation] }
686
+
687
+ assignment = TypeAssignment.new(analyzer.body, {}, {}, {},
688
+ scalar_types: types,
689
+ pointer_types: pointer_types)
690
+ generator = CGenerator.new(analyzer, {}, assignment.scalar_types,
691
+ origin: origin, block_source: source)
692
+ c_source = generator.generate_function(
693
+ symbol, names, parameters, return_type.text, return_type.computation)
694
+ handle, = Compiler.build(c_source, symbol, header: generator.provenance)
695
+ # Where the body can divide by zero or reach outside an array, the
696
+ # object carries a place to say so. Reading it is what lets a call
697
+ # from Ruby raise what the same expression raises in Ruby.
698
+ error = if generator.uses_error_flag?
699
+ Fiddle::Pointer.new(handle[CGenerator::ERROR_FLAG], 4)
700
+ end
701
+ # A body that reports failures is generated a second time for pasting,
702
+ # with the flag as a parameter. A second generator rather than the
703
+ # same one twice: what it emitted is what it holds, and the two forms
704
+ # differ from the first statement that can fail onwards.
705
+ pasted = generator
706
+ if generator.uses_error_flag?
707
+ pasted = CGenerator.new(analyzer, {}, assignment.scalar_types,
708
+ origin: origin, block_source: source)
709
+ pasted.generate_function(symbol, names, parameters, return_type.text,
710
+ return_type.computation,
711
+ error_parameter: true)
712
+ end
713
+ CFunction.new(symbol, prototype, return_type, parameters, handle[symbol],
714
+ block: block, c_source: generator.provenance + c_source,
715
+ origin: origin, error: error,
716
+ definition: pasted.function_definition,
717
+ helpers: pasted.helper_needs,
718
+ takes_error: generator.uses_error_flag?,
719
+ raise_messages: generator.raise_messages)
720
+ end
721
+
722
+ # Nothing outside the parameter list may be reached. A number could in
723
+ # principle be written into the C as a literal, and another c_function's
724
+ # address as a constant -- but both would put something in the compiled
725
+ # object that no key covers, so an object built for one capture would be
726
+ # handed back for another. The kernel path avoids that by passing
727
+ # captures in buffers at call time; a C function has no buffers, so its
728
+ # parameters are its whole surface.
729
+ #
730
+ # Read the other way round, this is what makes the body's text and the
731
+ # signature a complete key: with nothing captured, they settle which
732
+ # function it is. And it is what makes the compiled object pure C -- it
733
+ # touches no Ruby value and references no Ruby symbol, so the address is
734
+ # safe to call from a thread that holds no GVL, which is more than a
735
+ # Ruby-defined callback usually manages.
736
+ def refuse_captures (source, node, block, parameters, own_name = nil)
737
+ captured = capture_names(source, node) - parameters
738
+ # The function's own name is not a capture: it is the declarator's,
739
+ # in scope inside the body it heads, exactly as C has it. What the
740
+ # local holds while the body is being compiled is nothing -- the
741
+ # assignment has not happened yet -- and the compiled call reaches
742
+ # the symbol, not the local.
743
+ captured -= [own_name] if own_name
744
+ return if captured.empty?
745
+ name = captured.first
746
+ value = begin
747
+ binding_of(block).local_variable_get(name)
748
+ rescue NameError
749
+ nil
750
+ end
751
+ kind = case value
752
+ when CArray then "an array"
753
+ when CFunction then "another C function"
754
+ else "a value"
755
+ end
756
+ raise Unsupported,
757
+ "this function reaches `#{name}`, which is #{kind} outside it; " \
758
+ "a compiled function takes everything it needs through its " \
759
+ "parameters, so name it as one"
760
+ end
761
+
762
+ def library_handle (from)
763
+ case from
764
+ when nil then Fiddle::Handle::DEFAULT
765
+ when Fiddle::Handle then from
766
+ when String then Fiddle::Handle.new(from)
767
+ else
768
+ raise Unsupported,
769
+ "`from:` takes a library name or a Fiddle::Handle, " \
770
+ "got #{from.class}"
771
+ end
772
+ end
773
+
774
+ end
775
+
776
+ end
777
+ end