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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +771 -3
- data/README.md +7 -6
- data/carray-jit.gemspec +1 -3
- data/docs/00_Introduction.md +4 -3
- data/docs/01_GettingStarted.md +1 -1
- data/docs/02_KernelShapes.md +93 -14
- data/docs/03_SupportedFeatures.md +582 -26
- data/docs/04_Compiling.md +33 -6
- data/docs/05_DesignNotes.md +3 -3
- data/docs/06_Cheatsheet.md +198 -5
- data/docs/07_StepByStep.ja.md +534 -0
- data/docs/07_StepByStep.md +535 -0
- data/examples/README.md +12 -0
- data/examples/applications/alarm.rb +121 -0
- data/examples/applications/collatz.rb +105 -0
- data/examples/applications/cubic_spline.rb +331 -0
- data/examples/applications/dithering.rb +144 -0
- data/examples/applications/group_stats.rb +115 -0
- data/examples/applications/lookup.rb +126 -0
- data/examples/applications/median_filter.rb +153 -0
- data/examples/applications/parcel_ascent.rb +220 -0
- data/examples/applications/point_in_polygon.rb +111 -0
- data/examples/applications/random_walk.rb +98 -0
- data/examples/applications/van_der_pol.rb +186 -0
- data/examples/applications/wet_bulb.rb +140 -0
- data/examples/features/10_complex.rb +14 -4
- data/examples/features/15_loops.rb +7 -1
- data/lib/carray/jit/access.rb +14 -0
- data/lib/carray/jit/analyzer.rb +2077 -136
- data/lib/carray/jit/block_reader.rb +37 -6
- data/lib/carray/jit/c_function.rb +613 -76
- data/lib/carray/jit/c_generator.rb +1595 -156
- data/lib/carray/jit/call.rb +68 -0
- data/lib/carray/jit/compiler.rb +75 -11
- data/lib/carray/jit/kernel.rb +369 -32
- data/lib/carray/jit/node.rb +359 -9
- data/lib/carray/jit/sorting_networks.rb +182 -0
- data/lib/carray/jit/type_assignment.rb +371 -34
- data/lib/carray/jit/version.rb +1 -1
- data/lib/carray/jit.rb +560 -64
- metadata +22 -8
- data/ext/carray_jit_access/carray_jit_access.c +0 -460
- 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, @
|
|
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:
|
|
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
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
}
|
|
220
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
#
|
|
248
|
-
#
|
|
249
|
-
#
|
|
250
|
-
#
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
263
|
-
|
|
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, "#{
|
|
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
|
-
"`#{
|
|
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
|
|
445
|
-
#
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
|
596
|
-
#
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
692
|
-
#
|
|
693
|
-
#
|
|
694
|
-
#
|
|
695
|
-
#
|
|
696
|
-
#
|
|
697
|
-
#
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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 =
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
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 " \
|