carray-jit 0.1.2 → 0.1.3

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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +771 -3
  3. data/README.md +7 -6
  4. data/carray-jit.gemspec +1 -3
  5. data/docs/00_Introduction.md +4 -3
  6. data/docs/01_GettingStarted.md +1 -1
  7. data/docs/02_KernelShapes.md +93 -14
  8. data/docs/03_SupportedFeatures.md +582 -26
  9. data/docs/04_Compiling.md +33 -6
  10. data/docs/05_DesignNotes.md +3 -3
  11. data/docs/06_Cheatsheet.md +198 -5
  12. data/docs/07_StepByStep.ja.md +534 -0
  13. data/docs/07_StepByStep.md +535 -0
  14. data/examples/README.md +12 -0
  15. data/examples/applications/alarm.rb +121 -0
  16. data/examples/applications/collatz.rb +105 -0
  17. data/examples/applications/cubic_spline.rb +331 -0
  18. data/examples/applications/dithering.rb +144 -0
  19. data/examples/applications/group_stats.rb +115 -0
  20. data/examples/applications/lookup.rb +126 -0
  21. data/examples/applications/median_filter.rb +153 -0
  22. data/examples/applications/parcel_ascent.rb +220 -0
  23. data/examples/applications/point_in_polygon.rb +111 -0
  24. data/examples/applications/random_walk.rb +98 -0
  25. data/examples/applications/van_der_pol.rb +186 -0
  26. data/examples/applications/wet_bulb.rb +140 -0
  27. data/examples/features/10_complex.rb +14 -4
  28. data/examples/features/15_loops.rb +7 -1
  29. data/lib/carray/jit/access.rb +14 -0
  30. data/lib/carray/jit/analyzer.rb +2077 -136
  31. data/lib/carray/jit/block_reader.rb +37 -6
  32. data/lib/carray/jit/c_function.rb +613 -76
  33. data/lib/carray/jit/c_generator.rb +1595 -156
  34. data/lib/carray/jit/call.rb +68 -0
  35. data/lib/carray/jit/compiler.rb +75 -11
  36. data/lib/carray/jit/kernel.rb +369 -32
  37. data/lib/carray/jit/node.rb +359 -9
  38. data/lib/carray/jit/sorting_networks.rb +182 -0
  39. data/lib/carray/jit/type_assignment.rb +371 -34
  40. data/lib/carray/jit/version.rb +1 -1
  41. data/lib/carray/jit.rb +560 -64
  42. metadata +22 -8
  43. data/ext/carray_jit_access/carray_jit_access.c +0 -460
  44. data/ext/carray_jit_access/extconf.rb +0 -8
@@ -26,6 +26,18 @@ class CArray
26
26
  pointer && !element.nil?
27
27
  end
28
28
 
29
+ # A C99 complex passed or returned by value. Fiddle cannot carry one,
30
+ # so a call from Ruby goes through a shim; a call from a kernel is C
31
+ # calling C and needs nothing.
32
+ def complex?
33
+ !pointer && CDeclaration::COMPLEX_CODES.value?(fiddle)
34
+ end
35
+
36
+ # `CMPLX` for a double complex, `CMPLXF` for a float one.
37
+ def complex_build
38
+ fiddle == :float_complex ? "CMPLXF" : "CMPLX"
39
+ end
40
+
29
41
  # C's own declarator puts an array's length where a reader can see it,
30
42
  # so `const double coef[3]` and `const double *coef` are different
31
43
  # declarations of the same ABI. The length is kept rather than folded
@@ -63,6 +75,20 @@ class CArray
63
75
  # flag, no library path at compile time, and a compiled kernel that does
64
76
  # not depend on which library the function came from -- so `f.call(a)`
65
77
  # compiles once and serves every function of that signature.
78
+ # The namespace every symbol this compiler writes sits in, and that is
79
+ # not tidiness. A name the C already knows is the dangerous case, and
80
+ # the dangerous case is the one that does *not* fail: `double
81
+ # sin(double)` matches math.h's declaration, so the generated file
82
+ # defines libm's `sin` and the shared object exports it -- where symbols
83
+ # are interposable, that replaces sine for whatever loads it later. A
84
+ # mismatched signature is a compile error and would have been noticed;
85
+ # this one would not.
86
+ #
87
+ # Here rather than beside `function_symbol`, which writes the names,
88
+ # because `CFunction#c_source_as` has to refuse one as well: a caller
89
+ # naming its own symbol may not name one in here.
90
+ PREFIX = "carray_jit_"
91
+
66
92
  class CFunction
67
93
 
68
94
  # @!attribute [r] name
@@ -72,17 +98,31 @@ class CArray
72
98
  # @!attribute [r] c_source
73
99
  # @return [String, nil] the C compiled for a body written here, or
74
100
  # `nil` for one found elsewhere.
75
- attr_reader :name, :prototype, :return_type, :parameters, :pointer,
101
+ attr_reader :name, :symbol, :prototype, :return_type, :parameters, :pointer,
76
102
  :block, :c_source, :origin, :definition, :helpers,
103
+ # The compiled functions this body calls. Whoever pastes
104
+ # the definition has to paste these beside it: it reaches
105
+ # them by symbol, and the symbol is only there if the
106
+ # definition is.
107
+ :dependencies,
77
108
  # What `raise` in the body said, by the code it reports.
78
109
  # The kernel that pastes it answers for these too.
79
110
  :raise_messages
80
111
 
81
112
  def initialize (name, prototype, return_type, parameters, pointer,
113
+ symbol: nil,
82
114
  block: nil, c_source: nil, origin: nil, error: nil,
83
115
  definition: nil, helpers: nil, takes_error: false,
84
- raise_messages: {})
116
+ raise_messages: {}, dependencies: [], shim: nil)
117
+ # The name the declaration gave, which is what a reader wrote and
118
+ # what an error should say. It is nil where the declaration gave
119
+ # none -- `double (*)(double)` names no function.
85
120
  @name = name && name.to_sym
