carray-jit 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +917 -3
- data/README.md +14 -12
- data/carray-jit.gemspec +1 -3
- data/docs/00_Introduction.md +5 -3
- data/docs/01_GettingStarted.md +1 -1
- data/docs/02_KernelShapes.md +94 -15
- data/docs/03_SupportedFeatures.md +665 -32
- data/docs/04_Compiling.md +34 -7
- data/docs/05_DesignNotes.md +3 -3
- data/docs/06_Cheatsheet.md +200 -7
- data/docs/07_StepByStep.ja.md +533 -0
- data/docs/07_StepByStep.md +534 -0
- data/docs/08_LanguageReference.md +427 -0
- data/examples/README.md +12 -0
- data/examples/applications/alarm.rb +121 -0
- data/examples/applications/collatz.rb +105 -0
- data/examples/applications/cubic_spline.rb +331 -0
- data/examples/applications/dithering.rb +144 -0
- data/examples/applications/group_stats.rb +115 -0
- data/examples/applications/lookup.rb +126 -0
- data/examples/applications/median_filter.rb +153 -0
- data/examples/applications/parcel_ascent.rb +220 -0
- data/examples/applications/point_in_polygon.rb +111 -0
- data/examples/applications/random_walk.rb +98 -0
- data/examples/applications/van_der_pol.rb +186 -0
- data/examples/applications/wet_bulb.rb +140 -0
- data/examples/features/10_complex.rb +14 -4
- data/examples/features/15_loops.rb +7 -1
- data/lib/carray/jit/access.rb +14 -0
- data/lib/carray/jit/analyzer.rb +2218 -140
- data/lib/carray/jit/block_reader.rb +37 -6
- data/lib/carray/jit/c_function.rb +961 -86
- data/lib/carray/jit/c_generator.rb +1970 -191
- data/lib/carray/jit/call.rb +68 -0
- data/lib/carray/jit/compiler.rb +102 -14
- data/lib/carray/jit/expression.rb +271 -33
- data/lib/carray/jit/kernel.rb +382 -32
- data/lib/carray/jit/node.rb +374 -9
- data/lib/carray/jit/sorting_networks.rb +182 -0
- data/lib/carray/jit/type_assignment.rb +424 -35
- data/lib/carray/jit/version.rb +1 -1
- data/lib/carray/jit.rb +547 -77
- metadata +23 -8
- data/ext/carray_jit_access/carray_jit_access.c +0 -460
- data/ext/carray_jit_access/extconf.rb +0 -8
|
@@ -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,32 @@ 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,
|
|
82
|
-
|
|
113
|
+
symbol: nil,
|
|
114
|
+
block: nil, c_source: nil, origin: nil,
|
|
83
115
|
definition: nil, helpers: nil, takes_error: false,
|
|
84
|
-
raise_messages: {}
|
|
116
|
+
raise_messages: {}, dependencies: [], shim: nil,
|
|
117
|
+
error_state: nil)
|
|
118
|
+
# The name the declaration gave, which is what a reader wrote and
|
|
119
|
+
# what an error should say. It is nil where the declaration gave
|
|
120
|
+
# none -- `double (*)(double)` names no function.
|
|
85
121
|
@name = name && name.to_sym
|
|
122
|
+
# The name in the object, which is what a call reaches and what two
|
|
123
|
+
# functions have to differ by. For one bound from a library they
|
|
124
|
+
# are the same; for one compiled here the symbol carries a digest of
|
|
125
|
+
# the body, so that two bodies declared alike stay apart.
|
|
126
|
+
@symbol = (symbol || name)&.to_sym
|
|
86
127
|
@prototype = prototype
|
|
87
128
|
@return_type = return_type
|
|
88
129
|
@parameters = parameters
|
|
@@ -95,15 +136,21 @@ class CArray
|
|
|
95
136
|
# what it wants from a preamble -- what a kernel needs to paste it.
|
|
96
137
|
@definition = definition
|
|
97
138
|
@helpers = helpers
|
|
139
|
+
@dependencies = dependencies
|
|
98
140
|
# True when that definition ends in an `int32_t *`: the body can report
|
|
99
141
|
# a failure, and pasted it reports into the caller's slot rather than
|
|
100
142
|
# into the flag in its own object.
|
|
101
143
|
@takes_error = takes_error
|
|
102
144
|
@raise_messages = raise_messages
|
|
103
145
|
@origin = origin
|
|
104
|
-
#
|
|
105
|
-
#
|
|
106
|
-
|
|
146
|
+
# What the object answers when asked where this thread's flag, hook
|
|
147
|
+
# and stand-in stand. Nil when the body can neither divide by zero
|
|
148
|
+
# nor raise, and so has none of them.
|
|
149
|
+
@error_state = error_state
|
|
150
|
+
# The address of the entry point a call from Ruby takes where the
|
|
151
|
+
# signature carries a complex by value; nil where it does not, which
|
|
152
|
+
# is every other signature and every borrowed function.
|
|
153
|
+
@shim = shim
|
|
107
154
|
@function = nil
|
|
108
155
|
end
|
|
109
156
|
|
|
@@ -130,6 +177,16 @@ class CArray
|
|
|
130
177
|
pasted? && @takes_error
|
|
131
178
|
end
|
|
132
179
|
|
|
180
|
+
# The declaration under the name the block reached it by. What a
|
|
181
|
+
# message about a call should say: the caller wrote `f`, and the symbol
|
|
182
|
+
# a body compiled here carries -- `carray_jit_two_<digest>` -- along
|
|
183
|
+
# with the file it was written in are answers to a question nobody
|
|
184
|
+
# asked there. Those stay in `to_s`, which names the object rather
|
|
185
|
+
# than the call.
|
|
186
|
+
def declaration_as (name)
|
|
187
|
+
"#{@return_type.text} #{name}(#{@parameters.map(&:text).join(', ')})"
|
|
188
|
+
end
|
|
189
|
+
|
|
133
190
|
# The signature, without the address.
|
|
134
191
|
def signature
|
|
135
192
|
[@return_type.text, @parameters.map(&:text)]
|
|
@@ -147,7 +204,54 @@ class CArray
|
|
|
147
204
|
# the same way. Splitting the cache costs a compile per body; sharing
|
|
148
205
|
# it would hand back the wrong answer, and would do it quietly.
|
|
149
206
|
def kernel_key
|
|
150
|
-
compiled? ? [signature, @
|
|
207
|
+
compiled? ? [signature, @symbol] : signature
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# What to call this in a message: the name its declaration gave, and
|
|
211
|
+
# the symbol where it gave none.
|
|
212
|
+
def label
|
|
213
|
+
@name || @symbol
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# The C this was compiled from, under a symbol of the caller's
|
|
217
|
+
# choosing.
|
|
218
|
+
#
|
|
219
|
+
# For a caller writing the C into a file of its own rather than letting
|
|
220
|
+
# this compile it: the symbol here carries a digest of the body, which
|
|
221
|
+
# is right for an object in a cache and wrong for one in a repository,
|
|
222
|
+
# where the same build has to give the same name every time.
|
|
223
|
+
#
|
|
224
|
+
# It is a rename rather than a second run of the generator, and that is
|
|
225
|
+
# exact rather than close enough: the symbol is the only thing in the
|
|
226
|
+
# generated file that the choice of symbol decides. Two compilations
|
|
227
|
+
# of one block under two declared names differ in nothing else once
|
|
228
|
+
# both symbols are levelled, which the suite checks.
|
|
229
|
+
#
|
|
230
|
+
# The caller owns the name it picks, and with it the one hazard the
|
|
231
|
+
# generator's own prefix exists to close: a body declared `sin`
|
|
232
|
+
# emitted as `sin` defines libm's, and the object exports it. What is
|
|
233
|
+
# refused here is a name C cannot spell, and the generator's own
|
|
234
|
+
# namespace -- an object built under `carray_jit_...` would answer to
|
|
235
|
+
# a symbol a cached kernel is entitled to.
|
|
236
|
+
def c_source_as (symbol)
|
|
237
|
+
symbol = symbol.to_s
|
|
238
|
+
unless @c_source
|
|
239
|
+
raise Unsupported,
|
|
240
|
+
"`#{label}` was bound from a library rather than compiled " \
|
|
241
|
+
"here, so there is no C of its own to write out"
|
|
242
|
+
end
|
|
243
|
+
unless symbol =~ /\A[A-Za-z_][A-Za-z0-9_]*\z/
|
|
244
|
+
raise Unsupported,
|
|
245
|
+
"`#{symbol}` is not a C identifier, so nothing could be " \
|
|
246
|
+
"defined under it"
|
|
247
|
+
end
|
|
248
|
+
if symbol.start_with?(PREFIX)
|
|
249
|
+
raise Unsupported,
|
|
250
|
+
"`#{symbol}` is in this compiler's own namespace " \
|
|
251
|
+
"(`#{PREFIX}`), where a cached kernel is entitled to the " \
|
|
252
|
+
"name; pick one of your own"
|
|
253
|
+
end
|
|
254
|
+
@c_source.gsub(@symbol.to_s, symbol)
|
|
151
255
|
end
|
|
152
256
|
|
|
153
257
|
# @return [Integer] how many arguments the function takes.
|
|
@@ -198,9 +302,16 @@ class CArray
|
|
|
198
302
|
"wrong number of arguments (given #{arguments.size}, " \
|
|
199
303
|
"expected #{@parameters.size})"
|
|
200
304
|
end
|
|
305
|
+
return call_through_shim(arguments) if @shim
|
|
306
|
+
if carries_a_complex?
|
|
307
|
+
raise Unsupported,
|
|
308
|
+
"`#{self}` carries a C99 complex by value, which Fiddle has " \
|
|
309
|
+
"no type for, so it cannot be called from Ruby; a kernel " \
|
|
310
|
+
"calls it as C calls it"
|
|
311
|
+
end
|
|
201
312
|
@function ||= Fiddle::Function.new(@pointer, argument_types,
|
|
202
313
|
@return_type.fiddle,
|
|
203
|
-
name:
|
|
314
|
+
name: label.to_s)
|
|
204
315
|
arrays = []
|
|
205
316
|
prepared = arguments.zip(@parameters).map { |argument, type|
|
|
206
317
|
next argument unless type.indexable? && argument.is_a?(CArray)
|
|
@@ -208,18 +319,31 @@ class CArray
|
|
|
208
319
|
arrays << [argument, buffer, type]
|
|
209
320
|
buffer
|
|
210
321
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
322
|
+
# Borrowed rather than cleared, and borrowed here rather than on the
|
|
323
|
+
# way in: a call made inside a window -- someone else is holding this
|
|
324
|
+
# function's address and watching the same flag -- must answer for
|
|
325
|
+
# itself without disarming them. The checks above never reach the C
|
|
326
|
+
# and so never touch the flag at all.
|
|
327
|
+
outer = error_code
|
|
328
|
+
write_error(0)
|
|
329
|
+
hook = disarm_hook
|
|
330
|
+
begin
|
|
331
|
+
Access.open(arrays.map { |_, buffer, _| buffer },
|
|
332
|
+
arrays.map { |_, _, type| !type.const },
|
|
333
|
+
arrays.map { nil }, arrays.map { nil }) do |bases|
|
|
334
|
+
slot = -1
|
|
335
|
+
prepared = prepared.map { |value|
|
|
336
|
+
next value unless arrays.any? { |_, buffer, _| buffer.equal?(value) }
|
|
337
|
+
Fiddle::Pointer.new(bases[slot += 1][:pointer])
|
|
338
|
+
}
|
|
339
|
+
@result = @function.call(*prepared)
|
|
340
|
+
end
|
|
341
|
+
code = error_code
|
|
342
|
+
ensure
|
|
343
|
+
write_error(outer)
|
|
344
|
+
rearm_hook(hook)
|
|
221
345
|
end
|
|
222
|
-
|
|
346
|
+
raise_for(code)
|
|
223
347
|
# A view was copied to be made contiguous; a writable one is copied
|
|
224
348
|
# back, because the C wrote into the copy.
|
|
225
349
|
arrays.each do |array, buffer, type|
|
|
@@ -230,6 +354,79 @@ class CArray
|
|
|
230
354
|
|
|
231
355
|
alias [] call
|
|
232
356
|
|
|
357
|
+
# Whether a call from Ruby has to go round through the shim.
|
|
358
|
+
def carries_a_complex?
|
|
359
|
+
@return_type.complex? || @parameters.any?(&:complex?)
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
# Fiddle carries no complex, so the shim takes each complex argument as
|
|
363
|
+
# a pair of doubles and writes a complex result back the same way.
|
|
364
|
+
# Everything else keeps the type the declaration gave it and goes
|
|
365
|
+
# through Fiddle as before -- including a pointer parameter, which is
|
|
366
|
+
# still a CArray on this side.
|
|
367
|
+
#
|
|
368
|
+
# It is the same compiled body either way: the shim calls the function,
|
|
369
|
+
# it does not reimplement it.
|
|
370
|
+
def call_through_shim (arguments)
|
|
371
|
+
returns_complex = @return_type.complex?
|
|
372
|
+
types = @parameters.map { |type|
|
|
373
|
+
type.complex? ? Fiddle::TYPE_VOIDP : type.fiddle
|
|
374
|
+
}
|
|
375
|
+
types << Fiddle::TYPE_VOIDP if returns_complex
|
|
376
|
+
@shim_function ||=
|
|
377
|
+
Fiddle::Function.new(@shim, types,
|
|
378
|
+
returns_complex ? Fiddle::TYPE_VOID
|
|
379
|
+
: @return_type.fiddle)
|
|
380
|
+
result = returns_complex ? String.new("\0" * 16) : nil
|
|
381
|
+
arrays = []
|
|
382
|
+
prepared = arguments.zip(@parameters).map { |argument, type|
|
|
383
|
+
next pack_complex(argument, type) if type.complex?
|
|
384
|
+
next argument unless type.indexable? && argument.is_a?(CArray)
|
|
385
|
+
buffer = check_array(argument, type)
|
|
386
|
+
arrays << [argument, buffer, type]
|
|
387
|
+
buffer
|
|
388
|
+
}
|
|
389
|
+
outer = error_code
|
|
390
|
+
write_error(0)
|
|
391
|
+
hook = disarm_hook
|
|
392
|
+
begin
|
|
393
|
+
Access.open(arrays.map { |_, buffer, _| buffer },
|
|
394
|
+
arrays.map { |_, _, type| !type.const },
|
|
395
|
+
arrays.map { nil }, arrays.map { nil }) do |bases|
|
|
396
|
+
slot = -1
|
|
397
|
+
passed = prepared.map { |value|
|
|
398
|
+
next value unless arrays.any? { |_, buffer, _| buffer.equal?(value) }
|
|
399
|
+
Fiddle::Pointer.new(bases[slot += 1][:pointer])
|
|
400
|
+
}
|
|
401
|
+
passed << result if returns_complex
|
|
402
|
+
@result = @shim_function.call(*passed)
|
|
403
|
+
end
|
|
404
|
+
code = error_code
|
|
405
|
+
ensure
|
|
406
|
+
write_error(outer)
|
|
407
|
+
rearm_hook(hook)
|
|
408
|
+
end
|
|
409
|
+
raise_for(code)
|
|
410
|
+
arrays.each do |array, buffer, type|
|
|
411
|
+
array[] = buffer unless type.const || array.equal?(buffer)
|
|
412
|
+
end
|
|
413
|
+
@result = Complex(*result.unpack("dd")) if returns_complex
|
|
414
|
+
@result
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
# A Ruby number of any kind arrives as the two doubles the shim reads.
|
|
418
|
+
# `Complex()` is what Ruby itself converts with, so an Integer and a
|
|
419
|
+
# Float are taken where a Complex is asked for, as they are in Ruby.
|
|
420
|
+
def pack_complex (value, type)
|
|
421
|
+
number = begin
|
|
422
|
+
Complex(value)
|
|
423
|
+
rescue TypeError, ArgumentError
|
|
424
|
+
raise Unsupported,
|
|
425
|
+
"`#{type.text}` takes a number, got #{value.class}"
|
|
426
|
+
end
|
|
427
|
+
String.new([number.real.to_f, number.imaginary.to_f].pack("dd"))
|
|
428
|
+
end
|
|
429
|
+
|
|
233
430
|
# @return [String] the function's name.
|
|
234
431
|
def to_s
|
|
235
432
|
text = "#{@return_type.text} #{@name}" \
|
|
@@ -242,40 +439,284 @@ class CArray
|
|
|
242
439
|
"#<CArray::JIT::CFunction #{self}>"
|
|
243
440
|
end
|
|
244
441
|
|
|
245
|
-
|
|
442
|
+
# The window a caller opens when it hands the address out.
|
|
443
|
+
#
|
|
444
|
+
# `#call` is one call, and answers for it before it returns. A library
|
|
445
|
+
# given `#pointer` calls whenever it likes, as often as it likes, and
|
|
446
|
+
# what wants an answer is the whole of that -- so the flag is put down
|
|
447
|
+
# once, the address is lent for as long as the block runs, and what
|
|
448
|
+
# happened is asked for once at the end. It is the arrangement a kernel
|
|
449
|
+
# already keeps with its own slot, which is cleared before a sweep and
|
|
450
|
+
# read after it, never per cell.
|
|
451
|
+
#
|
|
452
|
+
# f.watching do
|
|
453
|
+
# Integration.qags(f.pointer, 0.0, 1.0)
|
|
454
|
+
# end
|
|
455
|
+
#
|
|
456
|
+
# A failure inside the block outranks whatever the library made of it.
|
|
457
|
+
# A body that fails returns a stand-in, so the library is the first to
|
|
458
|
+
# complain -- that the endpoints do not straddle, that the iteration did
|
|
459
|
+
# not converge -- and those complaints are the failure's consequences,
|
|
460
|
+
# not what happened. So the flag is read before that exception is let
|
|
461
|
+
# through, and only where nothing stands does the library's own story
|
|
462
|
+
# get to be the story.
|
|
463
|
+
#
|
|
464
|
+
# Windows nest, and a call made inside one leaves it armed: both borrow
|
|
465
|
+
# the flag and put it back as they found it, so an inner window answers
|
|
466
|
+
# for its own block and no other.
|
|
467
|
+
#
|
|
468
|
+
# `stand_in:` is the number a call answers C with once the flag stands,
|
|
469
|
+
# for a library that reads the return value as a status rather than as
|
|
470
|
+
# a value -- GSL's callbacks return `int`, and 0 is `GSL_SUCCESS`
|
|
471
|
+
# there, so a failed body reporting 0 tells GSL the step went well. It
|
|
472
|
+
# is cast to whatever the body returns and is 0 until a window says
|
|
473
|
+
# otherwise, which is what a failed call has always answered. Set for
|
|
474
|
+
# the length of the window, and put back afterwards.
|
|
475
|
+
def watching (stand_in: nil)
|
|
476
|
+
opened = open_window(nil, JIT.send(:window_stand_in, stand_in))
|
|
477
|
+
code = 0
|
|
478
|
+
begin
|
|
479
|
+
result = yield
|
|
480
|
+
rescue StandardError
|
|
481
|
+
# The library is complaining about what a stand-in did. The body's
|
|
482
|
+
# own failure is the cause, so it is the one that comes out.
|
|
483
|
+
code = close_window(opened)
|
|
484
|
+
opened = nil
|
|
485
|
+
raise_for(code)
|
|
486
|
+
raise
|
|
487
|
+
ensure
|
|
488
|
+
code = close_window(opened) if opened
|
|
489
|
+
end
|
|
490
|
+
raise_for(code)
|
|
491
|
+
result
|
|
492
|
+
end
|
|
246
493
|
|
|
247
|
-
#
|
|
248
|
-
#
|
|
249
|
-
#
|
|
250
|
-
#
|
|
494
|
+
# Put the flag down, before lending the address to something that will
|
|
495
|
+
# call it more than once. `#watching` is this and #report_error with
|
|
496
|
+
# the lending in between, and is what to reach for where the window is a
|
|
497
|
+
# block; these two are here for a window that is not -- one opened in
|
|
498
|
+
# one method and closed in another, or one whose block belongs to
|
|
499
|
+
# somebody else.
|
|
251
500
|
def clear_error
|
|
252
|
-
|
|
501
|
+
write_error(0)
|
|
253
502
|
end
|
|
254
503
|
|
|
255
504
|
# What the kernel raises for the same code, since it is the same thing
|
|
256
505
|
# that happened: `6 % 0` is a ZeroDivisionError wherever it is written,
|
|
257
506
|
# and the compiled body cannot raise it itself. A caller reaching the
|
|
258
|
-
# address from C sees the
|
|
507
|
+
# address from C sees the stand-in the helper returned and the flag
|
|
259
508
|
# standing, which is C's own arrangement for a function that has to
|
|
260
509
|
# return something whatever happened.
|
|
510
|
+
#
|
|
511
|
+
# Quiet where nothing stands, so that a caller may ask having no idea
|
|
512
|
+
# whether anything failed -- which is the position a caller is in after
|
|
513
|
+
# handing the address to a library. Asking does not put the flag down:
|
|
514
|
+
# it reads, and the window that put it down is what picks it up.
|
|
261
515
|
def report_error
|
|
262
|
-
|
|
263
|
-
|
|
516
|
+
raise_for(error_code)
|
|
517
|
+
end
|
|
518
|
+
|
|
519
|
+
# A C function to call when the body fails, so that whoever holds the
|
|
520
|
+
# address can be told to stop rather than go on being handed stand-ins:
|
|
521
|
+
#
|
|
522
|
+
# f.on_error(nlopt["nlopt_force_stop"], opt)
|
|
523
|
+
# f.watching { nlopt_optimize.call(opt, x, minf) }
|
|
524
|
+
#
|
|
525
|
+
# `hook` is the address of a `void (*)(void *)` -- a Fiddle::Pointer,
|
|
526
|
+
# an Integer, or anything answering #to_i -- and `data` is the one
|
|
527
|
+
# argument it is called with. It is called once, from the call whose
|
|
528
|
+
# body put the flag up, and not again until the flag has been put down
|
|
529
|
+
# and has gone up again; nothing about it reaches Ruby, so it may be
|
|
530
|
+
# called from a thread that holds no GVL. `nil` takes it away.
|
|
531
|
+
#
|
|
532
|
+
# `#call` does not call it: that is one call made from Ruby, and it
|
|
533
|
+
# answers for itself by raising, as it does inside a window. A body
|
|
534
|
+
# that cannot fail has no flag to go up, so for it this is accepted
|
|
535
|
+
# and never called.
|
|
536
|
+
#
|
|
537
|
+
# Like the flag, it belongs to the thread that set it: a body lent from
|
|
538
|
+
# two threads at once has a hook and a flag in each of them, and this
|
|
539
|
+
# sets the one belonging to the thread that calls it. So it is set on
|
|
540
|
+
# the thread that will lend the address, which is where `#watching` is
|
|
541
|
+
# written anyway.
|
|
542
|
+
#
|
|
543
|
+
# Returns self.
|
|
544
|
+
def on_error (hook, data = nil)
|
|
545
|
+
state = error_state
|
|
546
|
+
return self unless state
|
|
547
|
+
address = hook.nil? ? 0 : hook.to_i
|
|
548
|
+
state.data[0, Fiddle::SIZEOF_VOIDP] =
|
|
549
|
+
[data.nil? ? 0 : data.to_i].pack("J")
|
|
550
|
+
state.hook[0, Fiddle::SIZEOF_VOIDP] = [address].pack("J")
|
|
551
|
+
state.held = hook.nil? ? nil : [hook, data]
|
|
552
|
+
self
|
|
553
|
+
end
|
|
554
|
+
|
|
555
|
+
private
|
|
556
|
+
|
|
557
|
+
# One thread's copy of the four the compiled object carries -- the
|
|
558
|
+
# flag, the hook's address, the pointer it is handed, and what a failed
|
|
559
|
+
# call answers C with -- with the Ruby objects `#on_error` was given
|
|
560
|
+
# held beside them, so that nothing the C may still call is collected.
|
|
561
|
+
ErrorState = Struct.new(:flag, :hook, :data, :standin, :held)
|
|
562
|
+
private_constant :ErrorState
|
|
563
|
+
|
|
564
|
+
# Where those copies are kept, per thread. A thread variable rather
|
|
565
|
+
# than a fiber-local one, because what is being tracked is the C's own
|
|
566
|
+
# storage: every fiber running on a thread reaches that thread's copy.
|
|
567
|
+
THREAD_STATES = :carray_jit_error_states
|
|
568
|
+
private_constant :THREAD_STATES
|
|
569
|
+
|
|
570
|
+
# This thread's, resolved by asking the object where they stand and
|
|
571
|
+
# kept for as long as the thread lives -- a thread's copies do not
|
|
572
|
+
# move. Nil where the body cannot fail at all: one that neither
|
|
573
|
+
# divides nor raises is compiled without a flag, and has no failure to
|
|
574
|
+
# report rather than an unread one.
|
|
575
|
+
def error_state
|
|
576
|
+
return nil unless @error_state
|
|
577
|
+
thread = Thread.current
|
|
578
|
+
kept = thread.thread_variable_get(THREAD_STATES)
|
|
579
|
+
unless kept
|
|
580
|
+
kept = {}
|
|
581
|
+
thread.thread_variable_set(THREAD_STATES, kept)
|
|
582
|
+
end
|
|
583
|
+
# By symbol, which carries a digest of the body: two functions with
|
|
584
|
+
# the same symbol are one compiled object and one set of copies.
|
|
585
|
+
kept[@symbol] ||= resolve_error_state
|
|
586
|
+
end
|
|
587
|
+
|
|
588
|
+
def resolve_error_state
|
|
589
|
+
out = String.new("\0" * (4 * Fiddle::SIZEOF_VOIDP))
|
|
590
|
+
@error_state.call(out)
|
|
591
|
+
flag, hook, data, standin = out.unpack("J4")
|
|
592
|
+
ErrorState.new(Fiddle::Pointer.new(flag, 4),
|
|
593
|
+
Fiddle::Pointer.new(hook, Fiddle::SIZEOF_VOIDP),
|
|
594
|
+
Fiddle::Pointer.new(data, Fiddle::SIZEOF_VOIDP),
|
|
595
|
+
Fiddle::Pointer.new(standin, 8),
|
|
596
|
+
nil)
|
|
597
|
+
end
|
|
598
|
+
|
|
599
|
+
# 0 where the body cannot fail at all.
|
|
600
|
+
def error_code
|
|
601
|
+
state = error_state
|
|
602
|
+
state ? state.flag[0, 4].unpack1("l") : 0
|
|
603
|
+
end
|
|
604
|
+
|
|
605
|
+
def write_error (code)
|
|
606
|
+
state = error_state
|
|
607
|
+
state.flag[0, 4] = [code].pack("l") if state
|
|
608
|
+
end
|
|
609
|
+
|
|
610
|
+
# The hook's address, cleared for the length of one call from Ruby,
|
|
611
|
+
# and put back afterwards.
|
|
612
|
+
def disarm_hook
|
|
613
|
+
state = error_state
|
|
614
|
+
return nil unless state
|
|
615
|
+
held = state.hook[0, Fiddle::SIZEOF_VOIDP]
|
|
616
|
+
state.hook[0, Fiddle::SIZEOF_VOIDP] = "\0" * Fiddle::SIZEOF_VOIDP
|
|
617
|
+
held
|
|
618
|
+
end
|
|
619
|
+
|
|
620
|
+
def rearm_hook (held)
|
|
621
|
+
state = error_state
|
|
622
|
+
state.hook[0, Fiddle::SIZEOF_VOIDP] = held if state && held
|
|
623
|
+
end
|
|
624
|
+
|
|
625
|
+
# What a window does at its two ends. `#watching` is these with a
|
|
626
|
+
# block between them, and `CArray::JIT.watching` is these over several
|
|
627
|
+
# functions at once -- which is why they are named rather than written
|
|
628
|
+
# inline in the one method that had them.
|
|
629
|
+
#
|
|
630
|
+
# `hook` is the `[address, data]` a window wants a hook of its own for,
|
|
631
|
+
# and `standin` what it wants a failed call to answer C with; nil for
|
|
632
|
+
# either leaves whatever is set alone.
|
|
633
|
+
def open_window (hook = nil, standin = nil)
|
|
634
|
+
outer = error_code
|
|
635
|
+
write_error(0)
|
|
636
|
+
[outer, hook && hold_hook(*hook), standin && hold_standin(standin)]
|
|
637
|
+
end
|
|
638
|
+
|
|
639
|
+
# What this window's calls reported, with the flag, the hook and the
|
|
640
|
+
# stand-in put back as they were found.
|
|
641
|
+
def close_window (opened)
|
|
642
|
+
outer, held, standin = opened
|
|
643
|
+
code = error_code
|
|
644
|
+
write_error(outer)
|
|
645
|
+
restore_hook(held)
|
|
646
|
+
restore_standin(standin)
|
|
647
|
+
code
|
|
648
|
+
end
|
|
649
|
+
|
|
650
|
+
# This window's hook set and the one it displaced handed back, so that
|
|
651
|
+
# the window can put it there again. `#on_error` writes these two
|
|
652
|
+
# pointers; what it has no way to do is read them, and a window that
|
|
653
|
+
# brings a hook of its own has to -- otherwise it leaves with the
|
|
654
|
+
# owner's gone and nothing said about it.
|
|
655
|
+
def hold_hook (address, data)
|
|
656
|
+
state = error_state
|
|
657
|
+
return nil unless state
|
|
658
|
+
held = [state.hook[0, Fiddle::SIZEOF_VOIDP],
|
|
659
|
+
state.data[0, Fiddle::SIZEOF_VOIDP], state.held]
|
|
660
|
+
on_error(address, data)
|
|
661
|
+
held
|
|
662
|
+
end
|
|
663
|
+
|
|
664
|
+
def restore_hook (held)
|
|
665
|
+
state = error_state
|
|
666
|
+
return unless state && held
|
|
667
|
+
state.hook[0, Fiddle::SIZEOF_VOIDP] = held[0]
|
|
668
|
+
state.data[0, Fiddle::SIZEOF_VOIDP] = held[1]
|
|
669
|
+
state.held = held[2]
|
|
670
|
+
end
|
|
671
|
+
|
|
672
|
+
# This window's stand-in written, and the one it displaced handed back.
|
|
673
|
+
# Held as the eight bytes rather than as a number, so that whatever was
|
|
674
|
+
# there goes back exactly as it stood.
|
|
675
|
+
def hold_standin (value)
|
|
676
|
+
state = error_state
|
|
677
|
+
return nil unless state
|
|
678
|
+
held = state.standin[0, 8]
|
|
679
|
+
state.standin[0, 8] = [value].pack("q")
|
|
680
|
+
held
|
|
681
|
+
end
|
|
682
|
+
|
|
683
|
+
def restore_standin (held)
|
|
684
|
+
state = error_state
|
|
685
|
+
state.standin[0, 8] = held if state && held
|
|
686
|
+
end
|
|
687
|
+
|
|
688
|
+
def raise_for (code)
|
|
689
|
+
if (failure = CGenerator::FIXED_FAILURES[code])
|
|
690
|
+
raise failure[0], failure[1]
|
|
691
|
+
end
|
|
264
692
|
case code
|
|
265
693
|
when 0 then nil
|
|
266
694
|
when 1 then raise ZeroDivisionError, "divided by 0"
|
|
695
|
+
when 2 then raise IndexError, "index out of range"
|
|
696
|
+
when 3 then raise ArgumentError,
|
|
697
|
+
"min argument must be less than or equal to " \
|
|
698
|
+
"max argument"
|
|
699
|
+
when 4 then raise ArgumentError,
|
|
700
|
+
"comparison with a NaN failed, so `clamp` has " \
|
|
701
|
+
"no answer"
|
|
702
|
+
when 5 then raise Math::DomainError,
|
|
703
|
+
"Numerical argument is out of domain - gamma"
|
|
267
704
|
else
|
|
268
705
|
# `raise "..."` in the body. The message did not come back through
|
|
269
706
|
# the C -- it was registered when this was compiled -- so it is
|
|
270
707
|
# looked up here, and a kernel that pasted the same body looks the
|
|
271
708
|
# same message up under the same code.
|
|
272
709
|
message = @raise_messages[code]
|
|
273
|
-
raise Error, "#{
|
|
710
|
+
raise Error, "#{label} reported #{code}, which is no failure it " \
|
|
274
711
|
"was compiled to report" unless message
|
|
275
712
|
raise RuntimeError, message
|
|
276
713
|
end
|
|
277
714
|
end
|
|
278
715
|
|
|
716
|
+
# What a pointer parameter will accept, and what has to be true of it.
|
|
717
|
+
# The length is checked only where the declaration carried one: C's own
|
|
718
|
+
# rule is that an unsized pointer is the caller's responsibility, and
|
|
719
|
+
# writing `coef[3]` is how the caller asks to be checked.
|
|
279
720
|
def check_array (array, type)
|
|
280
721
|
wanted = CDeclaration::DATA_TYPES.fetch(type.element.fiddle)
|
|
281
722
|
unless array.data_type_name == wanted.to_s
|
|
@@ -317,7 +758,7 @@ class CArray
|
|
|
317
758
|
def computation_of (type, role)
|
|
318
759
|
return type.computation unless type.opaque?
|
|
319
760
|
raise Unsupported,
|
|
320
|
-
"`#{
|
|
761
|
+
"`#{label}` #{role} `#{type.text}`, which is a slot in the " \
|
|
321
762
|
"signature rather than a value a kernel can compute with"
|
|
322
763
|
end
|
|
323
764
|
|
|
@@ -342,24 +783,51 @@ class CArray
|
|
|
342
783
|
# aliases and its floating types are `float` and `double`.
|
|
343
784
|
KEYWORDS = %w[
|
|
344
785
|
const unsigned signed void char short int long float double
|
|
786
|
+
_Complex complex
|
|
345
787
|
int8_t int16_t int32_t int64_t
|
|
346
788
|
uint8_t uint16_t uint32_t uint64_t
|
|
347
789
|
size_t ssize_t ptrdiff_t intptr_t uintptr_t
|
|
348
790
|
].freeze
|
|
349
791
|
|
|
792
|
+
# Fiddle has no code for a C99 complex -- its parser does not know the
|
|
793
|
+
# word and the ABI it answers for has nowhere to put one -- so these
|
|
794
|
+
# stand where a Fiddle code stands elsewhere. Symbols rather than
|
|
795
|
+
# numbers, so that one reaching Fiddle by mistake is a TypeError there
|
|
796
|
+
# and not a silently wrong width.
|
|
797
|
+
COMPLEX_CODES = { "double" => :double_complex,
|
|
798
|
+
"float" => :float_complex }.freeze
|
|
799
|
+
|
|
800
|
+
# `double complex` is `<complex.h>`'s spelling of `double _Complex`,
|
|
801
|
+
# and both are written; which words a declaration used says nothing
|
|
802
|
+
# about what it declared.
|
|
803
|
+
COMPLEX_WORDS = %w[_Complex complex].freeze
|
|
804
|
+
|
|
350
805
|
# Fiddle answers `long double` with the code for `long`, silently, so it
|
|
351
806
|
# is refused by name rather than trusted.
|
|
352
807
|
REFUSED = {
|
|
353
808
|
"long double" => "`long double` is not a type this reads: Fiddle " \
|
|
354
809
|
"reports it as `long`, which would be the wrong " \
|
|
355
810
|
"width without saying so",
|
|
811
|
+
"long double _Complex" =>
|
|
812
|
+
"`long double _Complex` is not a type this reads: there is no " \
|
|
813
|
+
"long double here to make one of",
|
|
356
814
|
}.freeze
|
|
357
815
|
|
|
816
|
+
# The words C builds its integer types from. They may come in any
|
|
817
|
+
# order and `int` may be left out -- `unsigned`, `long unsigned int`
|
|
818
|
+
# and `int unsigned` are all C -- while Fiddle reads only some of those
|
|
819
|
+
# orders, fails on a bare `unsigned`, and reads `int unsigned` as a
|
|
820
|
+
# signed int without saying so. So a spelling made only of these is
|
|
821
|
+
# counted here and handed to Fiddle in the one order it reads.
|
|
822
|
+
INTEGER_WORDS = %w[signed unsigned char short int long].freeze
|
|
823
|
+
|
|
358
824
|
# The CArray data type a pointer parameter takes, by the code Fiddle
|
|
359
825
|
# gave its element. This is the table CGenerator::STORAGE_C_TYPES
|
|
360
826
|
# already holds, read the other way round -- nothing new is decided
|
|
361
827
|
# here about how a C type and a CArray type correspond.
|
|
362
828
|
DATA_TYPES = {
|
|
829
|
+
:double_complex => :cmplx128,
|
|
830
|
+
:float_complex => :cmplx64,
|
|
363
831
|
Fiddle::TYPE_DOUBLE => :float64,
|
|
364
832
|
Fiddle::TYPE_FLOAT => :float32,
|
|
365
833
|
Fiddle::TYPE_CHAR => :int8,
|
|
@@ -380,6 +848,11 @@ class CArray
|
|
|
380
848
|
# Absent means the type may be written down but holds no value a body
|
|
381
849
|
# can compute with -- `void`, and a pointer to it.
|
|
382
850
|
COMPUTATION = {
|
|
851
|
+
# A `float _Complex` computes in double complex, as a `float`
|
|
852
|
+
# computes in double: the declaration says what the signature is,
|
|
853
|
+
# and the body works in what Ruby would have worked in.
|
|
854
|
+
:double_complex => :complex,
|
|
855
|
+
:float_complex => :complex,
|
|
383
856
|
Fiddle::TYPE_DOUBLE => :double,
|
|
384
857
|
Fiddle::TYPE_FLOAT => :double,
|
|
385
858
|
Fiddle::TYPE_CHAR => :int64,
|
|
@@ -440,9 +913,40 @@ class CArray
|
|
|
440
913
|
text.split(",").map(&:strip)
|
|
441
914
|
end
|
|
442
915
|
|
|
916
|
+
# The names a declaration gave its parameters, in order, with `nil`
|
|
917
|
+
# where it gave none.
|
|
918
|
+
#
|
|
919
|
+
# `read_type` drops them, and for every other spelling that is right:
|
|
920
|
+
# nothing reads a parameter's name for a function bound from a library,
|
|
921
|
+
# and one compiled from a block takes its parameters from the block.
|
|
922
|
+
# `jit_call` is where they stop being decorative -- there the names are
|
|
923
|
+
# what binds the call to the locals around it -- so they are read here
|
|
924
|
+
# rather than kept on a type, where a name does not belong.
|
|
925
|
+
def parameter_names (prototype)
|
|
926
|
+
text = prototype.strip.sub(/;\z/, "")
|
|
927
|
+
_, _, parameter_text = split(text, prototype)
|
|
928
|
+
parts = split_parameters(parameter_text)
|
|
929
|
+
return [] if parts.size == 1 && parts.first.strip == "void"
|
|
930
|
+
parts.map { |part| parameter_name(part) }
|
|
931
|
+
end
|
|
932
|
+
|
|
933
|
+
# The same grammar `read_type` reads, answering the name it throws
|
|
934
|
+
# away. Written out beside it rather than folded into it: `read_type`
|
|
935
|
+
# is asked about a return type too, and a return type has no name.
|
|
936
|
+
def parameter_name (text)
|
|
937
|
+
stripped = text.strip.sub(/\[\s*\d*\s*\]\s*\z/, "")
|
|
938
|
+
words = stripped.split(/\s+|(?=\*)|(?<=\*)/).reject(&:empty?)
|
|
939
|
+
words.shift while words.first && KEYWORDS.include?(words.first)
|
|
940
|
+
words.shift while words.first == "*"
|
|
941
|
+
last = words.first
|
|
942
|
+
# A Symbol, which is what a block's parameters are and what the rest
|
|
943
|
+
# of the compiler compares names as.
|
|
944
|
+
last.to_sym if last&.match?(/\A[A-Za-z_]\w*\z/)
|
|
945
|
+
end
|
|
946
|
+
|
|
443
947
|
# `[const] <keywords> [*] [name] [[]]` is the whole grammar there is.
|
|
444
|
-
# A parameter's own name is dropped
|
|
445
|
-
#
|
|
948
|
+
# A parameter's own name is dropped here; `parameter_names` above reads
|
|
949
|
+
# it for the one spelling that needs it.
|
|
446
950
|
def read_type (text, prototype)
|
|
447
951
|
array = nil
|
|
448
952
|
stripped = text.strip.sub(/\[\s*(\d*)\s*\]\s*\z/) {
|
|
@@ -473,6 +977,7 @@ class CArray
|
|
|
473
977
|
if (reason = REFUSED[spelling])
|
|
474
978
|
raise Unsupported, "#{reason} (in `#{prototype}`)"
|
|
475
979
|
end
|
|
980
|
+
spelling = integer_spelling(spelling, text, prototype)
|
|
476
981
|
# A parameter written as an array is a pointer at the ABI, whatever
|
|
477
982
|
# its declarator says.
|
|
478
983
|
pointer ||= !array.nil?
|
|
@@ -484,12 +989,17 @@ class CArray
|
|
|
484
989
|
else
|
|
485
990
|
""
|
|
486
991
|
end
|
|
487
|
-
code =
|
|
992
|
+
code = if !pointer && (complex = complex_code(spelling))
|
|
993
|
+
complex
|
|
994
|
+
else
|
|
995
|
+
fiddle_code(pointer ? "void *" : spelling, text, prototype)
|
|
996
|
+
end
|
|
488
997
|
# What it points at, for a pointer that points at numbers. `void *`
|
|
489
998
|
# has no element, which is what keeps it a slot.
|
|
490
999
|
element = nil
|
|
491
1000
|
if pointer && spelling != "void"
|
|
492
|
-
element_code =
|
|
1001
|
+
element_code = complex_code(spelling) ||
|
|
1002
|
+
fiddle_code(spelling, text, prototype)
|
|
493
1003
|
if COMPUTATION[element_code]
|
|
494
1004
|
element = CType.new(spelling, element_code,
|
|
495
1005
|
COMPUTATION[element_code], false, nil, nil,
|
|
@@ -500,6 +1010,42 @@ class CArray
|
|
|
500
1010
|
array, element, keywords.include?("const"))
|
|
501
1011
|
end
|
|
502
1012
|
|
|
1013
|
+
# `long unsigned int` as `unsigned long`, `unsigned` as `unsigned int`:
|
|
1014
|
+
# the order Fiddle reads, for a spelling made only of INTEGER_WORDS.
|
|
1015
|
+
# Anything else is handed back untouched. A combination C does not
|
|
1016
|
+
# have -- `signed unsigned`, `short long`, three `long`s -- is refused
|
|
1017
|
+
# here rather than left to a parser that has been seen to guess.
|
|
1018
|
+
def integer_spelling (spelling, text, prototype)
|
|
1019
|
+
words = spelling.split(/\s+/)
|
|
1020
|
+
return spelling unless words.all? { |word| INTEGER_WORDS.include?(word) }
|
|
1021
|
+
count = words.tally
|
|
1022
|
+
longs = count.fetch("long", 0)
|
|
1023
|
+
base = count.keys & %w[char short]
|
|
1024
|
+
if count.values_at("signed", "unsigned", "char", "short", "int")
|
|
1025
|
+
.any? { |n| n.to_i > 1 } ||
|
|
1026
|
+
(count.key?("signed") && count.key?("unsigned")) ||
|
|
1027
|
+
base.size > 1 || (base.any? && longs > 0) || longs > 2 ||
|
|
1028
|
+
(count.key?("char") && count.key?("int"))
|
|
1029
|
+
raise Unsupported,
|
|
1030
|
+
"`#{text.strip}` in `#{prototype}` is not a C integer type"
|
|
1031
|
+
end
|
|
1032
|
+
sign = count.key?("unsigned") ? "unsigned " : ""
|
|
1033
|
+
width = base.first || { 0 => "int", 1 => "long", 2 => "long long" }.fetch(longs)
|
|
1034
|
+
# `signed char` is a type of its own in C, apart from plain `char`,
|
|
1035
|
+
# and Fiddle reads both as the one byte they are.
|
|
1036
|
+
sign = "signed " if width == "char" && count.key?("signed")
|
|
1037
|
+
"#{sign}#{width}"
|
|
1038
|
+
end
|
|
1039
|
+
|
|
1040
|
+
# The code for a complex spelling, or nil for anything else. `float
|
|
1041
|
+
# _Complex`, `complex float`, `float complex` -- the word may sit on
|
|
1042
|
+
# either side, as C allows.
|
|
1043
|
+
def complex_code (spelling)
|
|
1044
|
+
words = spelling.split(/\s+/)
|
|
1045
|
+
return nil unless (words & COMPLEX_WORDS).any?
|
|
1046
|
+
COMPLEX_CODES[(words - COMPLEX_WORDS).join(" ")]
|
|
1047
|
+
end
|
|
1048
|
+
|
|
503
1049
|
def fiddle_code (spelling, text, prototype)
|
|
504
1050
|
PARSER.parse_ctype(spelling)
|
|
505
1051
|
rescue StandardError
|
|
@@ -560,7 +1106,77 @@ class CArray
|
|
|
560
1106
|
# What comes back is the same object `jit_extern` hands out, so a kernel
|
|
561
1107
|
# calls either without knowing which it has; `compiled?` is where the
|
|
562
1108
|
# difference is still visible, along with the block, which it keeps.
|
|
563
|
-
|
|
1109
|
+
# `CArray.jit_call`: compile the block, read the locals the declaration
|
|
1110
|
+
# named, call.
|
|
1111
|
+
#
|
|
1112
|
+
# Kept per call site rather than per body. A block literal is a fresh
|
|
1113
|
+
# Proc every time the line runs, but its instruction sequence is the
|
|
1114
|
+
# site's and does not change -- which is the key `read_block` already
|
|
1115
|
+
# caches a block's source under.
|
|
1116
|
+
def call_here (prototype, block)
|
|
1117
|
+
site = RubyVM::InstructionSequence.of(block) if
|
|
1118
|
+
defined?(RubyVM::InstructionSequence)
|
|
1119
|
+
entry = site && call_sites[site]
|
|
1120
|
+
unless entry
|
|
1121
|
+
names = CDeclaration.parameter_names(prototype)
|
|
1122
|
+
if (missing = names.index(nil))
|
|
1123
|
+
raise Unsupported,
|
|
1124
|
+
"`#{prototype}` gives parameter #{missing + 1} no name, " \
|
|
1125
|
+
"and a name is what `jit_call` binds by -- it is the local " \
|
|
1126
|
+
"the call reads"
|
|
1127
|
+
end
|
|
1128
|
+
# A compiled function may already exist somewhere other than this
|
|
1129
|
+
# compiler's cache: built ahead of the program and shipped in a
|
|
1130
|
+
# shared object, which is what carray-jit-aot does with these same call
|
|
1131
|
+
# sites. A provider is asked before anything is compiled and
|
|
1132
|
+
# answers nil for a site that is not its own -- the same position
|
|
1133
|
+
# `jit_extern` puts a function from a library in, said about a call
|
|
1134
|
+
# rather than about a name.
|
|
1135
|
+
compiled = call_provider &&
|
|
1136
|
+
call_provider.call(prototype, block, names)
|
|
1137
|
+
unless compiled
|
|
1138
|
+
# Nothing answered, so this one is compiled -- and the compiler
|
|
1139
|
+
# is loaded here rather than beside the method, so a program
|
|
1140
|
+
# whose sites were all answered never loads it. `require` is
|
|
1141
|
+
# idempotent and this runs once per site.
|
|
1142
|
+
require "carray/jit"
|
|
1143
|
+
compiled = function(prototype, declared_parameters: names, &block)
|
|
1144
|
+
end
|
|
1145
|
+
entry = [compiled, names]
|
|
1146
|
+
call_sites[site] = entry if site
|
|
1147
|
+
end
|
|
1148
|
+
compiled, names = entry
|
|
1149
|
+
scope = block.binding
|
|
1150
|
+
compiled.call(*names.map { |local|
|
|
1151
|
+
begin
|
|
1152
|
+
scope.local_variable_get(local)
|
|
1153
|
+
rescue NameError
|
|
1154
|
+
raise Unsupported,
|
|
1155
|
+
"`#{prototype}` declares `#{local}`, and there is no local " \
|
|
1156
|
+
"by that name where the call stands. A declaration's " \
|
|
1157
|
+
"parameter names are what `jit_call` binds to"
|
|
1158
|
+
end
|
|
1159
|
+
})
|
|
1160
|
+
end
|
|
1161
|
+
|
|
1162
|
+
def call_sites
|
|
1163
|
+
@call_sites ||= {}.compare_by_identity
|
|
1164
|
+
end
|
|
1165
|
+
|
|
1166
|
+
# Asked at a `jit_call` site before anything is compiled, and answering
|
|
1167
|
+
# nil means "not mine, compile it". What it is handed is the
|
|
1168
|
+
# prototype, the block -- whose binding says which method and which
|
|
1169
|
+
# module the site is in -- and the names the declaration gave. What it
|
|
1170
|
+
# answers is anything that responds to `call`.
|
|
1171
|
+
#
|
|
1172
|
+
# There is one client and it is carray-jit-aot, which builds these same
|
|
1173
|
+
# sites into a shared object ahead of the program; a machine running
|
|
1174
|
+
# that gem then reaches no compiler. Left here rather than patched in
|
|
1175
|
+
# from there, because a gem reaching into another's method to change
|
|
1176
|
+
# what it does is the arrangement that breaks quietly.
|
|
1177
|
+
attr_accessor :call_provider
|
|
1178
|
+
|
|
1179
|
+
def function (prototype, declared_parameters: nil, &block)
|
|
564
1180
|
unless block
|
|
565
1181
|
raise Unsupported,
|
|
566
1182
|
"a function is compiled from a block, and none was given -- " \
|
|
@@ -568,11 +1184,131 @@ class CArray
|
|
|
568
1184
|
"`CArray.jit_extern(#{prototype.inspect})`"
|
|
569
1185
|
end
|
|
570
1186
|
name, return_type, parameters = CDeclaration.parse(prototype)
|
|
571
|
-
compile_c_function(prototype, name, return_type, parameters, block
|
|
1187
|
+
compile_c_function(prototype, name, return_type, parameters, block,
|
|
1188
|
+
declared_parameters)
|
|
1189
|
+
end
|
|
1190
|
+
|
|
1191
|
+
# One window over several functions, for a call that is handed more
|
|
1192
|
+
# than one address:
|
|
1193
|
+
#
|
|
1194
|
+
# CArray::JIT.watching(objective, gradient) do
|
|
1195
|
+
# NLopt.optimize(option, x, minimum)
|
|
1196
|
+
# end
|
|
1197
|
+
#
|
|
1198
|
+
# Every flag is put down before the block, and every one is read after
|
|
1199
|
+
# it -- because a flag nobody reads is a failure that becomes a wrong
|
|
1200
|
+
# answer, and then surfaces on an unrelated later window. Whichever
|
|
1201
|
+
# failure comes first in the argument list is the one raised, and a
|
|
1202
|
+
# failure raised this way outranks whatever the block raised, for the
|
|
1203
|
+
# reason `CFunction#watching` gives: the library is complaining about
|
|
1204
|
+
# a stand-in, and the stand-in is the consequence.
|
|
1205
|
+
#
|
|
1206
|
+
# `on_error:` is the `[hook, data]` pair `CFunction#on_error` takes,
|
|
1207
|
+
# set on every function for the length of the window and taken off
|
|
1208
|
+
# again afterwards -- with whatever hook was there before put back.
|
|
1209
|
+
# That is the difference from calling `#on_error` around the block:
|
|
1210
|
+
# the pair can be read back here and cannot be read back from Ruby, so
|
|
1211
|
+
# a window that brings its own hook is the only way to borrow one
|
|
1212
|
+
# without leaving the owner's gone.
|
|
1213
|
+
#
|
|
1214
|
+
# `stand_in:` is the number a failed call answers C with, as
|
|
1215
|
+
# `CFunction#watching` takes it, and is set on every function watched.
|
|
1216
|
+
# Which number is right belongs to the window and not to the body: the
|
|
1217
|
+
# same `int (*)(...)` may be handed to a routine that stops on a
|
|
1218
|
+
# non-zero status and to one that reads it as a rejected step and tries
|
|
1219
|
+
# again, and the body has no way of knowing which it was given to.
|
|
1220
|
+
#
|
|
1221
|
+
# `nil` in the list is skipped, so an optional gradient or jacobian may
|
|
1222
|
+
# be passed as it stands, and a function named twice is watched once.
|
|
1223
|
+
# A body that cannot fail has no flag, no hook and no stand-in, and
|
|
1224
|
+
# passing one changes nothing -- which is what lets a binding watch
|
|
1225
|
+
# what it was given without asking which kind of body it is.
|
|
1226
|
+
def watching (*functions, on_error: nil, stand_in: nil)
|
|
1227
|
+
armed = functions.compact
|
|
1228
|
+
armed.each do |function|
|
|
1229
|
+
next if function.is_a?(CFunction)
|
|
1230
|
+
raise TypeError,
|
|
1231
|
+
"`CArray::JIT.watching` watches compiled functions, and " \
|
|
1232
|
+
"#{function.class} is not one -- pass what `jit_function` " \
|
|
1233
|
+
"or `jit_extern` answered"
|
|
1234
|
+
end
|
|
1235
|
+
armed = armed.uniq
|
|
1236
|
+
hook = window_hook(on_error)
|
|
1237
|
+
standin = window_stand_in(stand_in)
|
|
1238
|
+
return yield if armed.empty?
|
|
1239
|
+
opened = armed.map { |function|
|
|
1240
|
+
function.send(:open_window, hook, standin)
|
|
1241
|
+
}
|
|
1242
|
+
codes = []
|
|
1243
|
+
begin
|
|
1244
|
+
result = yield
|
|
1245
|
+
rescue StandardError
|
|
1246
|
+
# Closed here rather than in the ensure below, so that the failure
|
|
1247
|
+
# raised next is raised from inside this rescue and carries the
|
|
1248
|
+
# library's own complaint as its cause.
|
|
1249
|
+
codes = close_windows(armed, opened)
|
|
1250
|
+
opened = nil
|
|
1251
|
+
raise_first_failure(armed, codes)
|
|
1252
|
+
raise
|
|
1253
|
+
ensure
|
|
1254
|
+
codes = close_windows(armed, opened) if opened
|
|
1255
|
+
end
|
|
1256
|
+
raise_first_failure(armed, codes)
|
|
1257
|
+
result
|
|
572
1258
|
end
|
|
573
1259
|
|
|
574
1260
|
private
|
|
575
1261
|
|
|
1262
|
+
# Every window closed, in the order they were opened, and what each of
|
|
1263
|
+
# them saw. All of them, whatever happened in the block: a flag nobody
|
|
1264
|
+
# reads is one that surfaces on an unrelated later window.
|
|
1265
|
+
def close_windows (armed, opened)
|
|
1266
|
+
armed.zip(opened).map { |function, state|
|
|
1267
|
+
function.send(:close_window, state)
|
|
1268
|
+
}
|
|
1269
|
+
end
|
|
1270
|
+
|
|
1271
|
+
# The first failure among the flags just read, raised as the function
|
|
1272
|
+
# that reported it would raise it. A code of 0 is no failure and is
|
|
1273
|
+
# passed over, so this walks the list in the order it was written and
|
|
1274
|
+
# stops at the first that has something to say.
|
|
1275
|
+
def raise_first_failure (armed, codes)
|
|
1276
|
+
armed.zip(codes).each do |function, code|
|
|
1277
|
+
function.send(:raise_for, code)
|
|
1278
|
+
end
|
|
1279
|
+
end
|
|
1280
|
+
|
|
1281
|
+
# The number a window hands the C as a failed call's answer. An
|
|
1282
|
+
# Integer because that is what the object carries it in -- one 64-bit
|
|
1283
|
+
# word, cast at the return -- and because a status is a whole number
|
|
1284
|
+
# wherever one is read.
|
|
1285
|
+
def window_stand_in (stand_in)
|
|
1286
|
+
return nil if stand_in.nil?
|
|
1287
|
+
unless stand_in.is_a?(Integer)
|
|
1288
|
+
raise TypeError,
|
|
1289
|
+
"`stand_in:` is the number a failed call answers C with, " \
|
|
1290
|
+
"and #{stand_in.class} is not one -- it is cast to whatever " \
|
|
1291
|
+
"the body returns, so what is given here is an Integer"
|
|
1292
|
+
end
|
|
1293
|
+
unless (-(2**63)...2**63).cover?(stand_in)
|
|
1294
|
+
raise RangeError,
|
|
1295
|
+
"`stand_in:` is carried in one 64-bit word, and " \
|
|
1296
|
+
"#{stand_in} does not fit in one"
|
|
1297
|
+
end
|
|
1298
|
+
stand_in
|
|
1299
|
+
end
|
|
1300
|
+
|
|
1301
|
+
def window_hook (on_error)
|
|
1302
|
+
return nil if on_error.nil?
|
|
1303
|
+
unless on_error.is_a?(Array) && (1..2).cover?(on_error.size)
|
|
1304
|
+
raise ArgumentError,
|
|
1305
|
+
"`on_error:` takes the hook and the one pointer it is " \
|
|
1306
|
+
"handed, as `CFunction#on_error` does -- " \
|
|
1307
|
+
"`on_error: [handle[\"nlopt_force_stop\"], option]`"
|
|
1308
|
+
end
|
|
1309
|
+
[on_error[0], on_error[1]]
|
|
1310
|
+
end
|
|
1311
|
+
|
|
576
1312
|
def bind_c_function (prototype, name, return_type, parameters, from)
|
|
577
1313
|
handle = library_handle(from)
|
|
578
1314
|
begin
|
|
@@ -592,28 +1328,80 @@ class CArray
|
|
|
592
1328
|
# see one name for all of them. So an anonymous one carries its own
|
|
593
1329
|
# digest, and a named one carries the name it was given.
|
|
594
1330
|
#
|
|
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_"
|
|
1331
|
+
# Always behind `PREFIX`, though, for the reason stated where it is
|
|
1332
|
+
# defined.
|
|
603
1333
|
|
|
604
1334
|
def function_symbol (name, key)
|
|
605
1335
|
digest = Digest::SHA256.hexdigest(key.inspect)[0, 12]
|
|
606
1336
|
"#{PREFIX}#{name || "function"}_#{digest}"
|
|
607
1337
|
end
|
|
608
1338
|
|
|
609
|
-
def compile_c_function (prototype, name, return_type, parameters, block
|
|
1339
|
+
def compile_c_function (prototype, name, return_type, parameters, block,
|
|
1340
|
+
declared_parameters = nil)
|
|
610
1341
|
node, source, origin = read_block(block)
|
|
611
|
-
|
|
1342
|
+
# A function this body calls is pasted into it, so which one it is
|
|
1343
|
+
# belongs in the key beside the body's own text. Two blocks spelled
|
|
1344
|
+
# the same that reach different functions are different functions,
|
|
1345
|
+
# and without this the first compiled would be handed back for the
|
|
1346
|
+
# second -- quietly, since nothing about them differs to look at.
|
|
1347
|
+
# `kernel_key` is what a kernel already keys a pasted body by: the
|
|
1348
|
+
# signature and the symbol, which carries the digest of the body. A
|
|
1349
|
+
# symbol rather than an address, so the key means the same thing in
|
|
1350
|
+
# the next process as in this one.
|
|
1351
|
+
called = called_functions(source, node, block, name)
|
|
1352
|
+
key = [source, return_type.text, parameters.map(&:text), name,
|
|
1353
|
+
declared_parameters, called.values.map(&:kernel_key)]
|
|
612
1354
|
found = function_registry[key]
|
|
613
1355
|
return found if found
|
|
614
1356
|
function_registry[key] =
|
|
615
1357
|
build_c_function(prototype, name, return_type, parameters,
|
|
616
|
-
source, node, origin, block, function_symbol(name, key)
|
|
1358
|
+
source, node, origin, block, function_symbol(name, key),
|
|
1359
|
+
called, declared_parameters)
|
|
1360
|
+
end
|
|
1361
|
+
|
|
1362
|
+
# The compiled functions the block reaches for, by the name it reaches
|
|
1363
|
+
# them by. Only these: everything else it closes over is refused, and
|
|
1364
|
+
# `refuse_captures` is where that is said. This runs before the
|
|
1365
|
+
# registry is consulted, because the key cannot be built without it.
|
|
1366
|
+
def called_functions (source, node, block, own_name)
|
|
1367
|
+
names = capture_names(source, node) - block.parameters.map(&:last)
|
|
1368
|
+
names -= [own_name.to_sym] if own_name
|
|
1369
|
+
binding = binding_of(block)
|
|
1370
|
+
names.each_with_object({}) do |captured, found|
|
|
1371
|
+
value = captured_value(captured, binding)
|
|
1372
|
+
# A function compiled here is pasted into this one; one bound from
|
|
1373
|
+
# a library is declared and called by its name, which is what it
|
|
1374
|
+
# has and what a linker resolves. Both are functions this body
|
|
1375
|
+
# calls, so both are here.
|
|
1376
|
+
found[captured] = value if value.is_a?(CFunction)
|
|
1377
|
+
end
|
|
1378
|
+
end
|
|
1379
|
+
|
|
1380
|
+
# What a name held where the block was written, or nil for one that
|
|
1381
|
+
# held nothing. A constant is looked up as well as a local, because a
|
|
1382
|
+
# method body closes over nothing: a `def` that compiles a function
|
|
1383
|
+
# reaches the one it calls by a constant or not at all.
|
|
1384
|
+
def captured_value (name, binding)
|
|
1385
|
+
if name.to_s.start_with?(/[A-Z]/)
|
|
1386
|
+
binding.eval(name.to_s)
|
|
1387
|
+
elsif binding.local_variable_defined?(name)
|
|
1388
|
+
binding.local_variable_get(name)
|
|
1389
|
+
end
|
|
1390
|
+
rescue NameError
|
|
1391
|
+
nil
|
|
1392
|
+
end
|
|
1393
|
+
|
|
1394
|
+
# Whether the name held anything at all, which `captured_value` cannot
|
|
1395
|
+
# say: a name holding nil and a name that is not there both come back
|
|
1396
|
+
# as nil, and only one of them is worth a different message.
|
|
1397
|
+
def captured_name_defined? (name, binding)
|
|
1398
|
+
if name.to_s.start_with?(/[A-Z]/)
|
|
1399
|
+
binding.eval("defined?(#{name})") ? true : false
|
|
1400
|
+
else
|
|
1401
|
+
binding.local_variable_defined?(name)
|
|
1402
|
+
end
|
|
1403
|
+
rescue NameError
|
|
1404
|
+
false
|
|
617
1405
|
end
|
|
618
1406
|
|
|
619
1407
|
def function_registry
|
|
@@ -621,19 +1409,24 @@ class CArray
|
|
|
621
1409
|
end
|
|
622
1410
|
|
|
623
1411
|
def build_c_function (prototype, name, return_type, parameters,
|
|
624
|
-
source, node, origin, block, symbol
|
|
1412
|
+
source, node, origin, block, symbol, called = {},
|
|
1413
|
+
declared_parameters = nil)
|
|
625
1414
|
# A function is a function of its parameters. Whatever else the block
|
|
626
1415
|
# reaches for is refused, and the reason differs by what it is -- so
|
|
627
1416
|
# the captures are looked at before the body is walked, or the body
|
|
628
1417
|
# would raise first and say something less useful.
|
|
629
|
-
|
|
1418
|
+
# A body written for `jit_call` takes no parameters -- the
|
|
1419
|
+
# declaration named them, and the same names are the locals the call
|
|
1420
|
+
# reads -- so the arity is the declaration's to state.
|
|
1421
|
+
names = declared_parameters || block.parameters.map(&:last)
|
|
630
1422
|
unless names.size == parameters.size
|
|
631
1423
|
raise Unsupported,
|
|
632
1424
|
"`#{prototype}` names #{parameters.size} " \
|
|
633
1425
|
"#{parameters.size == 1 ? 'parameter' : 'parameters'}, and " \
|
|
634
1426
|
"the block takes #{names.size}"
|
|
635
1427
|
end
|
|
636
|
-
refuse_captures(source, node, block, names
|
|
1428
|
+
refuse_captures(source, node, block, names + called.keys,
|
|
1429
|
+
name && name.to_sym)
|
|
637
1430
|
|
|
638
1431
|
# `void` is a return type a body may have: a function whose work is
|
|
639
1432
|
# through its pointer parameters has nothing to hand back, and C says
|
|
@@ -659,6 +1452,9 @@ class CArray
|
|
|
659
1452
|
.to_h { |name, type|
|
|
660
1453
|
[name, type.indexable? ? !type.const : nil]
|
|
661
1454
|
}
|
|
1455
|
+
pointer_lengths = names.zip(parameters)
|
|
1456
|
+
.select { |_, type| type.indexable? && type.sized? }
|
|
1457
|
+
.to_h { |name, type| [name, type.array] }
|
|
662
1458
|
pointer_types = names.zip(parameters).select { |_, type| type.indexable? }
|
|
663
1459
|
.to_h { |name, type| [name, type.element.computation] }
|
|
664
1460
|
|
|
@@ -666,8 +1462,11 @@ class CArray
|
|
|
666
1462
|
# own body, as C does, so the body can call itself. An anonymous one
|
|
667
1463
|
# has nothing to call itself by, and gets no recursion.
|
|
668
1464
|
analyzer = Analyzer.new(source, node: node, function: true,
|
|
1465
|
+
declared_parameters: declared_parameters,
|
|
669
1466
|
returns: !returns_nothing,
|
|
670
1467
|
pointers: pointers,
|
|
1468
|
+
pointer_lengths: pointer_lengths,
|
|
1469
|
+
c_functions: called,
|
|
671
1470
|
recursion: (name && [name.to_sym, parameters,
|
|
672
1471
|
return_type.computation]))
|
|
673
1472
|
names = analyzer.parameter_names
|
|
@@ -682,45 +1481,59 @@ class CArray
|
|
|
682
1481
|
raise Unsupported,
|
|
683
1482
|
(if type.indexable?
|
|
684
1483
|
"`#{loose}` is declared `#{type.text}`, which is a pointer; " \
|
|
685
|
-
"index it, as in `#{loose}[0]
|
|
1484
|
+
"index it, as in `#{loose}[0]`, or ask whether one came, " \
|
|
1485
|
+
"as in `if #{loose}` or `#{loose}.nil?`"
|
|
686
1486
|
else
|
|
687
1487
|
"`#{loose}` is declared `#{type.text}`, which is a slot in " \
|
|
688
|
-
"the signature rather than a value; the body cannot read
|
|
1488
|
+
"the signature rather than a value; the body cannot read " \
|
|
1489
|
+
"it, only ask whether it was filled, as in `#{loose}.nil?`"
|
|
689
1490
|
end)
|
|
690
1491
|
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
|
|
1492
|
+
# A parameter of any computation type the body can work in, uint64
|
|
1493
|
+
# included. A kernel's captures have three buses to travel in and
|
|
1494
|
+
# uint64 is not one of them, but these are not captures: they are the
|
|
1495
|
+
# parameters of this function's own C signature, and arrive in the
|
|
1496
|
+
# type the declaration named. So `size_t n` is a value the body can
|
|
1497
|
+
# be handed, which is the point -- it is how the C that counts bytes
|
|
1498
|
+
# is spelled.
|
|
707
1499
|
types = names.zip(parameters).reject { |_, type| type.pointer }
|
|
708
1500
|
.to_h { |parameter, type| [parameter, type.computation] }
|
|
709
1501
|
|
|
710
|
-
assignment = TypeAssignment.new(analyzer.body, {}, {},
|
|
1502
|
+
assignment = TypeAssignment.new(analyzer.body, {}, {}, called,
|
|
711
1503
|
scalar_types: types,
|
|
712
1504
|
pointer_types: pointer_types)
|
|
713
1505
|
generator = CGenerator.new(analyzer, {}, assignment.scalar_types,
|
|
714
|
-
|
|
1506
|
+
c_functions: called,
|
|
1507
|
+
origin: origin, block_source: source,
|
|
1508
|
+
scalar_parameters: types.keys)
|
|
715
1509
|
c_source = generator.generate_function(
|
|
716
1510
|
symbol, names, parameters, return_type.text, return_type.computation)
|
|
1511
|
+
# A signature carrying a complex by value gets a second entry point
|
|
1512
|
+
# for Ruby to reach it by, since Fiddle has no such type to call
|
|
1513
|
+
# with. Only such a signature: everything else is called directly,
|
|
1514
|
+
# and pays nothing for a road it does not take.
|
|
1515
|
+
shim_symbol = nil
|
|
1516
|
+
if return_type.complex? || parameters.any?(&:complex?)
|
|
1517
|
+
shim_symbol = "#{symbol}_from_ruby"
|
|
1518
|
+
c_source += shim_source(shim_symbol, symbol, names, parameters,
|
|
1519
|
+
return_type)
|
|
1520
|
+
end
|
|
717
1521
|
handle, = Compiler.build(c_source, symbol, header: generator.provenance)
|
|
718
1522
|
# Where the body can divide by zero or reach outside an array, the
|
|
719
1523
|
# object carries a place to say so. Reading it is what lets a call
|
|
720
1524
|
# from Ruby raise what the same expression raises in Ruby.
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
1525
|
+
#
|
|
1526
|
+
# The place is the calling thread's, and a thread-local is not a name
|
|
1527
|
+
# the loader can hand an address for, so what is taken here is the
|
|
1528
|
+
# accessor beside it -- called once per thread, from that thread. It
|
|
1529
|
+
# is named after the body, the four it answers for being `static`, so
|
|
1530
|
+
# that two objects generated this way can be linked side by side.
|
|
1531
|
+
error_state = if generator.uses_error_flag?
|
|
1532
|
+
accessor = CGenerator.error_state_name(symbol)
|
|
1533
|
+
Fiddle::Function.new(handle[accessor],
|
|
1534
|
+
[Fiddle::TYPE_VOIDP],
|
|
1535
|
+
Fiddle::TYPE_VOID, name: accessor)
|
|
1536
|
+
end
|
|
724
1537
|
# A body that reports failures is generated a second time for pasting,
|
|
725
1538
|
# with the flag as a parameter. A second generator rather than the
|
|
726
1539
|
# same one twice: what it emitted is what it holds, and the two forms
|
|
@@ -728,20 +1541,68 @@ class CArray
|
|
|
728
1541
|
pasted = generator
|
|
729
1542
|
if generator.uses_error_flag?
|
|
730
1543
|
pasted = CGenerator.new(analyzer, {}, assignment.scalar_types,
|
|
731
|
-
|
|
1544
|
+
c_functions: called,
|
|
1545
|
+
origin: origin, block_source: source,
|
|
1546
|
+
scalar_parameters: types.keys)
|
|
732
1547
|
pasted.generate_function(symbol, names, parameters, return_type.text,
|
|
733
1548
|
return_type.computation,
|
|
734
1549
|
error_parameter: true)
|
|
735
1550
|
end
|
|
736
|
-
CFunction.new(
|
|
1551
|
+
CFunction.new(name, prototype, return_type, parameters, handle[symbol],
|
|
1552
|
+
symbol: symbol,
|
|
1553
|
+
shim: shim_symbol && handle[shim_symbol],
|
|
737
1554
|
block: block, c_source: generator.provenance + c_source,
|
|
738
|
-
origin: origin,
|
|
1555
|
+
origin: origin, error_state: error_state,
|
|
739
1556
|
definition: pasted.function_definition,
|
|
740
1557
|
helpers: pasted.helper_needs,
|
|
1558
|
+
dependencies: called.values,
|
|
741
1559
|
takes_error: generator.uses_error_flag?,
|
|
742
1560
|
raise_messages: generator.raise_messages)
|
|
743
1561
|
end
|
|
744
1562
|
|
|
1563
|
+
# The entry point a call from Ruby takes where the signature carries a
|
|
1564
|
+
# complex by value. Each complex argument arrives as the two doubles
|
|
1565
|
+
# C99 lays one out as, and a complex result is written back the same
|
|
1566
|
+
# way; every other parameter keeps its own type, so a pointer is still
|
|
1567
|
+
# a pointer and an integer is still that integer.
|
|
1568
|
+
#
|
|
1569
|
+
# It calls the function rather than repeating it, so there is one body
|
|
1570
|
+
# and both roads reach it.
|
|
1571
|
+
def shim_source (shim_symbol, symbol, names, parameters, return_type)
|
|
1572
|
+
declarations = names.zip(parameters).map { |name, type|
|
|
1573
|
+
type.complex? ? "const double *#{name}" : type.declare(name)
|
|
1574
|
+
}
|
|
1575
|
+
passed = names.zip(parameters).map { |name, type|
|
|
1576
|
+
next name unless type.complex?
|
|
1577
|
+
"#{type.complex_build}(#{name}[0], #{name}[1])"
|
|
1578
|
+
}
|
|
1579
|
+
call = "#{symbol}(#{passed.join(', ')})"
|
|
1580
|
+
if return_type.complex?
|
|
1581
|
+
declarations << "double *carray_jit_result"
|
|
1582
|
+
body = <<~C
|
|
1583
|
+
#{return_type.text} carray_jit_value = #{call};
|
|
1584
|
+
carray_jit_result[0] = creal(carray_jit_value);
|
|
1585
|
+
carray_jit_result[1] = cimag(carray_jit_value);
|
|
1586
|
+
C
|
|
1587
|
+
head = "void"
|
|
1588
|
+
else
|
|
1589
|
+
body = "#{return_type.computation ? 'return ' : ''}#{call};\n"
|
|
1590
|
+
head = return_type.text
|
|
1591
|
+
end
|
|
1592
|
+
<<~C
|
|
1593
|
+
|
|
1594
|
+
/* Fiddle has no type for a C99 complex, so a call from Ruby comes
|
|
1595
|
+
through here: the complex arguments arrive as the two doubles
|
|
1596
|
+
one is laid out as, and a complex result goes back the same way.
|
|
1597
|
+
A kernel calls the function above directly, C to C. */
|
|
1598
|
+
#{head}
|
|
1599
|
+
#{shim_symbol} (#{declarations.join(', ')})
|
|
1600
|
+
{
|
|
1601
|
+
#{body.strip}
|
|
1602
|
+
}
|
|
1603
|
+
C
|
|
1604
|
+
end
|
|
1605
|
+
|
|
745
1606
|
# Nothing outside the parameter list may be reached. A number could in
|
|
746
1607
|
# principle be written into the C as a literal, and another c_function's
|
|
747
1608
|
# address as a constant -- but both would put something in the compiled
|
|
@@ -766,16 +1627,30 @@ class CArray
|
|
|
766
1627
|
captured -= [own_name] if own_name
|
|
767
1628
|
return if captured.empty?
|
|
768
1629
|
name = captured.first
|
|
769
|
-
value =
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
1630
|
+
value = captured_value(name, binding_of(block))
|
|
1631
|
+
# A CFunction never reaches here: `called_functions` took both
|
|
1632
|
+
# kinds above -- compiled ones to paste, borrowed ones to declare and
|
|
1633
|
+
# call by name -- so neither is a capture by the time this runs.
|
|
1634
|
+
if value.is_a?(CArray::Rng)
|
|
1635
|
+
# A generator has state, and a compiled function has nowhere to
|
|
1636
|
+
# keep one -- the same reason a borrowed address is refused above.
|
|
1637
|
+
# A kernel is handed its operands at every call and can be given
|
|
1638
|
+
# the state among them; a function's arguments are the ones its
|
|
1639
|
+
# declaration named, and adding one behind the caller's back would
|
|
1640
|
+
# change the C signature it was promised. So the state is named,
|
|
1641
|
+
# and the body draws from it the way the generator's own C does.
|
|
1642
|
+
raise Unsupported,
|
|
1643
|
+
"this function reaches `#{name}`, which is a generator; a " \
|
|
1644
|
+
"compiled function has nowhere to keep its state. Take the " \
|
|
1645
|
+
"state as a parameter -- declare `int64_t state[4]` and pass " \
|
|
1646
|
+
"`#{name}.state` -- or draw in the kernel that calls this, " \
|
|
1647
|
+
"where `random(rng: #{name})` works"
|
|
1648
|
+
end
|
|
1649
|
+
unless captured_name_defined?(name, binding_of(block))
|
|
1650
|
+
raise Unsupported,
|
|
1651
|
+
"`#{name}` is not defined where the block was written"
|
|
1652
|
+
end
|
|
1653
|
+
kind = value.is_a?(CArray) ? "an array" : "a value"
|
|
779
1654
|
raise Unsupported,
|
|
780
1655
|
"this function reaches `#{name}`, which is #{kind} outside it; " \
|
|
781
1656
|
"a compiled function takes everything it needs through its " \
|