carray-jit 0.1.5 → 0.1.7
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 +51 -0
- data/README.md +2 -2
- data/carray-jit.gemspec +4 -2
- data/docs/02_KernelShapes.md +2 -2
- data/docs/03_SupportedFeatures.md +2 -2
- data/docs/04_Compiling.md +5 -5
- data/docs/08_LanguageReference.md +2 -2
- data/lib/carray/jit/analyzer.rb +1 -1
- data/lib/carray/jit/c_function.rb +140 -139
- data/lib/carray/jit/c_generator.rb +123 -24
- data/lib/carray/jit/compiler.rb +44 -26
- data/lib/carray/jit/expression.rb +79 -29
- data/lib/carray/jit/kernel.rb +23 -47
- data/lib/carray/jit/native.rb +149 -0
- data/lib/carray/jit/sweep.rb +14 -64
- data/lib/carray/jit/version.rb +1 -1
- data/lib/carray/jit.rb +96 -19
- metadata +7 -6
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
require "digest"
|
|
2
|
-
require "
|
|
3
|
-
require "fiddle/import"
|
|
2
|
+
require "carray/jit/native"
|
|
4
3
|
|
|
5
4
|
class CArray
|
|
6
5
|
module JIT
|
|
7
6
|
|
|
8
7
|
# One parameter or return type, as the prototype spelled it.
|
|
9
8
|
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
# are one thing to it. That is enough to *call* a function and
|
|
13
|
-
# to *write* one, which is why the declarator's own text is
|
|
14
|
-
# the
|
|
15
|
-
CType = Struct.new(:text, :
|
|
9
|
+
# The type a call is made with answers what the ABI needs and no more:
|
|
10
|
+
# every pointer is `:pointer`, so `const double *`, `double *` and
|
|
11
|
+
# `void *` are one thing to it. That is enough to *call* a function and
|
|
12
|
+
# not enough to *write* one, which is why the declarator's own text is
|
|
13
|
+
# kept beside the name in Native::TYPES it was given.
|
|
14
|
+
CType = Struct.new(:text, :abi, :computation, :pointer, :array,
|
|
16
15
|
:element, :const) do
|
|
17
16
|
# True for a type that may be written into a signature but holds no
|
|
18
17
|
# value a kernel or a body can compute with.
|
|
@@ -26,16 +25,16 @@ class CArray
|
|
|
26
25
|
pointer && !element.nil?
|
|
27
26
|
end
|
|
28
27
|
|
|
29
|
-
# A C99 complex passed or returned by value.
|
|
30
|
-
#
|
|
28
|
+
# A C99 complex passed or returned by value. A call from Ruby cannot
|
|
29
|
+
# carry one, so it goes through a shim; a call from a kernel is C
|
|
31
30
|
# calling C and needs nothing.
|
|
32
31
|
def complex?
|
|
33
|
-
!pointer && CDeclaration::COMPLEX_CODES.value?(
|
|
32
|
+
!pointer && CDeclaration::COMPLEX_CODES.value?(abi)
|
|
34
33
|
end
|
|
35
34
|
|
|
36
35
|
# `CMPLX` for a double complex, `CMPLXF` for a float one.
|
|
37
36
|
def complex_build
|
|
38
|
-
|
|
37
|
+
abi == :float_complex ? "CMPLXF" : "CMPLX"
|
|
39
38
|
end
|
|
40
39
|
|
|
41
40
|
# C's own declarator puts an array's length where a reader can see it,
|
|
@@ -55,6 +54,13 @@ class CArray
|
|
|
55
54
|
"#{text.sub(/\s*\[.*\]\z/, "")} #{name}[#{array if sized?}]"
|
|
56
55
|
end
|
|
57
56
|
|
|
57
|
+
# The pointer an array argument decays to, as C spells it:
|
|
58
|
+
# `const double coef[3]` takes a `const double *`.
|
|
59
|
+
def decayed
|
|
60
|
+
return text unless array
|
|
61
|
+
"#{text.sub(/\s*\[.*\]\z/, "")} *"
|
|
62
|
+
end
|
|
63
|
+
|
|
58
64
|
# @return [String] the type, as C spells it.
|
|
59
65
|
def to_s
|
|
60
66
|
text
|
|
@@ -63,12 +69,12 @@ class CArray
|
|
|
63
69
|
|
|
64
70
|
# A C function a kernel body can call, and that C can be handed back.
|
|
65
71
|
#
|
|
66
|
-
# The binding is borrowed from
|
|
67
|
-
#
|
|
68
|
-
#
|
|
69
|
-
#
|
|
70
|
-
#
|
|
71
|
-
#
|
|
72
|
+
# The binding is borrowed from Native -- it finds the library and the
|
|
73
|
+
# symbol, and decides what type a C spelling is on this machine. The
|
|
74
|
+
# *call* is not borrowed: reaching a function through a function Native
|
|
75
|
+
# makes costs a few hundred nanoseconds per cell, against single digits
|
|
76
|
+
# from compiled C. Native is asked where the function is; the kernel
|
|
77
|
+
# calls it.
|
|
72
78
|
#
|
|
73
79
|
# The address travels to the kernel in a buffer, the way a captured scalar
|
|
74
80
|
# already does, rather than being linked against. That means no `-l`
|
|
@@ -259,9 +265,10 @@ class CArray
|
|
|
259
265
|
@parameters.size
|
|
260
266
|
end
|
|
261
267
|
|
|
262
|
-
# The
|
|
268
|
+
# The type codes of the layer Native stands for, for calling it from
|
|
269
|
+
# Ruby through that layer directly.
|
|
263
270
|
def argument_types
|
|
264
|
-
@parameters.map(
|
|
271
|
+
@parameters.map { |type| Native::TYPES.fetch(type.abi, type.abi) }
|
|
265
272
|
end
|
|
266
273
|
|
|
267
274
|
# The C spelling of the pointer type, for the typedef a kernel emits.
|
|
@@ -305,13 +312,13 @@ class CArray
|
|
|
305
312
|
return call_through_shim(arguments) if @shim
|
|
306
313
|
if carries_a_complex?
|
|
307
314
|
raise Unsupported,
|
|
308
|
-
"`#{self}` carries a C99 complex by value, which
|
|
309
|
-
"no type for, so it cannot be called from
|
|
310
|
-
"calls it as C calls it"
|
|
315
|
+
"`#{self}` carries a C99 complex by value, which a call " \
|
|
316
|
+
"from Ruby has no type for, so it cannot be called from " \
|
|
317
|
+
"here; a kernel calls it as C calls it"
|
|
311
318
|
end
|
|
312
|
-
@function ||=
|
|
313
|
-
|
|
314
|
-
|
|
319
|
+
@function ||= Native.function(@pointer, @parameters.map(&:abi),
|
|
320
|
+
@return_type.abi, release_gvl: true,
|
|
321
|
+
name: label.to_s)
|
|
315
322
|
arrays = []
|
|
316
323
|
prepared = arguments.zip(@parameters).map { |argument, type|
|
|
317
324
|
next argument unless type.indexable? && argument.is_a?(CArray)
|
|
@@ -334,7 +341,7 @@ class CArray
|
|
|
334
341
|
slot = -1
|
|
335
342
|
prepared = prepared.map { |value|
|
|
336
343
|
next value unless arrays.any? { |_, buffer, _| buffer.equal?(value) }
|
|
337
|
-
|
|
344
|
+
bases[slot += 1][:pointer]
|
|
338
345
|
}
|
|
339
346
|
@result = @function.call(*prepared)
|
|
340
347
|
end
|
|
@@ -359,10 +366,10 @@ class CArray
|
|
|
359
366
|
@return_type.complex? || @parameters.any?(&:complex?)
|
|
360
367
|
end
|
|
361
368
|
|
|
362
|
-
#
|
|
363
|
-
# a pair of doubles and writes a complex result back the
|
|
364
|
-
# Everything else keeps the type the declaration gave it and
|
|
365
|
-
#
|
|
369
|
+
# A call from Ruby carries no complex, so the shim takes each complex
|
|
370
|
+
# argument as a pair of doubles and writes a complex result back the
|
|
371
|
+
# same way. Everything else keeps the type the declaration gave it and
|
|
372
|
+
# is called as before -- including a pointer parameter, which is
|
|
366
373
|
# still a CArray on this side.
|
|
367
374
|
#
|
|
368
375
|
# It is the same compiled body either way: the shim calls the function,
|
|
@@ -370,13 +377,13 @@ class CArray
|
|
|
370
377
|
def call_through_shim (arguments)
|
|
371
378
|
returns_complex = @return_type.complex?
|
|
372
379
|
types = @parameters.map { |type|
|
|
373
|
-
type.complex? ?
|
|
380
|
+
type.complex? ? :pointer : type.abi
|
|
374
381
|
}
|
|
375
|
-
types <<
|
|
382
|
+
types << :pointer if returns_complex
|
|
376
383
|
@shim_function ||=
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
384
|
+
Native.function(@shim, types,
|
|
385
|
+
returns_complex ? :void : @return_type.abi,
|
|
386
|
+
release_gvl: true)
|
|
380
387
|
result = returns_complex ? String.new("\0" * 16) : nil
|
|
381
388
|
arrays = []
|
|
382
389
|
prepared = arguments.zip(@parameters).map { |argument, type|
|
|
@@ -396,7 +403,7 @@ class CArray
|
|
|
396
403
|
slot = -1
|
|
397
404
|
passed = prepared.map { |value|
|
|
398
405
|
next value unless arrays.any? { |_, buffer, _| buffer.equal?(value) }
|
|
399
|
-
|
|
406
|
+
bases[slot += 1][:pointer]
|
|
400
407
|
}
|
|
401
408
|
passed << result if returns_complex
|
|
402
409
|
@result = @shim_function.call(*passed)
|
|
@@ -522,8 +529,8 @@ class CArray
|
|
|
522
529
|
# f.on_error(nlopt["nlopt_force_stop"], opt)
|
|
523
530
|
# f.watching { nlopt_optimize.call(opt, x, minf) }
|
|
524
531
|
#
|
|
525
|
-
# `hook` is the address of a `void (*)(void *)` --
|
|
526
|
-
#
|
|
532
|
+
# `hook` is the address of a `void (*)(void *)` -- an Integer, or
|
|
533
|
+
# anything answering #to_i, as a pointer object does -- and `data` is the one
|
|
527
534
|
# argument it is called with. It is called once, from the call whose
|
|
528
535
|
# body put the flag up, and not again until the flag has been put down
|
|
529
536
|
# and has gone up again; nothing about it reaches Ruby, so it may be
|
|
@@ -545,18 +552,17 @@ class CArray
|
|
|
545
552
|
state = error_state
|
|
546
553
|
return self unless state
|
|
547
554
|
address = hook.nil? ? 0 : hook.to_i
|
|
548
|
-
state.data[0
|
|
549
|
-
|
|
550
|
-
state.hook[0, Fiddle::SIZEOF_VOIDP] = [address].pack("J")
|
|
555
|
+
Native.write(state.data, [data.nil? ? 0 : data.to_i].pack("J"))
|
|
556
|
+
Native.write(state.hook, [address].pack("J"))
|
|
551
557
|
state.held = hook.nil? ? nil : [hook, data]
|
|
552
558
|
self
|
|
553
559
|
end
|
|
554
560
|
|
|
555
561
|
private
|
|
556
562
|
|
|
557
|
-
#
|
|
558
|
-
# flag, the hook's address, the pointer it is handed, and
|
|
559
|
-
# call answers C with -- with the Ruby objects `#on_error` was given
|
|
563
|
+
# Where one thread's copy of the four the compiled object carries
|
|
564
|
+
# stands -- the flag, the hook's address, the pointer it is handed, and
|
|
565
|
+
# what a failed call answers C with -- with the Ruby objects `#on_error` was given
|
|
560
566
|
# held beside them, so that nothing the C may still call is collected.
|
|
561
567
|
ErrorState = Struct.new(:flag, :hook, :data, :standin, :held)
|
|
562
568
|
private_constant :ErrorState
|
|
@@ -586,25 +592,21 @@ class CArray
|
|
|
586
592
|
end
|
|
587
593
|
|
|
588
594
|
def resolve_error_state
|
|
589
|
-
out = String.new("\0" * (4 *
|
|
595
|
+
out = String.new("\0" * (4 * Native::POINTER_SIZE))
|
|
590
596
|
@error_state.call(out)
|
|
591
597
|
flag, hook, data, standin = out.unpack("J4")
|
|
592
|
-
ErrorState.new(
|
|
593
|
-
Fiddle::Pointer.new(hook, Fiddle::SIZEOF_VOIDP),
|
|
594
|
-
Fiddle::Pointer.new(data, Fiddle::SIZEOF_VOIDP),
|
|
595
|
-
Fiddle::Pointer.new(standin, 8),
|
|
596
|
-
nil)
|
|
598
|
+
ErrorState.new(flag, hook, data, standin, nil)
|
|
597
599
|
end
|
|
598
600
|
|
|
599
601
|
# 0 where the body cannot fail at all.
|
|
600
602
|
def error_code
|
|
601
603
|
state = error_state
|
|
602
|
-
state ? state.flag
|
|
604
|
+
state ? Native.read(state.flag, 4).unpack1("l") : 0
|
|
603
605
|
end
|
|
604
606
|
|
|
605
607
|
def write_error (code)
|
|
606
608
|
state = error_state
|
|
607
|
-
state.flag
|
|
609
|
+
Native.write(state.flag, [code].pack("l")) if state
|
|
608
610
|
end
|
|
609
611
|
|
|
610
612
|
# The hook's address, cleared for the length of one call from Ruby,
|
|
@@ -612,14 +614,14 @@ class CArray
|
|
|
612
614
|
def disarm_hook
|
|
613
615
|
state = error_state
|
|
614
616
|
return nil unless state
|
|
615
|
-
held = state.hook
|
|
616
|
-
state.hook
|
|
617
|
+
held = Native.read(state.hook, Native::POINTER_SIZE)
|
|
618
|
+
Native.write(state.hook, "\0" * Native::POINTER_SIZE)
|
|
617
619
|
held
|
|
618
620
|
end
|
|
619
621
|
|
|
620
622
|
def rearm_hook (held)
|
|
621
623
|
state = error_state
|
|
622
|
-
state.hook
|
|
624
|
+
Native.write(state.hook, held) if state && held
|
|
623
625
|
end
|
|
624
626
|
|
|
625
627
|
# What a window does at its two ends. `#watching` is these with a
|
|
@@ -655,8 +657,8 @@ class CArray
|
|
|
655
657
|
def hold_hook (address, data)
|
|
656
658
|
state = error_state
|
|
657
659
|
return nil unless state
|
|
658
|
-
held = [state.hook
|
|
659
|
-
state.data
|
|
660
|
+
held = [Native.read(state.hook, Native::POINTER_SIZE),
|
|
661
|
+
Native.read(state.data, Native::POINTER_SIZE), state.held]
|
|
660
662
|
on_error(address, data)
|
|
661
663
|
held
|
|
662
664
|
end
|
|
@@ -664,8 +666,8 @@ class CArray
|
|
|
664
666
|
def restore_hook (held)
|
|
665
667
|
state = error_state
|
|
666
668
|
return unless state && held
|
|
667
|
-
state.hook
|
|
668
|
-
state.data
|
|
669
|
+
Native.write(state.hook, held[0])
|
|
670
|
+
Native.write(state.data, held[1])
|
|
669
671
|
state.held = held[2]
|
|
670
672
|
end
|
|
671
673
|
|
|
@@ -675,14 +677,14 @@ class CArray
|
|
|
675
677
|
def hold_standin (value)
|
|
676
678
|
state = error_state
|
|
677
679
|
return nil unless state
|
|
678
|
-
held = state.standin
|
|
679
|
-
state.standin
|
|
680
|
+
held = Native.read(state.standin, 8)
|
|
681
|
+
Native.write(state.standin, [value].pack("q"))
|
|
680
682
|
held
|
|
681
683
|
end
|
|
682
684
|
|
|
683
685
|
def restore_standin (held)
|
|
684
686
|
state = error_state
|
|
685
|
-
state.standin
|
|
687
|
+
Native.write(state.standin, held) if state && held
|
|
686
688
|
end
|
|
687
689
|
|
|
688
690
|
def raise_for (code)
|
|
@@ -718,7 +720,7 @@ class CArray
|
|
|
718
720
|
# rule is that an unsized pointer is the caller's responsibility, and
|
|
719
721
|
# writing `coef[3]` is how the caller asks to be checked.
|
|
720
722
|
def check_array (array, type)
|
|
721
|
-
wanted = CDeclaration::DATA_TYPES.fetch(type.element.
|
|
723
|
+
wanted = CDeclaration::DATA_TYPES.fetch(type.element.abi)
|
|
722
724
|
unless array.data_type_name == wanted.to_s
|
|
723
725
|
raise Unsupported,
|
|
724
726
|
"`#{type.text}` takes a #{wanted} array, and this one is " \
|
|
@@ -766,19 +768,16 @@ class CArray
|
|
|
766
768
|
|
|
767
769
|
# Reads the subset of C declarations a signature may be written in.
|
|
768
770
|
#
|
|
769
|
-
#
|
|
770
|
-
#
|
|
771
|
+
# Native is asked what it is good at -- what type a spelling is on this
|
|
772
|
+
# machine -- and this decides what that answer throws away: whether a
|
|
771
773
|
# parameter is a pointer, and what it was a pointer to. Between them a
|
|
772
774
|
# prototype yields both what is needed to call a function and what is
|
|
773
775
|
# needed to write one.
|
|
774
776
|
# @private
|
|
775
777
|
module CDeclaration
|
|
776
778
|
|
|
777
|
-
PARSER = ::Object.new.extend(Fiddle::CParser)
|
|
778
|
-
private_constant :PARSER
|
|
779
|
-
|
|
780
779
|
# The words a type may be spelled with. `<stdint.h>`'s exact-width
|
|
781
|
-
# names are here because
|
|
780
|
+
# names are here because C has them; there is no `float64_t`
|
|
782
781
|
# because C has no such type -- its integer types have exact-width
|
|
783
782
|
# aliases and its floating types are `float` and `double`.
|
|
784
783
|
KEYWORDS = %w[
|
|
@@ -789,11 +788,10 @@ class CArray
|
|
|
789
788
|
size_t ssize_t ptrdiff_t intptr_t uintptr_t
|
|
790
789
|
].freeze
|
|
791
790
|
|
|
792
|
-
#
|
|
793
|
-
#
|
|
794
|
-
#
|
|
795
|
-
#
|
|
796
|
-
# and not a silently wrong width.
|
|
791
|
+
# Native has no type for a C99 complex -- a call from Ruby has nowhere
|
|
792
|
+
# to put one -- so these stand where a name in Native::TYPES stands
|
|
793
|
+
# elsewhere, and one reaching Native by mistake is refused there rather
|
|
794
|
+
# than taken for a wrong width.
|
|
797
795
|
COMPLEX_CODES = { "double" => :double_complex,
|
|
798
796
|
"float" => :float_complex }.freeze
|
|
799
797
|
|
|
@@ -802,12 +800,13 @@ class CArray
|
|
|
802
800
|
# about what it declared.
|
|
803
801
|
COMPLEX_WORDS = %w[_Complex complex].freeze
|
|
804
802
|
|
|
805
|
-
#
|
|
806
|
-
# is refused by name
|
|
803
|
+
# A call from Ruby has no `long double` to make, and the parser behind
|
|
804
|
+
# Native takes it for `long` without saying so, so it is refused by name
|
|
805
|
+
# rather than asked about.
|
|
807
806
|
REFUSED = {
|
|
808
|
-
"long double" => "`long double` is not a type this reads:
|
|
809
|
-
"
|
|
810
|
-
"width without saying so",
|
|
807
|
+
"long double" => "`long double` is not a type this reads: there is " \
|
|
808
|
+
"nothing here to call with one, and taking it for " \
|
|
809
|
+
"`long` would be the wrong width without saying so",
|
|
811
810
|
"long double _Complex" =>
|
|
812
811
|
"`long double _Complex` is not a type this reads: there is no " \
|
|
813
812
|
"long double here to make one of",
|
|
@@ -815,58 +814,59 @@ class CArray
|
|
|
815
814
|
|
|
816
815
|
# The words C builds its integer types from. They may come in any
|
|
817
816
|
# order and `int` may be left out -- `unsigned`, `long unsigned int`
|
|
818
|
-
# and `int unsigned` are all C -- while
|
|
819
|
-
# orders, fails on a bare `unsigned`, and reads `int
|
|
820
|
-
# signed int without saying so. So a spelling made only
|
|
821
|
-
# counted here and handed to
|
|
817
|
+
# and `int unsigned` are all C -- while the parser behind Native reads
|
|
818
|
+
# only some of those orders, fails on a bare `unsigned`, and reads `int
|
|
819
|
+
# unsigned` as a signed int without saying so. So a spelling made only
|
|
820
|
+
# of these is counted here and handed to Native in the one order it
|
|
821
|
+
# reads.
|
|
822
822
|
INTEGER_WORDS = %w[signed unsigned char short int long].freeze
|
|
823
823
|
|
|
824
|
-
# The CArray data type a pointer parameter takes, by the
|
|
824
|
+
# The CArray data type a pointer parameter takes, by the type Native
|
|
825
825
|
# gave its element. This is the table CGenerator::STORAGE_C_TYPES
|
|
826
826
|
# already holds, read the other way round -- nothing new is decided
|
|
827
827
|
# here about how a C type and a CArray type correspond.
|
|
828
828
|
DATA_TYPES = {
|
|
829
|
-
:double_complex
|
|
830
|
-
:float_complex
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
# `unsigned long` and `size_t` arrive as the same
|
|
829
|
+
:double_complex => :cmplx128,
|
|
830
|
+
:float_complex => :cmplx64,
|
|
831
|
+
:double => :float64,
|
|
832
|
+
:float => :float32,
|
|
833
|
+
:char => :int8,
|
|
834
|
+
:uchar => :uint8,
|
|
835
|
+
:short => :int16,
|
|
836
|
+
:ushort => :uint16,
|
|
837
|
+
:int => :int32,
|
|
838
|
+
:uint => :uint32,
|
|
839
|
+
:long => :int64,
|
|
840
|
+
:long_long => :int64,
|
|
841
|
+
# `unsigned long` and `size_t` arrive as the same type, and
|
|
842
842
|
# `uint64_t` as the other one.
|
|
843
|
-
|
|
844
|
-
|
|
843
|
+
:ulong => :uint64,
|
|
844
|
+
:ulong_long => :uint64,
|
|
845
845
|
}.freeze
|
|
846
846
|
|
|
847
|
-
#
|
|
847
|
+
# Types Native may answer, mapped to what a kernel computes them in.
|
|
848
848
|
# Absent means the type may be written down but holds no value a body
|
|
849
849
|
# can compute with -- `void`, and a pointer to it.
|
|
850
850
|
COMPUTATION = {
|
|
851
851
|
# A `float _Complex` computes in double complex, as a `float`
|
|
852
852
|
# computes in double: the declaration says what the signature is,
|
|
853
853
|
# and the body works in what Ruby would have worked in.
|
|
854
|
-
:double_complex
|
|
855
|
-
:float_complex
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
854
|
+
:double_complex => :complex,
|
|
855
|
+
:float_complex => :complex,
|
|
856
|
+
:double => :double,
|
|
857
|
+
:float => :double,
|
|
858
|
+
:char => :int64,
|
|
859
|
+
:uchar => :int64,
|
|
860
|
+
:short => :int64,
|
|
861
|
+
:ushort => :int64,
|
|
862
|
+
:int => :int64,
|
|
863
|
+
:uint => :int64,
|
|
864
|
+
:long => :int64,
|
|
865
|
+
:long_long => :int64,
|
|
866
866
|
# uint64 is a computation type of its own, precisely because int64
|
|
867
867
|
# cannot carry what it holds; CArray has the array to match.
|
|
868
|
-
|
|
869
|
-
|
|
868
|
+
:ulong => :uint64,
|
|
869
|
+
:ulong_long => :uint64,
|
|
870
870
|
}.freeze
|
|
871
871
|
|
|
872
872
|
module_function
|
|
@@ -992,14 +992,14 @@ class CArray
|
|
|
992
992
|
code = if !pointer && (complex = complex_code(spelling))
|
|
993
993
|
complex
|
|
994
994
|
else
|
|
995
|
-
|
|
995
|
+
abi_code(pointer ? "void *" : spelling, text, prototype)
|
|
996
996
|
end
|
|
997
997
|
# What it points at, for a pointer that points at numbers. `void *`
|
|
998
998
|
# has no element, which is what keeps it a slot.
|
|
999
999
|
element = nil
|
|
1000
1000
|
if pointer && spelling != "void"
|
|
1001
1001
|
element_code = complex_code(spelling) ||
|
|
1002
|
-
|
|
1002
|
+
abi_code(spelling, text, prototype)
|
|
1003
1003
|
if COMPUTATION[element_code]
|
|
1004
1004
|
element = CType.new(spelling, element_code,
|
|
1005
1005
|
COMPUTATION[element_code], false, nil, nil,
|
|
@@ -1011,7 +1011,7 @@ class CArray
|
|
|
1011
1011
|
end
|
|
1012
1012
|
|
|
1013
1013
|
# `long unsigned int` as `unsigned long`, `unsigned` as `unsigned int`:
|
|
1014
|
-
# the order
|
|
1014
|
+
# the order Native reads, for a spelling made only of INTEGER_WORDS.
|
|
1015
1015
|
# Anything else is handed back untouched. A combination C does not
|
|
1016
1016
|
# have -- `signed unsigned`, `short long`, three `long`s -- is refused
|
|
1017
1017
|
# here rather than left to a parser that has been seen to guess.
|
|
@@ -1032,7 +1032,7 @@ class CArray
|
|
|
1032
1032
|
sign = count.key?("unsigned") ? "unsigned " : ""
|
|
1033
1033
|
width = base.first || { 0 => "int", 1 => "long", 2 => "long long" }.fetch(longs)
|
|
1034
1034
|
# `signed char` is a type of its own in C, apart from plain `char`,
|
|
1035
|
-
# and
|
|
1035
|
+
# and Native reads both as the one byte they are.
|
|
1036
1036
|
sign = "signed " if width == "char" && count.key?("signed")
|
|
1037
1037
|
"#{sign}#{width}"
|
|
1038
1038
|
end
|
|
@@ -1046,11 +1046,11 @@ class CArray
|
|
|
1046
1046
|
COMPLEX_CODES[(words - COMPLEX_WORDS).join(" ")]
|
|
1047
1047
|
end
|
|
1048
1048
|
|
|
1049
|
-
def
|
|
1050
|
-
|
|
1051
|
-
|
|
1049
|
+
def abi_code (spelling, text, prototype)
|
|
1050
|
+
type = Native.c_type(spelling)
|
|
1051
|
+
return type if type
|
|
1052
1052
|
raise Unsupported,
|
|
1053
|
-
"`#{text.strip}` in `#{prototype}` is not a type
|
|
1053
|
+
"`#{text.strip}` in `#{prototype}` is not a type this knows. " \
|
|
1054
1054
|
"C's own spellings are what this takes, and `float64_t` is not " \
|
|
1055
1055
|
"one of them -- C's integer types have exact-width names, its " \
|
|
1056
1056
|
"floating types are `float` and `double`"
|
|
@@ -1069,7 +1069,7 @@ class CArray
|
|
|
1069
1069
|
# or nothing at all, which searches what the process has already loaded.
|
|
1070
1070
|
#
|
|
1071
1071
|
# `extern` is C's own word for a body that lives elsewhere, and that is
|
|
1072
|
-
# all this does: it asks
|
|
1072
|
+
# all this does: it asks Native where the function is. Nothing is
|
|
1073
1073
|
# compiled here, which is the difference from `jit_function` and the
|
|
1074
1074
|
# reason the two are not one method with a branch in it.
|
|
1075
1075
|
#
|
|
@@ -1312,8 +1312,8 @@ class CArray
|
|
|
1312
1312
|
def bind_c_function (prototype, name, return_type, parameters, from)
|
|
1313
1313
|
handle = library_handle(from)
|
|
1314
1314
|
begin
|
|
1315
|
-
pointer = handle
|
|
1316
|
-
rescue
|
|
1315
|
+
pointer = Native.address(handle, name.to_s)
|
|
1316
|
+
rescue Native::LinkError => error
|
|
1317
1317
|
raise Unsupported,
|
|
1318
1318
|
"`#{name}` was not found in " \
|
|
1319
1319
|
"#{from ? from.inspect : "the loaded libraries"} " \
|
|
@@ -1437,7 +1437,7 @@ class CArray
|
|
|
1437
1437
|
# Every other return type with no computation behind it is a pointer,
|
|
1438
1438
|
# which a body cannot produce: there is nothing here to take an
|
|
1439
1439
|
# address of that would outlive the call.
|
|
1440
|
-
returns_nothing = return_type.
|
|
1440
|
+
returns_nothing = return_type.abi == :void &&
|
|
1441
1441
|
!return_type.pointer
|
|
1442
1442
|
unless return_type.computation || returns_nothing
|
|
1443
1443
|
raise Unsupported,
|
|
@@ -1509,8 +1509,8 @@ class CArray
|
|
|
1509
1509
|
c_source = generator.generate_function(
|
|
1510
1510
|
symbol, names, parameters, return_type.text, return_type.computation)
|
|
1511
1511
|
# A signature carrying a complex by value gets a second entry point
|
|
1512
|
-
# for Ruby to reach it by, since
|
|
1513
|
-
# with. Only such a signature: everything else is called directly,
|
|
1512
|
+
# for Ruby to reach it by, since a call from Ruby has no such type
|
|
1513
|
+
# to call with. Only such a signature: everything else is called directly,
|
|
1514
1514
|
# and pays nothing for a road it does not take.
|
|
1515
1515
|
shim_symbol = nil
|
|
1516
1516
|
if return_type.complex? || parameters.any?(&:complex?)
|
|
@@ -1530,9 +1530,9 @@ class CArray
|
|
|
1530
1530
|
# that two objects generated this way can be linked side by side.
|
|
1531
1531
|
error_state = if generator.uses_error_flag?
|
|
1532
1532
|
accessor = CGenerator.error_state_name(symbol)
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1533
|
+
Native.function(Native.address(handle, accessor),
|
|
1534
|
+
[:pointer], :void, release_gvl: true,
|
|
1535
|
+
name: accessor)
|
|
1536
1536
|
end
|
|
1537
1537
|
# A body that reports failures is generated a second time for pasting,
|
|
1538
1538
|
# with the flag as a parameter. A second generator rather than the
|
|
@@ -1548,9 +1548,10 @@ class CArray
|
|
|
1548
1548
|
return_type.computation,
|
|
1549
1549
|
error_parameter: true)
|
|
1550
1550
|
end
|
|
1551
|
-
CFunction.new(name, prototype, return_type, parameters,
|
|
1551
|
+
CFunction.new(name, prototype, return_type, parameters,
|
|
1552
|
+
Native.address(handle, symbol),
|
|
1552
1553
|
symbol: symbol,
|
|
1553
|
-
shim: shim_symbol && handle
|
|
1554
|
+
shim: shim_symbol && Native.address(handle, shim_symbol),
|
|
1554
1555
|
block: block, c_source: generator.provenance + c_source,
|
|
1555
1556
|
origin: origin, error_state: error_state,
|
|
1556
1557
|
definition: pasted.function_definition,
|
|
@@ -1591,7 +1592,7 @@ class CArray
|
|
|
1591
1592
|
end
|
|
1592
1593
|
<<~C
|
|
1593
1594
|
|
|
1594
|
-
/*
|
|
1595
|
+
/* A call from Ruby has no type for a C99 complex, so it comes
|
|
1595
1596
|
through here: the complex arguments arrive as the two doubles
|
|
1596
1597
|
one is laid out as, and a complex result goes back the same way.
|
|
1597
1598
|
A kernel calls the function above directly, C to C. */
|
|
@@ -1659,12 +1660,12 @@ class CArray
|
|
|
1659
1660
|
|
|
1660
1661
|
def library_handle (from)
|
|
1661
1662
|
case from
|
|
1662
|
-
when nil
|
|
1663
|
-
when
|
|
1664
|
-
when String
|
|
1663
|
+
when nil then Native.loaded_libraries
|
|
1664
|
+
when Native::Library then from
|
|
1665
|
+
when String then Native.open_library(from, global: true)
|
|
1665
1666
|
else
|
|
1666
1667
|
raise Unsupported,
|
|
1667
|
-
"`from:` takes a library name or a
|
|
1668
|
+
"`from:` takes a library name or a #{Native::Library}, " \
|
|
1668
1669
|
"got #{from.class}"
|
|
1669
1670
|
end
|
|
1670
1671
|
end
|