121
+ # The name in the object, which is what a call reaches and what two
122
+ # functions have to differ by. For one bound from a library they
123
+ # are the same; for one compiled here the symbol carries a digest of
124
+ # the body, so that two bodies declared alike stay apart.
125
+ @symbol = (symbol || name)&.to_sym
86
126
  @prototype = prototype
87
127
  @return_type = return_type
88
128
  @parameters = parameters
@@ -95,6 +135,7 @@ class CArray
95
135
  # what it wants from a preamble -- what a kernel needs to paste it.
96
136
  @definition = definition
97
137
  @helpers = helpers
138
+ @dependencies = dependencies
98
139
  # True when that definition ends in an `int32_t *`: the body can report
99
140
  # a failure, and pasted it reports into the caller's slot rather than
100
141
  # into the flag in its own object.
@@ -104,6 +145,10 @@ class CArray
104
145
  # Where the compiled body says a division had no divisor, or a
105
146
  # subscript ran off its array. Nil when the body can do neither.
106
147
  @error = error
148
+ # The address of the entry point a call from Ruby takes where the
149
+ # signature carries a complex by value; nil where it does not, which
150
+ # is every other signature and every borrowed function.
151
+ @shim = shim
107
152
  @function = nil
108
153
  end
109
154
 
@@ -130,6 +175,16 @@ class CArray
130
175
  pasted? && @takes_error
131
176
  end
132
177
 
178
+ # The declaration under the name the block reached it by. What a
179
+ # message about a call should say: the caller wrote `f`, and the symbol
180
+ # a body compiled here carries -- `carray_jit_two_<digest>` -- along
181
+ # with the file it was written in are answers to a question nobody
182
+ # asked there. Those stay in `to_s`, which names the object rather
183
+ # than the call.
184
+ def declaration_as (name)
185
+ "#{@return_type.text} #{name}(#{@parameters.map(&:text).join(', ')})"
186
+ end
187
+
133
188
  # The signature, without the address.
134
189
  def signature
135
190
  [@return_type.text, @parameters.map(&:text)]
@@ -147,7 +202,54 @@ class CArray
147
202
  # the same way. Splitting the cache costs a compile per body; sharing
148
203
  # it would hand back the wrong answer, and would do it quietly.
149
204
  def kernel_key
150
- compiled? ? [signature, @name] : signature
205
+ compiled? ? [signature, @symbol] : signature
206
+ end
207
+
208
+ # What to call this in a message: the name its declaration gave, and
209
+ # the symbol where it gave none.
210
+ def label
211
+ @name || @symbol
212
+ end
213
+
214
+ # The C this was compiled from, under a symbol of the caller's
215
+ # choosing.
216
+ #
217
+ # For a caller writing the C into a file of its own rather than letting
218
+ # this compile it: the symbol here carries a digest of the body, which
219
+ # is right for an object in a cache and wrong for one in a repository,
220
+ # where the same build has to give the same name every time.
221
+ #
222
+ # It is a rename rather than a second run of the generator, and that is
223
+ # exact rather than close enough: the symbol is the only thing in the
224
+ # generated file that the choice of symbol decides. Two compilations
225
+ # of one block under two declared names differ in nothing else once
226
+ # both symbols are levelled, which the suite checks.
227
+ #
228
+ # The caller owns the name it picks, and with it the one hazard the
229
+ # generator's own prefix exists to close: a body declared `sin`
230
+ # emitted as `sin` defines libm's, and the object exports it. What is
231
+ # refused here is a name C cannot spell, and the generator's own
232
+ # namespace -- an object built under `carray_jit_...` would answer to
233
+ # a symbol a cached kernel is entitled to.
234
+ def c_source_as (symbol)
235
+ symbol = symbol.to_s
236
+ unless @c_source
237
+ raise Unsupported,
238
+ "`#{label}` was bound from a library rather than compiled " \
239
+ "here, so there is no C of its own to write out"
240
+ end
241
+ unless symbol =~ /\A[A-Za-z_][A-Za-z0-9_]*\z/
242
+ raise Unsupported,
243
+ "`#{symbol}` is not a C identifier, so nothing could be " \
244
+ "defined under it"
245
+ end
246
+ if symbol.start_with?(PREFIX)
247
+ raise Unsupported,
248
+ "`#{symbol}` is in this compiler's own namespace " \
249
+ "(`#{PREFIX}`), where a cached kernel is entitled to the " \
250
+ "name; pick one of your own"
251
+ end
252
+ @c_source.gsub(@symbol.to_s, symbol)
151
253
  end
152
254
 
153
255
  # @return [Integer] how many arguments the function takes.
@@ -198,9 +300,16 @@ class CArray
198
300
  "wrong number of arguments (given #{arguments.size}, " \
199
301
  "expected #{@parameters.size})"
200
302
  end
303
+ return call_through_shim(arguments) if @shim
304
+ if carries_a_complex?
305
+ raise Unsupported,
306
+ "`#{self}` carries a C99 complex by value, which Fiddle has " \
307
+ "no type for, so it cannot be called from Ruby; a kernel " \
308
+ "calls it as C calls it"
309
+ end
201
310
  @function ||= Fiddle::Function.new(@pointer, argument_types,
202
311
  @return_type.fiddle,
203
- name: @name.to_s)
312
+ name: label.to_s)
204
313
  arrays = []
205
314
  prepared = arguments.zip(@parameters).map { |argument, type|
206
315
  next argument unless type.indexable? && argument.is_a?(CArray)
@@ -208,18 +317,29 @@ class CArray
208
317
  arrays << [argument, buffer, type]
209
318
  buffer
210
319
  }
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)
320
+ # Borrowed rather than cleared, and borrowed here rather than on the
321
+ # way in: a call made inside a window -- someone else is holding this
322
+ # function's address and watching the same flag -- must answer for
323
+ # itself without disarming them. The checks above never reach the C
324
+ # and so never touch the flag at all.
325
+ outer = error_code
326
+ write_error(0)
327
+ begin
328
+ Access.open(arrays.map { |_, buffer, _| buffer },
329
+ arrays.map { |_, _, type| !type.const },
330
+ arrays.map { nil }, arrays.map { nil }) do |bases|
331
+ slot = -1
332
+ prepared = prepared.map { |value|
333
+ next value unless arrays.any? { |_, buffer, _| buffer.equal?(value) }
334
+ Fiddle::Pointer.new(bases[slot += 1][:pointer])
335
+ }
336
+ @result = @function.call(*prepared)
337
+ end
338
+ code = error_code
339
+ ensure
340
+ write_error(outer)
221
341
  end
222
- report_error
342
+ raise_for(code)
223
343
  # A view was copied to be made contiguous; a writable one is copied
224
344
  # back, because the C wrote into the copy.
225
345
  arrays.each do |array, buffer, type|
@@ -230,6 +350,77 @@ class CArray
230
350
 
231
351
  alias [] call
232
352
 
353
+ # Whether a call from Ruby has to go round through the shim.
354
+ def carries_a_complex?
355
+ @return_type.complex? || @parameters.any?(&:complex?)
356
+ end
357
+
358
+ # Fiddle carries no complex, so the shim takes each complex argument as
359
+ # a pair of doubles and writes a complex result back the same way.
360
+ # Everything else keeps the type the declaration gave it and goes
361
+ # through Fiddle as before -- including a pointer parameter, which is
362
+ # still a CArray on this side.
363
+ #
364
+ # It is the same compiled body either way: the shim calls the function,
365
+ # it does not reimplement it.
366
+ def call_through_shim (arguments)
367
+ returns_complex = @return_type.complex?
368
+ types = @parameters.map { |type|
369
+ type.complex? ? Fiddle::TYPE_VOIDP : type.fiddle
370
+ }
371
+ types << Fiddle::TYPE_VOIDP if returns_complex
372
+ @shim_function ||=
373
+ Fiddle::Function.new(@shim, types,
374
+ returns_complex ? Fiddle::TYPE_VOID
375
+ : @return_type.fiddle)
376
+ result = returns_complex ? String.new("\0" * 16) : nil
377
+ arrays = []
378
+ prepared = arguments.zip(@parameters).map { |argument, type|
379
+ next pack_complex(argument, type) if type.complex?
380
+ next argument unless type.indexable? && argument.is_a?(CArray)
381
+ buffer = check_array(argument, type)
382
+ arrays << [argument, buffer, type]
383
+ buffer
384
+ }
385
+ outer = error_code
386
+ write_error(0)
387
+ begin
388
+ Access.open(arrays.map { |_, buffer, _| buffer },
389
+ arrays.map { |_, _, type| !type.const },
390
+ arrays.map { nil }, arrays.map { nil }) do |bases|
391
+ slot = -1
392
+ passed = prepared.map { |value|
393
+ next value unless arrays.any? { |_, buffer, _| buffer.equal?(value) }
394
+ Fiddle::Pointer.new(bases[slot += 1][:pointer])
395
+ }
396
+ passed << result if returns_complex
397
+ @result = @shim_function.call(*passed)
398
+ end
399
+ code = error_code
400
+ ensure
401
+ write_error(outer)
402
+ end
403
+ raise_for(code)
404
+ arrays.each do |array, buffer, type|
405
+ array[] = buffer unless type.const || array.equal?(buffer)
406
+ end
407
+ @result = Complex(*result.unpack("dd")) if returns_complex
408
+ @result
409
+ end
410
+
411
+ # A Ruby number of any kind arrives as the two doubles the shim reads.
412
+ # `Complex()` is what Ruby itself converts with, so an Integer and a
413
+ # Float are taken where a Complex is asked for, as they are in Ruby.
414
+ def pack_complex (value, type)
415
+ number = begin
416
+ Complex(value)
417
+ rescue TypeError, ArgumentError
418
+ raise Unsupported,
419
+ "`#{type.text}` takes a number, got #{value.class}"
420
+ end
421
+ String.new([number.real.to_f, number.imaginary.to_f].pack("dd"))
422
+ end
423
+
233
424
  # @return [String] the function's name.
234
425
  def to_s
235
426
  text = "#{@return_type.text} #{@name}" \
@@ -242,40 +433,118 @@ class CArray
242
433
  "#<CArray::JIT::CFunction #{self}>"
243
434
  end
244
435
 
245
- private
436
+ # The window a caller opens when it hands the address out.
437
+ #
438
+ # `#call` is one call, and answers for it before it returns. A library
439
+ # given `#pointer` calls whenever it likes, as often as it likes, and
440
+ # what wants an answer is the whole of that -- so the flag is put down
441
+ # once, the address is lent for as long as the block runs, and what
442
+ # happened is asked for once at the end. It is the arrangement a kernel
443
+ # already keeps with its own slot, which is cleared before a sweep and
444
+ # read after it, never per cell.
445
+ #
446
+ # f.watching do
447
+ # Integration.qags(f.pointer, 0.0, 1.0)
448
+ # end
449
+ #
450
+ # A failure inside the block outranks whatever the library made of it.
451
+ # A body that fails returns a stand-in, so the library is the first to
452
+ # complain -- that the endpoints do not straddle, that the iteration did
453
+ # not converge -- and those complaints are the failure's consequences,
454
+ # not what happened. So the flag is read before that exception is let
455
+ # through, and only where nothing stands does the library's own story
456
+ # get to be the story.
457
+ #
458
+ # Windows nest, and a call made inside one leaves it armed: both borrow
459
+ # the flag and put it back as they found it, so an inner window answers
460
+ # for its own block and no other.
461
+ def watching
462
+ outer = error_code
463
+ write_error(0)
464
+ code = 0
465
+ begin
466
+ result = yield
467
+ code = error_code
468
+ rescue StandardError
469
+ raise_for(error_code)
470
+ raise
471
+ ensure
472
+ write_error(outer)
473
+ end
474
+ raise_for(code)
475
+ result
476
+ end
246
477
 
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.
478
+ # Put the flag down, before lending the address to something that will
479
+ # call it more than once. `#watching` is this and #report_error with
480
+ # the lending in between, and is what to reach for where the window is a
481
+ # block; these two are here for a window that is not -- one opened in
482
+ # one method and closed in another, or one whose block belongs to
483
+ # somebody else.
251
484
  def clear_error
252
- @error[0, 4] = [0].pack("l") if @error
485
+ write_error(0)
253
486
  end
254
487
 
255
488
  # What the kernel raises for the same code, since it is the same thing
256
489
  # that happened: `6 % 0` is a ZeroDivisionError wherever it is written,
257
490
  # 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
491
+ # address from C sees the stand-in the helper returned and the flag
259
492
  # standing, which is C's own arrangement for a function that has to
260
493
  # return something whatever happened.
494
+ #
495
+ # Quiet where nothing stands, so that a caller may ask having no idea
496
+ # whether anything failed -- which is the position a caller is in after
497
+ # handing the address to a library. Asking does not put the flag down:
498
+ # it reads, and the window that put it down is what picks it up.
261
499
  def report_error
262
- return unless @error
263
- code = @error[0, 4].unpack1("l")
500
+ raise_for(error_code)
501
+ end
502
+
503
+ private
504
+
505
+ # 0 where the body cannot fail at all: one that neither divides nor
506
+ # raises is compiled without a flag to read, and has no failure to
507
+ # report rather than an unread one.
508
+ def error_code
509
+ @error ? @error[0, 4].unpack1("l") : 0
510
+ end
511
+
512
+ def write_error (code)
513
+ @error[0, 4] = [code].pack("l") if @error
514
+ end
515
+
516
+ def raise_for (code)
517
+ if (failure = CGenerator::FIXED_FAILURES[code])
518
+ raise failure[0], failure[1]
519
+ end
264
520
  case code
265
521
  when 0 then nil
266
522
  when 1 then raise ZeroDivisionError, "divided by 0"
523
+ when 2 then raise IndexError, "index out of range"
524
+ when 3 then raise ArgumentError,
525
+ "min argument must be less than or equal to " \
526
+ "max argument"
527
+ when 4 then raise ArgumentError,
528
+ "comparison with a NaN failed, so `clamp` has " \
529
+ "no answer"
530
+ when 5 then raise Math::DomainError,
531
+ "Numerical argument is out of domain - gamma"
267
532
  else
268
533
  # `raise "..."` in the body. The message did not come back through
269
534
  # the C -- it was registered when this was compiled -- so it is
270
535
  # looked up here, and a kernel that pasted the same body looks the
271
536
  # same message up under the same code.
272
537
  message = @raise_messages[code]
273
- raise Error, "#{@name} reported #{code}, which is no failure it " \
538
+ raise Error, "#{label} reported #{code}, which is no failure it " \
274
539
  "was compiled to report" unless message
275
540
  raise RuntimeError, message
276
541
  end
277
542
  end
278
543
 
544
+ # What a pointer parameter will accept, and what has to be true of it.
545
+ # The length is checked only where the declaration carried one: C's own
546
+ # rule is that an unsized pointer is the caller's responsibility, and
547
+ # writing `coef[3]` is how the caller asks to be checked.
279
548
  def check_array (array, type)
280
549
  wanted = CDeclaration::DATA_TYPES.fetch(type.element.fiddle)
281
550
  unless array.data_type_name == wanted.to_s
@@ -317,7 +586,7 @@ class CArray
317
586
  def computation_of (type, role)
318
587
  return type.computation unless type.opaque?
319
588
  raise Unsupported,
320
- "`#{@name}` #{role} `#{type.text}`, which is a slot in the " \
589
+ "`#{label}` #{role} `#{type.text}`, which is a slot in the " \
321
590
  "signature rather than a value a kernel can compute with"
322
591
  end
323
592
 
@@ -342,17 +611,34 @@ class CArray
342
611
  # aliases and its floating types are `float` and `double`.
343
612
  KEYWORDS = %w[
344
613
  const unsigned signed void char short int long float double
614
+ _Complex complex
345
615
  int8_t int16_t int32_t int64_t
346
616
  uint8_t uint16_t uint32_t uint64_t
347
617
  size_t ssize_t ptrdiff_t intptr_t uintptr_t
348
618
  ].freeze
349
619
 
620
+ # Fiddle has no code for a C99 complex -- its parser does not know the
621
+ # word and the ABI it answers for has nowhere to put one -- so these
622
+ # stand where a Fiddle code stands elsewhere. Symbols rather than
623
+ # numbers, so that one reaching Fiddle by mistake is a TypeError there
624
+ # and not a silently wrong width.
625
+ COMPLEX_CODES = { "double" => :double_complex,
626
+ "float" => :float_complex }.freeze
627
+
628
+ # `double complex` is `<complex.h>`'s spelling of `double _Complex`,
629
+ # and both are written; which words a declaration used says nothing
630
+ # about what it declared.
631
+ COMPLEX_WORDS = %w[_Complex complex].freeze
632
+
350
633
  # Fiddle answers `long double` with the code for `long`, silently, so it
351
634
  # is refused by name rather than trusted.
352
635
  REFUSED = {
353
636
  "long double" => "`long double` is not a type this reads: Fiddle " \
354
637
  "reports it as `long`, which would be the wrong " \
355
638
  "width without saying so",
639
+ "long double _Complex" =>
640
+ "`long double _Complex` is not a type this reads: there is no " \
641
+ "long double here to make one of",
356
642
  }.freeze
357
643
 
358
644
  # The CArray data type a pointer parameter takes, by the code Fiddle
@@ -360,6 +646,8 @@ class CArray
360
646
  # already holds, read the other way round -- nothing new is decided
361
647
  # here about how a C type and a CArray type correspond.
362
648
  DATA_TYPES = {
649
+ :double_complex => :cmplx128,
650
+ :float_complex => :cmplx64,
363
651
  Fiddle::TYPE_DOUBLE => :float64,
364
652
  Fiddle::TYPE_FLOAT => :float32,
365
653
  Fiddle::TYPE_CHAR => :int8,
@@ -380,6 +668,11 @@ class CArray
380
668
  # Absent means the type may be written down but holds no value a body
381
669
  # can compute with -- `void`, and a pointer to it.
382
670
  COMPUTATION = {
671
+ # A `float _Complex` computes in double complex, as a `float`
672
+ # computes in double: the declaration says what the signature is,
673
+ # and the body works in what Ruby would have worked in.
674
+ :double_complex => :complex,
675
+ :float_complex => :complex,
383
676
  Fiddle::TYPE_DOUBLE => :double,
384
677
  Fiddle::TYPE_FLOAT => :double,
385
678
  Fiddle::TYPE_CHAR => :int64,
@@ -440,9 +733,40 @@ class CArray
440
733
  text.split(",").map(&:strip)
441
734
  end
442
735
 
736
+ # The names a declaration gave its parameters, in order, with `nil`
737
+ # where it gave none.
738
+ #
739
+ # `read_type` drops them, and for every other spelling that is right:
740
+ # nothing reads a parameter's name for a function bound from a library,
741
+ # and one compiled from a block takes its parameters from the block.
742
+ # `jit_call` is where they stop being decorative -- there the names are
743
+ # what binds the call to the locals around it -- so they are read here
744
+ # rather than kept on a type, where a name does not belong.
745
+ def parameter_names (prototype)
746
+ text = prototype.strip.sub(/;\z/, "")
747
+ _, _, parameter_text = split(text, prototype)
748
+ parts = split_parameters(parameter_text)
749
+ return [] if parts.size == 1 && parts.first.strip == "void"
750
+ parts.map { |part| parameter_name(part) }
751
+ end
752
+
753
+ # The same grammar `read_type` reads, answering the name it throws
754
+ # away. Written out beside it rather than folded into it: `read_type`
755
+ # is asked about a return type too, and a return type has no name.
756
+ def parameter_name (text)
757
+ stripped = text.strip.sub(/\[\s*\d*\s*\]\s*\z/, "")
758
+ words = stripped.split(/\s+|(?=\*)|(?<=\*)/).reject(&:empty?)
759
+ words.shift while words.first && KEYWORDS.include?(words.first)
760
+ words.shift while words.first == "*"
761
+ last = words.first
762
+ # A Symbol, which is what a block's parameters are and what the rest
763
+ # of the compiler compares names as.
764
+ last.to_sym if last&.match?(/\A[A-Za-z_]\w*\z/)
765
+ end
766
+
443
767
  # `[const] <keywords> [*] [name] [[]]` is the whole grammar there is.
444
- # A parameter's own name is dropped: nothing reads it for a bound
445
- # function, and a compiled one uses the block's parameter names.
768
+ # A parameter's own name is dropped here; `parameter_names` above reads
769
+ # it for the one spelling that needs it.
446
770
  def read_type (text, prototype)
447
771
  array = nil
448
772
  stripped = text.strip.sub(/\[\s*(\d*)\s*\]\s*\z/) {
@@ -484,12 +808,17 @@ class CArray
484
808
  else
485
809
  ""
486
810
  end
487
- code = fiddle_code(pointer ? "void *" : spelling, text, prototype)
811
+ code = if !pointer && (complex = complex_code(spelling))
812
+ complex
813
+ else
814
+ fiddle_code(pointer ? "void *" : spelling, text, prototype)
815
+ end
488
816
  # What it points at, for a pointer that points at numbers. `void *`
489
817
  # has no element, which is what keeps it a slot.
490
818
  element = nil
491
819
  if pointer && spelling != "void"
492
- element_code = fiddle_code(spelling, text, prototype)
820
+ element_code = complex_code(spelling) ||
821
+ fiddle_code(spelling, text, prototype)
493
822
  if COMPUTATION[element_code]
494
823
  element = CType.new(spelling, element_code,
495
824
  COMPUTATION[element_code], false, nil, nil,
@@ -500,6 +829,15 @@ class CArray
500
829
  array, element, keywords.include?("const"))
501
830
  end
502
831
 
832
+ # The code for a complex spelling, or nil for anything else. `float
833
+ # _Complex`, `complex float`, `float complex` -- the word may sit on
834
+ # either side, as C allows.
835
+ def complex_code (spelling)
836
+ words = spelling.split(/\s+/)
837
+ return nil unless (words & COMPLEX_WORDS).any?
838
+ COMPLEX_CODES[(words - COMPLEX_WORDS).join(" ")]
839
+ end
840
+
503
841
  def fiddle_code (spelling, text, prototype)
504
842
  PARSER.parse_ctype(spelling)
505
843
  rescue StandardError
@@ -560,7 +898,77 @@ class CArray
560
898
  # What comes back is the same object `jit_extern` hands out, so a kernel
561
899
  # calls either without knowing which it has; `compiled?` is where the
562
900
  # difference is still visible, along with the block, which it keeps.
563
- def function (prototype, &block)
901
+ # `CArray.jit_call`: compile the block, read the locals the declaration
902
+ # named, call.
903
+ #
904
+ # Kept per call site rather than per body. A block literal is a fresh
905
+ # Proc every time the line runs, but its instruction sequence is the
906
+ # site's and does not change -- which is the key `read_block` already
907
+ # caches a block's source under.
908
+ def call_here (prototype, block)
909
+ site = RubyVM::InstructionSequence.of(block) if
910
+ defined?(RubyVM::InstructionSequence)
911
+ entry = site && call_sites[site]
912
+ unless entry
913
+ names = CDeclaration.parameter_names(prototype)
914
+ if (missing = names.index(nil))
915
+ raise Unsupported,
916
+ "`#{prototype}` gives parameter #{missing + 1} no name, " \
917
+ "and a name is what `jit_call` binds by -- it is the local " \
918
+ "the call reads"
919
+ end
920
+ # A compiled function may already exist somewhere other than this
921
+ # compiler's cache: built ahead of the program and shipped in a
922
+ # shared object, which is what carray-jit-aot does with these same call
923
+ # sites. A provider is asked before anything is compiled and
924
+ # answers nil for a site that is not its own -- the same position
925
+ # `jit_extern` puts a function from a library in, said about a call
926
+ # rather than about a name.
927
+ compiled = call_provider &&
928
+ call_provider.call(prototype, block, names)
929
+ unless compiled
930
+ # Nothing answered, so this one is compiled -- and the compiler
931
+ # is loaded here rather than beside the method, so a program
932
+ # whose sites were all answered never loads it. `require` is
933
+ # idempotent and this runs once per site.
934
+ require "carray/jit"
935
+ compiled = function(prototype, declared_parameters: names, &block)
936
+ end
937
+ entry = [compiled, names]
938
+ call_sites[site] = entry if site
939
+ end
940
+ compiled, names = entry
941
+ scope = block.binding
942
+ compiled.call(*names.map { |local|
943
+ begin
944
+ scope.local_variable_get(local)
945
+ rescue NameError
946
+ raise Unsupported,
947
+ "`#{prototype}` declares `#{local}`, and there is no local " \
948
+ "by that name where the call stands. A declaration's " \
949
+ "parameter names are what `jit_call` binds to"
950
+ end
951
+ })
952
+ end
953
+
954
+ def call_sites
955
+ @call_sites ||= {}.compare_by_identity
956
+ end
957
+
958
+ # Asked at a `jit_call` site before anything is compiled, and answering
959
+ # nil means "not mine, compile it". What it is handed is the
960
+ # prototype, the block -- whose binding says which method and which
961
+ # module the site is in -- and the names the declaration gave. What it
962
+ # answers is anything that responds to `call`.
963
+ #
964
+ # There is one client and it is carray-jit-aot, which builds these same
965
+ # sites into a shared object ahead of the program; a machine running
966
+ # that gem then reaches no compiler. Left here rather than patched in
967
+ # from there, because a gem reaching into another's method to change
968
+ # what it does is the arrangement that breaks quietly.
969
+ attr_accessor :call_provider
970
+
971
+ def function (prototype, declared_parameters: nil, &block)
564
972
  unless block
565
973
  raise Unsupported,
566
974
  "a function is compiled from a block, and none was given -- " \
@@ -568,7 +976,8 @@ class CArray
568
976
  "`CArray.jit_extern(#{prototype.inspect})`"
569
977
  end
570
978
  name, return_type, parameters = CDeclaration.parse(prototype)
571
- compile_c_function(prototype, name, return_type, parameters, block)
979
+ compile_c_function(prototype, name, return_type, parameters, block,
980
+ declared_parameters)
572
981
  end
573
982
 
574
983
  private
@@ -592,28 +1001,80 @@ class CArray
592
1001
  # see one name for all of them. So an anonymous one carries its own
593
1002
  # digest, and a named one carries the name it was given.
594
1003
  #
595
- # Always behind a prefix, though, and that is not tidiness. A name the
596
- # C already knows is the dangerous case, and the dangerous case is the
597
- # one that does *not* fail: `double sin(double)` matches math.h's
598
- # declaration, so the generated file defines libm's `sin` and the
599
- # shared object exports it -- where symbols are interposable, that
600
- # replaces sine for whatever loads it later. A mismatched signature is
601
- # a compile error and would have been noticed; this one would not.
602
- PREFIX = "carray_jit_"
1004
+ # Always behind `PREFIX`, though, for the reason stated where it is
1005
+ # defined.
603
1006
 
604
1007
  def function_symbol (name, key)
605
1008
  digest = Digest::SHA256.hexdigest(key.inspect)[0, 12]
606
1009
  "#{PREFIX}#{name || "function"}_#{digest}"
607
1010
  end
608
1011
 
609
- def compile_c_function (prototype, name, return_type, parameters, block)
1012
+ def compile_c_function (prototype, name, return_type, parameters, block,
1013
+ declared_parameters = nil)
610
1014
  node, source, origin = read_block(block)
611
- key = [source, return_type.text, parameters.map(&:text), name]
1015
+ # A function this body calls is pasted into it, so which one it is
1016
+ # belongs in the key beside the body's own text. Two blocks spelled
1017
+ # the same that reach different functions are different functions,
1018
+ # and without this the first compiled would be handed back for the
1019
+ # second -- quietly, since nothing about them differs to look at.
1020
+ # `kernel_key` is what a kernel already keys a pasted body by: the
1021
+ # signature and the symbol, which carries the digest of the body. A
1022
+ # symbol rather than an address, so the key means the same thing in
1023
+ # the next process as in this one.
1024
+ called = called_functions(source, node, block, name)
1025
+ key = [source, return_type.text, parameters.map(&:text), name,
1026
+ declared_parameters, called.values.map(&:kernel_key)]
612
1027
  found = function_registry[key]
613
1028
  return found if found
614
1029
  function_registry[key] =
615
1030
  build_c_function(prototype, name, return_type, parameters,
616
- source, node, origin, block, function_symbol(name, key))
1031
+ source, node, origin, block, function_symbol(name, key),
1032
+ called, declared_parameters)
1033
+ end
1034
+
1035
+ # The compiled functions the block reaches for, by the name it reaches
1036
+ # them by. Only these: everything else it closes over is refused, and
1037
+ # `refuse_captures` is where that is said. This runs before the
1038
+ # registry is consulted, because the key cannot be built without it.
1039
+ def called_functions (source, node, block, own_name)
1040
+ names = capture_names(source, node) - block.parameters.map(&:last)
1041
+ names -= [own_name.to_sym] if own_name
1042
+ binding = binding_of(block)
1043
+ names.each_with_object({}) do |captured, found|
1044
+ value = captured_value(captured, binding)
1045
+ # A function compiled here is pasted into this one; one bound from
1046
+ # a library is declared and called by its name, which is what it
1047
+ # has and what a linker resolves. Both are functions this body
1048
+ # calls, so both are here.
1049
+ found[captured] = value if value.is_a?(CFunction)
1050
+ end
1051
+ end
1052
+
1053
+ # What a name held where the block was written, or nil for one that
1054
+ # held nothing. A constant is looked up as well as a local, because a
1055
+ # method body closes over nothing: a `def` that compiles a function
1056
+ # reaches the one it calls by a constant or not at all.
1057
+ def captured_value (name, binding)
1058
+ if name.to_s.start_with?(/[A-Z]/)
1059
+ binding.eval(name.to_s)
1060
+ elsif binding.local_variable_defined?(name)
1061
+ binding.local_variable_get(name)
1062
+ end
1063
+ rescue NameError
1064
+ nil
1065
+ end
1066
+
1067
+ # Whether the name held anything at all, which `captured_value` cannot
1068
+ # say: a name holding nil and a name that is not there both come back
1069
+ # as nil, and only one of them is worth a different message.
1070
+ def captured_name_defined? (name, binding)
1071
+ if name.to_s.start_with?(/[A-Z]/)
1072
+ binding.eval("defined?(#{name})") ? true : false
1073
+ else
1074
+ binding.local_variable_defined?(name)
1075
+ end
1076
+ rescue NameError
1077
+ false
617
1078
  end
618
1079
 
619
1080
  def function_registry
@@ -621,19 +1082,24 @@ class CArray
621
1082
  end
622
1083
 
623
1084
  def build_c_function (prototype, name, return_type, parameters,
624
- source, node, origin, block, symbol)
1085
+ source, node, origin, block, symbol, called = {},
1086
+ declared_parameters = nil)
625
1087
  # A function is a function of its parameters. Whatever else the block
626
1088
  # reaches for is refused, and the reason differs by what it is -- so
627
1089
  # the captures are looked at before the body is walked, or the body
628
1090
  # would raise first and say something less useful.
629
- names = block.parameters.map(&:last)
1091
+ # A body written for `jit_call` takes no parameters -- the
1092
+ # declaration named them, and the same names are the locals the call
1093
+ # reads -- so the arity is the declaration's to state.
1094
+ names = declared_parameters || block.parameters.map(&:last)
630
1095
  unless names.size == parameters.size
631
1096
  raise Unsupported,
632
1097
  "`#{prototype}` names #{parameters.size} " \
633
1098
  "#{parameters.size == 1 ? 'parameter' : 'parameters'}, and " \
634
1099
  "the block takes #{names.size}"
635
1100
  end
636
- refuse_captures(source, node, block, names, name && name.to_sym)
1101
+ refuse_captures(source, node, block, names + called.keys,
1102
+ name && name.to_sym)
637
1103
 
638
1104
  # `void` is a return type a body may have: a function whose work is
639
1105
  # through its pointer parameters has nothing to hand back, and C says
@@ -659,6 +1125,9 @@ class CArray
659
1125
  .to_h { |name, type|
660
1126
  [name, type.indexable? ? !type.const : nil]
661
1127
  }
1128
+ pointer_lengths = names.zip(parameters)
1129
+ .select { |_, type| type.indexable? && type.sized? }
1130
+ .to_h { |name, type| [name, type.array] }
662
1131
  pointer_types = names.zip(parameters).select { |_, type| type.indexable? }
663
1132
  .to_h { |name, type| [name, type.element.computation] }
664
1133
 
@@ -666,8 +1135,11 @@ class CArray
666
1135
  # own body, as C does, so the body can call itself. An anonymous one
667
1136
  # has nothing to call itself by, and gets no recursion.
668
1137
  analyzer = Analyzer.new(source, node: node, function: true,
1138
+ declared_parameters: declared_parameters,
669
1139
  returns: !returns_nothing,
670
1140
  pointers: pointers,
1141
+ pointer_lengths: pointer_lengths,
1142
+ c_functions: called,
671
1143
  recursion: (name && [name.to_sym, parameters,
672
1144
  return_type.computation]))
673
1145
  names = analyzer.parameter_names
@@ -688,32 +1160,35 @@ class CArray
688
1160
  "the signature rather than a value; the body cannot read it"
689
1161
  end)
690
1162
  end
691
- # A body is handed its values through the kernel's three scalar buses,
692
- # which carry doubles, int64s and complexes. A uint64 argument is the
693
- # one numeric type none of them can carry whole -- that is what having
694
- # its own computation type means -- so it is refused here, where the
695
- # declaration is, rather than deeper down as a kernel that cannot be
696
- # built. A `uint64_t *` reaches an array of them, and a uint64_t
697
- # value comes back out of a body unharmed.
698
- names.zip(parameters).each do |parameter, type|
699
- next if type.pointer || type.computation != :uint64
700
- raise Unsupported,
701
- "`#{parameter}` is declared `#{type.text}`, and a value is " \
702
- "handed to a body as a double, an int64 or a complex -- a " \
703
- "uint64 fits none of them without losing a bit. Take " \
704
- "`#{type.text} *` and index it, or take an int64 where the " \
705
- "values are small enough to be one"
706
- end
1163
+ # A parameter of any computation type the body can work in, uint64
1164
+ # included. A kernel's captures have three buses to travel in and
1165
+ # uint64 is not one of them, but these are not captures: they are the
1166
+ # parameters of this function's own C signature, and arrive in the
1167
+ # type the declaration named. So `size_t n` is a value the body can
1168
+ # be handed, which is the point -- it is how the C that counts bytes
1169
+ # is spelled.
707
1170
  types = names.zip(parameters).reject { |_, type| type.pointer }
708
1171
  .to_h { |parameter, type| [parameter, type.computation] }
709
1172
 
710
- assignment = TypeAssignment.new(analyzer.body, {}, {}, {},
1173
+ assignment = TypeAssignment.new(analyzer.body, {}, {}, called,
711
1174
  scalar_types: types,
712
1175
  pointer_types: pointer_types)
713
1176
  generator = CGenerator.new(analyzer, {}, assignment.scalar_types,
714
- origin: origin, block_source: source)
1177
+ c_functions: called,
1178
+ origin: origin, block_source: source,
1179
+ scalar_parameters: types.keys)
715
1180
  c_source = generator.generate_function(
716
1181
  symbol, names, parameters, return_type.text, return_type.computation)
1182
+ # A signature carrying a complex by value gets a second entry point
1183
+ # for Ruby to reach it by, since Fiddle has no such type to call
1184
+ # with. Only such a signature: everything else is called directly,
1185
+ # and pays nothing for a road it does not take.
1186
+ shim_symbol = nil
1187
+ if return_type.complex? || parameters.any?(&:complex?)
1188
+ shim_symbol = "#{symbol}_from_ruby"
1189
+ c_source += shim_source(shim_symbol, symbol, names, parameters,
1190
+ return_type)
1191
+ end
717
1192
  handle, = Compiler.build(c_source, symbol, header: generator.provenance)
718
1193
  # Where the body can divide by zero or reach outside an array, the
719
1194
  # object carries a place to say so. Reading it is what lets a call
@@ -728,20 +1203,68 @@ class CArray
728
1203
  pasted = generator
729
1204
  if generator.uses_error_flag?
730
1205
  pasted = CGenerator.new(analyzer, {}, assignment.scalar_types,
731
- origin: origin, block_source: source)
1206
+ c_functions: called,
1207
+ origin: origin, block_source: source,
1208
+ scalar_parameters: types.keys)
732
1209
  pasted.generate_function(symbol, names, parameters, return_type.text,
733
1210
  return_type.computation,
734
1211
  error_parameter: true)
735
1212
  end
736
- CFunction.new(symbol, prototype, return_type, parameters, handle[symbol],
1213
+ CFunction.new(name, prototype, return_type, parameters, handle[symbol],
1214
+ symbol: symbol,
1215
+ shim: shim_symbol && handle[shim_symbol],
737
1216
  block: block, c_source: generator.provenance + c_source,
738
1217
  origin: origin, error: error,
739
1218
  definition: pasted.function_definition,
740
1219
  helpers: pasted.helper_needs,
1220
+ dependencies: called.values,
741
1221
  takes_error: generator.uses_error_flag?,
742
1222
  raise_messages: generator.raise_messages)
743
1223
  end
744
1224
 
1225
+ # The entry point a call from Ruby takes where the signature carries a
1226
+ # complex by value. Each complex argument arrives as the two doubles
1227
+ # C99 lays one out as, and a complex result is written back the same
1228
+ # way; every other parameter keeps its own type, so a pointer is still
1229
+ # a pointer and an integer is still that integer.
1230
+ #
1231
+ # It calls the function rather than repeating it, so there is one body
1232
+ # and both roads reach it.
1233
+ def shim_source (shim_symbol, symbol, names, parameters, return_type)
1234
+ declarations = names.zip(parameters).map { |name, type|
1235
+ type.complex? ? "const double *#{name}" : type.declare(name)
1236
+ }
1237
+ passed = names.zip(parameters).map { |name, type|
1238
+ next name unless type.complex?
1239
+ "#{type.complex_build}(#{name}[0], #{name}[1])"
1240
+ }
1241
+ call = "#{symbol}(#{passed.join(', ')})"
1242
+ if return_type.complex?
1243
+ declarations << "double *carray_jit_result"
1244
+ body = <<~C
1245
+ #{return_type.text} carray_jit_value = #{call};
1246
+ carray_jit_result[0] = creal(carray_jit_value);
1247
+ carray_jit_result[1] = cimag(carray_jit_value);
1248
+ C
1249
+ head = "void"
1250
+ else
1251
+ body = "#{return_type.computation ? 'return ' : ''}#{call};\n"
1252
+ head = return_type.text
1253
+ end
1254
+ <<~C
1255
+
1256
+ /* Fiddle has no type for a C99 complex, so a call from Ruby comes
1257
+ through here: the complex arguments arrive as the two doubles
1258
+ one is laid out as, and a complex result goes back the same way.
1259
+ A kernel calls the function above directly, C to C. */
1260
+ #{head}
1261
+ #{shim_symbol} (#{declarations.join(', ')})
1262
+ {
1263
+ #{body.strip}
1264
+ }
1265
+ C
1266
+ end
1267
+
745
1268
  # Nothing outside the parameter list may be reached. A number could in
746
1269
  # principle be written into the C as a literal, and another c_function's
747
1270
  # address as a constant -- but both would put something in the compiled
@@ -766,16 +1289,30 @@ class CArray
766
1289
  captured -= [own_name] if own_name
767
1290
  return if captured.empty?
768
1291
  name = captured.first
769
- value = begin
770
- binding_of(block).local_variable_get(name)
771
- rescue NameError
772
- nil
773
- end
774
- kind = case value
775
- when CArray then "an array"
776
- when CFunction then "another C function"
777
- else "a value"
778
- end
1292
+ value = captured_value(name, binding_of(block))
1293
+ # A CFunction never reaches here: `called_functions` took both
1294
+ # kinds above -- compiled ones to paste, borrowed ones to declare and
1295
+ # call by name -- so neither is a capture by the time this runs.
1296
+ if value.is_a?(CArray::Rng)
1297
+ # A generator has state, and a compiled function has nowhere to
1298
+ # keep one -- the same reason a borrowed address is refused above.
1299
+ # A kernel is handed its operands at every call and can be given
1300
+ # the state among them; a function's arguments are the ones its
1301
+ # declaration named, and adding one behind the caller's back would
1302
+ # change the C signature it was promised. So the state is named,
1303
+ # and the body draws from it the way the generator's own C does.
1304
+ raise Unsupported,
1305
+ "this function reaches `#{name}`, which is a generator; a " \
1306
+ "compiled function has nowhere to keep its state. Take the " \
1307
+ "state as a parameter -- declare `int64_t state[4]` and pass " \
1308
+ "`#{name}.state` -- or draw in the kernel that calls this, " \
1309
+ "where `random(rng: #{name})` works"
1310
+ end
1311
+ unless captured_name_defined?(name, binding_of(block))
1312
+ raise Unsupported,
1313
+ "`#{name}` is not defined where the block was written"
1314
+ end
1315
+ kind = value.is_a?(CArray) ? "an array" : "a value"
779
1316
  raise Unsupported,
780
1317
  "this function reaches `#{name}`, which is #{kind} outside it; " \
781
1318
  "a compiled function takes everything it needs through its " \