carray-jit 0.1.6 → 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 +22 -0
- data/README.md +1 -1
- data/carray-jit.gemspec +4 -2
- data/docs/02_KernelShapes.md +2 -2
- data/docs/03_SupportedFeatures.md +1 -1
- data/docs/04_Compiling.md +5 -5
- data/lib/carray/jit/analyzer.rb +1 -1
- data/lib/carray/jit/c_function.rb +133 -139
- data/lib/carray/jit/c_generator.rb +19 -10
- data/lib/carray/jit/compiler.rb +44 -26
- data/lib/carray/jit/expression.rb +68 -28
- 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,
|
|
@@ -70,12 +69,12 @@ class CArray
|
|
|
70
69
|
|
|
71
70
|
# A C function a kernel body can call, and that C can be handed back.
|
|
72
71
|
#
|
|
73
|
-
# The binding is borrowed from
|
|
74
|
-
#
|
|
75
|
-
#
|
|
76
|
-
#
|
|
77
|
-
#
|
|
78
|
-
#
|
|
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.
|
|
79
78
|
#
|
|
80
79
|
# The address travels to the kernel in a buffer, the way a captured scalar
|
|
81
80
|
# already does, rather than being linked against. That means no `-l`
|
|
@@ -266,9 +265,10 @@ class CArray
|
|
|
266
265
|
@parameters.size
|
|
267
266
|
end
|
|
268
267
|
|
|
269
|
-
# The
|
|
268
|
+
# The type codes of the layer Native stands for, for calling it from
|
|
269
|
+
# Ruby through that layer directly.
|
|
270
270
|
def argument_types
|
|
271
|
-
@parameters.map(
|
|
271
|
+
@parameters.map { |type| Native::TYPES.fetch(type.abi, type.abi) }
|
|
272
272
|
end
|
|
273
273
|
|
|
274
274
|
# The C spelling of the pointer type, for the typedef a kernel emits.
|
|
@@ -312,13 +312,13 @@ class CArray
|
|
|
312
312
|
return call_through_shim(arguments) if @shim
|
|
313
313
|
if carries_a_complex?
|
|
314
314
|
raise Unsupported,
|
|
315
|
-
"`#{self}` carries a C99 complex by value, which
|
|
316
|
-
"no type for, so it cannot be called from
|
|
317
|
-
"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"
|
|
318
318
|
end
|
|
319
|
-
@function ||=
|
|
320
|
-
|
|
321
|
-
|
|
319
|
+
@function ||= Native.function(@pointer, @parameters.map(&:abi),
|
|
320
|
+
@return_type.abi, release_gvl: true,
|
|
321
|
+
name: label.to_s)
|
|
322
322
|
arrays = []
|
|
323
323
|
prepared = arguments.zip(@parameters).map { |argument, type|
|
|
324
324
|
next argument unless type.indexable? && argument.is_a?(CArray)
|
|
@@ -341,7 +341,7 @@ class CArray
|
|
|
341
341
|
slot = -1
|
|
342
342
|
prepared = prepared.map { |value|
|
|
343
343
|
next value unless arrays.any? { |_, buffer, _| buffer.equal?(value) }
|
|
344
|
-
|
|
344
|
+
bases[slot += 1][:pointer]
|
|
345
345
|
}
|
|
346
346
|
@result = @function.call(*prepared)
|
|
347
347
|
end
|
|
@@ -366,10 +366,10 @@ class CArray
|
|
|
366
366
|
@return_type.complex? || @parameters.any?(&:complex?)
|
|
367
367
|
end
|
|
368
368
|
|
|
369
|
-
#
|
|
370
|
-
# a pair of doubles and writes a complex result back the
|
|
371
|
-
# Everything else keeps the type the declaration gave it and
|
|
372
|
-
#
|
|
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
|
|
373
373
|
# still a CArray on this side.
|
|
374
374
|
#
|
|
375
375
|
# It is the same compiled body either way: the shim calls the function,
|
|
@@ -377,13 +377,13 @@ class CArray
|
|
|
377
377
|
def call_through_shim (arguments)
|
|
378
378
|
returns_complex = @return_type.complex?
|
|
379
379
|
types = @parameters.map { |type|
|
|
380
|
-
type.complex? ?
|
|
380
|
+
type.complex? ? :pointer : type.abi
|
|
381
381
|
}
|
|
382
|
-
types <<
|
|
382
|
+
types << :pointer if returns_complex
|
|
383
383
|
@shim_function ||=
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
384
|
+
Native.function(@shim, types,
|
|
385
|
+
returns_complex ? :void : @return_type.abi,
|
|
386
|
+
release_gvl: true)
|
|
387
387
|
result = returns_complex ? String.new("\0" * 16) : nil
|
|
388
388
|
arrays = []
|
|
389
389
|
prepared = arguments.zip(@parameters).map { |argument, type|
|
|
@@ -403,7 +403,7 @@ class CArray
|
|
|
403
403
|
slot = -1
|
|
404
404
|
passed = prepared.map { |value|
|
|
405
405
|
next value unless arrays.any? { |_, buffer, _| buffer.equal?(value) }
|
|
406
|
-
|
|
406
|
+
bases[slot += 1][:pointer]
|
|
407
407
|
}
|
|
408
408
|
passed << result if returns_complex
|
|
409
409
|
@result = @shim_function.call(*passed)
|
|
@@ -529,8 +529,8 @@ class CArray
|
|
|
529
529
|
# f.on_error(nlopt["nlopt_force_stop"], opt)
|
|
530
530
|
# f.watching { nlopt_optimize.call(opt, x, minf) }
|
|
531
531
|
#
|
|
532
|
-
# `hook` is the address of a `void (*)(void *)` --
|
|
533
|
-
#
|
|
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
|
|
534
534
|
# argument it is called with. It is called once, from the call whose
|
|
535
535
|
# body put the flag up, and not again until the flag has been put down
|
|
536
536
|
# and has gone up again; nothing about it reaches Ruby, so it may be
|
|
@@ -552,18 +552,17 @@ class CArray
|
|
|
552
552
|
state = error_state
|
|
553
553
|
return self unless state
|
|
554
554
|
address = hook.nil? ? 0 : hook.to_i
|
|
555
|
-
state.data[0
|
|
556
|
-
|
|
557
|
-
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"))
|
|
558
557
|
state.held = hook.nil? ? nil : [hook, data]
|
|
559
558
|
self
|
|
560
559
|
end
|
|
561
560
|
|
|
562
561
|
private
|
|
563
562
|
|
|
564
|
-
#
|
|
565
|
-
# flag, the hook's address, the pointer it is handed, and
|
|
566
|
-
# 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
|
|
567
566
|
# held beside them, so that nothing the C may still call is collected.
|
|
568
567
|
ErrorState = Struct.new(:flag, :hook, :data, :standin, :held)
|
|
569
568
|
private_constant :ErrorState
|
|
@@ -593,25 +592,21 @@ class CArray
|
|
|
593
592
|
end
|
|
594
593
|
|
|
595
594
|
def resolve_error_state
|
|
596
|
-
out = String.new("\0" * (4 *
|
|
595
|
+
out = String.new("\0" * (4 * Native::POINTER_SIZE))
|
|
597
596
|
@error_state.call(out)
|
|
598
597
|
flag, hook, data, standin = out.unpack("J4")
|
|
599
|
-
ErrorState.new(
|
|
600
|
-
Fiddle::Pointer.new(hook, Fiddle::SIZEOF_VOIDP),
|
|
601
|
-
Fiddle::Pointer.new(data, Fiddle::SIZEOF_VOIDP),
|
|
602
|
-
Fiddle::Pointer.new(standin, 8),
|
|
603
|
-
nil)
|
|
598
|
+
ErrorState.new(flag, hook, data, standin, nil)
|
|
604
599
|
end
|
|
605
600
|
|
|
606
601
|
# 0 where the body cannot fail at all.
|
|
607
602
|
def error_code
|
|
608
603
|
state = error_state
|
|
609
|
-
state ? state.flag
|
|
604
|
+
state ? Native.read(state.flag, 4).unpack1("l") : 0
|
|
610
605
|
end
|
|
611
606
|
|
|
612
607
|
def write_error (code)
|
|
613
608
|
state = error_state
|
|
614
|
-
state.flag
|
|
609
|
+
Native.write(state.flag, [code].pack("l")) if state
|
|
615
610
|
end
|
|
616
611
|
|
|
617
612
|
# The hook's address, cleared for the length of one call from Ruby,
|
|
@@ -619,14 +614,14 @@ class CArray
|
|
|
619
614
|
def disarm_hook
|
|
620
615
|
state = error_state
|
|
621
616
|
return nil unless state
|
|
622
|
-
held = state.hook
|
|
623
|
-
state.hook
|
|
617
|
+
held = Native.read(state.hook, Native::POINTER_SIZE)
|
|
618
|
+
Native.write(state.hook, "\0" * Native::POINTER_SIZE)
|
|
624
619
|
held
|
|
625
620
|
end
|
|
626
621
|
|
|
627
622
|
def rearm_hook (held)
|
|
628
623
|
state = error_state
|
|
629
|
-
state.hook
|
|
624
|
+
Native.write(state.hook, held) if state && held
|
|
630
625
|
end
|
|
631
626
|
|
|
632
627
|
# What a window does at its two ends. `#watching` is these with a
|
|
@@ -662,8 +657,8 @@ class CArray
|
|
|
662
657
|
def hold_hook (address, data)
|
|
663
658
|
state = error_state
|
|
664
659
|
return nil unless state
|
|
665
|
-
held = [state.hook
|
|
666
|
-
state.data
|
|
660
|
+
held = [Native.read(state.hook, Native::POINTER_SIZE),
|
|
661
|
+
Native.read(state.data, Native::POINTER_SIZE), state.held]
|
|
667
662
|
on_error(address, data)
|
|
668
663
|
held
|
|
669
664
|
end
|
|
@@ -671,8 +666,8 @@ class CArray
|
|
|
671
666
|
def restore_hook (held)
|
|
672
667
|
state = error_state
|
|
673
668
|
return unless state && held
|
|
674
|
-
state.hook
|
|
675
|
-
state.data
|
|
669
|
+
Native.write(state.hook, held[0])
|
|
670
|
+
Native.write(state.data, held[1])
|
|
676
671
|
state.held = held[2]
|
|
677
672
|
end
|
|
678
673
|
|
|
@@ -682,14 +677,14 @@ class CArray
|
|
|
682
677
|
def hold_standin (value)
|
|
683
678
|
state = error_state
|
|
684
679
|
return nil unless state
|
|
685
|
-
held = state.standin
|
|
686
|
-
state.standin
|
|
680
|
+
held = Native.read(state.standin, 8)
|
|
681
|
+
Native.write(state.standin, [value].pack("q"))
|
|
687
682
|
held
|
|
688
683
|
end
|
|
689
684
|
|
|
690
685
|
def restore_standin (held)
|
|
691
686
|
state = error_state
|
|
692
|
-
state.standin
|
|
687
|
+
Native.write(state.standin, held) if state && held
|
|
693
688
|
end
|
|
694
689
|
|
|
695
690
|
def raise_for (code)
|
|
@@ -725,7 +720,7 @@ class CArray
|
|
|
725
720
|
# rule is that an unsized pointer is the caller's responsibility, and
|
|
726
721
|
# writing `coef[3]` is how the caller asks to be checked.
|
|
727
722
|
def check_array (array, type)
|
|
728
|
-
wanted = CDeclaration::DATA_TYPES.fetch(type.element.
|
|
723
|
+
wanted = CDeclaration::DATA_TYPES.fetch(type.element.abi)
|
|
729
724
|
unless array.data_type_name == wanted.to_s
|
|
730
725
|
raise Unsupported,
|
|
731
726
|
"`#{type.text}` takes a #{wanted} array, and this one is " \
|
|
@@ -773,19 +768,16 @@ class CArray
|
|
|
773
768
|
|
|
774
769
|
# Reads the subset of C declarations a signature may be written in.
|
|
775
770
|
#
|
|
776
|
-
#
|
|
777
|
-
#
|
|
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
|
|
778
773
|
# parameter is a pointer, and what it was a pointer to. Between them a
|
|
779
774
|
# prototype yields both what is needed to call a function and what is
|
|
780
775
|
# needed to write one.
|
|
781
776
|
# @private
|
|
782
777
|
module CDeclaration
|
|
783
778
|
|
|
784
|
-
PARSER = ::Object.new.extend(Fiddle::CParser)
|
|
785
|
-
private_constant :PARSER
|
|
786
|
-
|
|
787
779
|
# The words a type may be spelled with. `<stdint.h>`'s exact-width
|
|
788
|
-
# names are here because
|
|
780
|
+
# names are here because C has them; there is no `float64_t`
|
|
789
781
|
# because C has no such type -- its integer types have exact-width
|
|
790
782
|
# aliases and its floating types are `float` and `double`.
|
|
791
783
|
KEYWORDS = %w[
|
|
@@ -796,11 +788,10 @@ class CArray
|
|
|
796
788
|
size_t ssize_t ptrdiff_t intptr_t uintptr_t
|
|
797
789
|
].freeze
|
|
798
790
|
|
|
799
|
-
#
|
|
800
|
-
#
|
|
801
|
-
#
|
|
802
|
-
#
|
|
803
|
-
# 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.
|
|
804
795
|
COMPLEX_CODES = { "double" => :double_complex,
|
|
805
796
|
"float" => :float_complex }.freeze
|
|
806
797
|
|
|
@@ -809,12 +800,13 @@ class CArray
|
|
|
809
800
|
# about what it declared.
|
|
810
801
|
COMPLEX_WORDS = %w[_Complex complex].freeze
|
|
811
802
|
|
|
812
|
-
#
|
|
813
|
-
# 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.
|
|
814
806
|
REFUSED = {
|
|
815
|
-
"long double" => "`long double` is not a type this reads:
|
|
816
|
-
"
|
|
817
|
-
"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",
|
|
818
810
|
"long double _Complex" =>
|
|
819
811
|
"`long double _Complex` is not a type this reads: there is no " \
|
|
820
812
|
"long double here to make one of",
|
|
@@ -822,58 +814,59 @@ class CArray
|
|
|
822
814
|
|
|
823
815
|
# The words C builds its integer types from. They may come in any
|
|
824
816
|
# order and `int` may be left out -- `unsigned`, `long unsigned int`
|
|
825
|
-
# and `int unsigned` are all C -- while
|
|
826
|
-
# orders, fails on a bare `unsigned`, and reads `int
|
|
827
|
-
# signed int without saying so. So a spelling made only
|
|
828
|
-
# 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.
|
|
829
822
|
INTEGER_WORDS = %w[signed unsigned char short int long].freeze
|
|
830
823
|
|
|
831
|
-
# The CArray data type a pointer parameter takes, by the
|
|
824
|
+
# The CArray data type a pointer parameter takes, by the type Native
|
|
832
825
|
# gave its element. This is the table CGenerator::STORAGE_C_TYPES
|
|
833
826
|
# already holds, read the other way round -- nothing new is decided
|
|
834
827
|
# here about how a C type and a CArray type correspond.
|
|
835
828
|
DATA_TYPES = {
|
|
836
|
-
:double_complex
|
|
837
|
-
:float_complex
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
# `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
|
|
849
842
|
# `uint64_t` as the other one.
|
|
850
|
-
|
|
851
|
-
|
|
843
|
+
:ulong => :uint64,
|
|
844
|
+
:ulong_long => :uint64,
|
|
852
845
|
}.freeze
|
|
853
846
|
|
|
854
|
-
#
|
|
847
|
+
# Types Native may answer, mapped to what a kernel computes them in.
|
|
855
848
|
# Absent means the type may be written down but holds no value a body
|
|
856
849
|
# can compute with -- `void`, and a pointer to it.
|
|
857
850
|
COMPUTATION = {
|
|
858
851
|
# A `float _Complex` computes in double complex, as a `float`
|
|
859
852
|
# computes in double: the declaration says what the signature is,
|
|
860
853
|
# and the body works in what Ruby would have worked in.
|
|
861
|
-
:double_complex
|
|
862
|
-
:float_complex
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
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,
|
|
873
866
|
# uint64 is a computation type of its own, precisely because int64
|
|
874
867
|
# cannot carry what it holds; CArray has the array to match.
|
|
875
|
-
|
|
876
|
-
|
|
868
|
+
:ulong => :uint64,
|
|
869
|
+
:ulong_long => :uint64,
|
|
877
870
|
}.freeze
|
|
878
871
|
|
|
879
872
|
module_function
|
|
@@ -999,14 +992,14 @@ class CArray
|
|
|
999
992
|
code = if !pointer && (complex = complex_code(spelling))
|
|
1000
993
|
complex
|
|
1001
994
|
else
|
|
1002
|
-
|
|
995
|
+
abi_code(pointer ? "void *" : spelling, text, prototype)
|
|
1003
996
|
end
|
|
1004
997
|
# What it points at, for a pointer that points at numbers. `void *`
|
|
1005
998
|
# has no element, which is what keeps it a slot.
|
|
1006
999
|
element = nil
|
|
1007
1000
|
if pointer && spelling != "void"
|
|
1008
1001
|
element_code = complex_code(spelling) ||
|
|
1009
|
-
|
|
1002
|
+
abi_code(spelling, text, prototype)
|
|
1010
1003
|
if COMPUTATION[element_code]
|
|
1011
1004
|
element = CType.new(spelling, element_code,
|
|
1012
1005
|
COMPUTATION[element_code], false, nil, nil,
|
|
@@ -1018,7 +1011,7 @@ class CArray
|
|
|
1018
1011
|
end
|
|
1019
1012
|
|
|
1020
1013
|
# `long unsigned int` as `unsigned long`, `unsigned` as `unsigned int`:
|
|
1021
|
-
# the order
|
|
1014
|
+
# the order Native reads, for a spelling made only of INTEGER_WORDS.
|
|
1022
1015
|
# Anything else is handed back untouched. A combination C does not
|
|
1023
1016
|
# have -- `signed unsigned`, `short long`, three `long`s -- is refused
|
|
1024
1017
|
# here rather than left to a parser that has been seen to guess.
|
|
@@ -1039,7 +1032,7 @@ class CArray
|
|
|
1039
1032
|
sign = count.key?("unsigned") ? "unsigned " : ""
|
|
1040
1033
|
width = base.first || { 0 => "int", 1 => "long", 2 => "long long" }.fetch(longs)
|
|
1041
1034
|
# `signed char` is a type of its own in C, apart from plain `char`,
|
|
1042
|
-
# and
|
|
1035
|
+
# and Native reads both as the one byte they are.
|
|
1043
1036
|
sign = "signed " if width == "char" && count.key?("signed")
|
|
1044
1037
|
"#{sign}#{width}"
|
|
1045
1038
|
end
|
|
@@ -1053,11 +1046,11 @@ class CArray
|
|
|
1053
1046
|
COMPLEX_CODES[(words - COMPLEX_WORDS).join(" ")]
|
|
1054
1047
|
end
|
|
1055
1048
|
|
|
1056
|
-
def
|
|
1057
|
-
|
|
1058
|
-
|
|
1049
|
+
def abi_code (spelling, text, prototype)
|
|
1050
|
+
type = Native.c_type(spelling)
|
|
1051
|
+
return type if type
|
|
1059
1052
|
raise Unsupported,
|
|
1060
|
-
"`#{text.strip}` in `#{prototype}` is not a type
|
|
1053
|
+
"`#{text.strip}` in `#{prototype}` is not a type this knows. " \
|
|
1061
1054
|
"C's own spellings are what this takes, and `float64_t` is not " \
|
|
1062
1055
|
"one of them -- C's integer types have exact-width names, its " \
|
|
1063
1056
|
"floating types are `float` and `double`"
|
|
@@ -1076,7 +1069,7 @@ class CArray
|
|
|
1076
1069
|
# or nothing at all, which searches what the process has already loaded.
|
|
1077
1070
|
#
|
|
1078
1071
|
# `extern` is C's own word for a body that lives elsewhere, and that is
|
|
1079
|
-
# all this does: it asks
|
|
1072
|
+
# all this does: it asks Native where the function is. Nothing is
|
|
1080
1073
|
# compiled here, which is the difference from `jit_function` and the
|
|
1081
1074
|
# reason the two are not one method with a branch in it.
|
|
1082
1075
|
#
|
|
@@ -1319,8 +1312,8 @@ class CArray
|
|
|
1319
1312
|
def bind_c_function (prototype, name, return_type, parameters, from)
|
|
1320
1313
|
handle = library_handle(from)
|
|
1321
1314
|
begin
|
|
1322
|
-
pointer = handle
|
|
1323
|
-
rescue
|
|
1315
|
+
pointer = Native.address(handle, name.to_s)
|
|
1316
|
+
rescue Native::LinkError => error
|
|
1324
1317
|
raise Unsupported,
|
|
1325
1318
|
"`#{name}` was not found in " \
|
|
1326
1319
|
"#{from ? from.inspect : "the loaded libraries"} " \
|
|
@@ -1444,7 +1437,7 @@ class CArray
|
|
|
1444
1437
|
# Every other return type with no computation behind it is a pointer,
|
|
1445
1438
|
# which a body cannot produce: there is nothing here to take an
|
|
1446
1439
|
# address of that would outlive the call.
|
|
1447
|
-
returns_nothing = return_type.
|
|
1440
|
+
returns_nothing = return_type.abi == :void &&
|
|
1448
1441
|
!return_type.pointer
|
|
1449
1442
|
unless return_type.computation || returns_nothing
|
|
1450
1443
|
raise Unsupported,
|
|
@@ -1516,8 +1509,8 @@ class CArray
|
|
|
1516
1509
|
c_source = generator.generate_function(
|
|
1517
1510
|
symbol, names, parameters, return_type.text, return_type.computation)
|
|
1518
1511
|
# A signature carrying a complex by value gets a second entry point
|
|
1519
|
-
# for Ruby to reach it by, since
|
|
1520
|
-
# 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,
|
|
1521
1514
|
# and pays nothing for a road it does not take.
|
|
1522
1515
|
shim_symbol = nil
|
|
1523
1516
|
if return_type.complex? || parameters.any?(&:complex?)
|
|
@@ -1537,9 +1530,9 @@ class CArray
|
|
|
1537
1530
|
# that two objects generated this way can be linked side by side.
|
|
1538
1531
|
error_state = if generator.uses_error_flag?
|
|
1539
1532
|
accessor = CGenerator.error_state_name(symbol)
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1533
|
+
Native.function(Native.address(handle, accessor),
|
|
1534
|
+
[:pointer], :void, release_gvl: true,
|
|
1535
|
+
name: accessor)
|
|
1543
1536
|
end
|
|
1544
1537
|
# A body that reports failures is generated a second time for pasting,
|
|
1545
1538
|
# with the flag as a parameter. A second generator rather than the
|
|
@@ -1555,9 +1548,10 @@ class CArray
|
|
|
1555
1548
|
return_type.computation,
|
|
1556
1549
|
error_parameter: true)
|
|
1557
1550
|
end
|
|
1558
|
-
CFunction.new(name, prototype, return_type, parameters,
|
|
1551
|
+
CFunction.new(name, prototype, return_type, parameters,
|
|
1552
|
+
Native.address(handle, symbol),
|
|
1559
1553
|
symbol: symbol,
|
|
1560
|
-
shim: shim_symbol && handle
|
|
1554
|
+
shim: shim_symbol && Native.address(handle, shim_symbol),
|
|
1561
1555
|
block: block, c_source: generator.provenance + c_source,
|
|
1562
1556
|
origin: origin, error_state: error_state,
|
|
1563
1557
|
definition: pasted.function_definition,
|
|
@@ -1598,7 +1592,7 @@ class CArray
|
|
|
1598
1592
|
end
|
|
1599
1593
|
<<~C
|
|
1600
1594
|
|
|
1601
|
-
/*
|
|
1595
|
+
/* A call from Ruby has no type for a C99 complex, so it comes
|
|
1602
1596
|
through here: the complex arguments arrive as the two doubles
|
|
1603
1597
|
one is laid out as, and a complex result goes back the same way.
|
|
1604
1598
|
A kernel calls the function above directly, C to C. */
|
|
@@ -1666,12 +1660,12 @@ class CArray
|
|
|
1666
1660
|
|
|
1667
1661
|
def library_handle (from)
|
|
1668
1662
|
case from
|
|
1669
|
-
when nil
|
|
1670
|
-
when
|
|
1671
|
-
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)
|
|
1672
1666
|
else
|
|
1673
1667
|
raise Unsupported,
|
|
1674
|
-
"`from:` takes a library name or a
|
|
1668
|
+
"`from:` takes a library name or a #{Native::Library}, " \
|
|
1675
1669
|
"got #{from.class}"
|
|
1676
1670
|
end
|
|
1677
1671
|
end
|
|
@@ -9,8 +9,8 @@ class CArray
|
|
|
9
9
|
# names the block used, and casts only where a type actually changes.
|
|
10
10
|
# `CARRAY_JIT_DUMP=1` prints it.
|
|
11
11
|
#
|
|
12
|
-
# Every kernel has the same C signature, so one
|
|
13
|
-
#
|
|
12
|
+
# Every kernel has the same C signature, so one function shape serves
|
|
13
|
+
# all of them; the per-kernel detail is unpacked into named locals
|
|
14
14
|
# at the top of the body, where it also reads better.
|
|
15
15
|
class CGenerator
|
|
16
16
|
|
|
@@ -1777,6 +1777,14 @@ class CArray
|
|
|
1777
1777
|
# Each axis carries a start, a limit and a step. The step is a compiled
|
|
1778
1778
|
# constant when it is one, because `i++` is what lets the loop vectorise
|
|
1779
1779
|
# and that is the case nearly every kernel is in.
|
|
1780
|
+
#
|
|
1781
|
+
# The limit and a step that is not a constant are read into locals
|
|
1782
|
+
# before the loop, not out of `bounds` at each test. `bounds` is an
|
|
1783
|
+
# `int64_t *`, and so is an int64 array the body writes: the compiler
|
|
1784
|
+
# cannot tell that a store into the array leaves the limit alone, reads
|
|
1785
|
+
# it again after every cell, and having no trip count does not vectorise
|
|
1786
|
+
# the loop at all. A kernel writing a float64 or int32 array never met
|
|
1787
|
+
# this, which is why it went unseen; test_vectorization.rb keeps it seen.
|
|
1780
1788
|
def loop_open
|
|
1781
1789
|
@rank.times.map { |axis|
|
|
1782
1790
|
indent = " " + " " * axis
|
|
@@ -1788,17 +1796,18 @@ class CArray
|
|
|
1788
1796
|
# way to report does not pay for the test at all.
|
|
1789
1797
|
guard = @body_reports ? "#{indent} if ( *error ) break;\n" : ""
|
|
1790
1798
|
start = "bounds[#{3 * axis}]"
|
|
1791
|
-
limit = "
|
|
1799
|
+
limit = "#{index}__limit"
|
|
1792
1800
|
step = @steps[axis]
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
"#{advance}) {\n#{guard}"
|
|
1801
|
+
locals = "#{index} = #{start}, #{limit} = bounds[#{3 * axis + 1}]"
|
|
1802
|
+
if step && step.abs == 1
|
|
1803
|
+
advance = step == -1 ? "#{index}--" : "#{index}++"
|
|
1797
1804
|
else
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
"#{advance}) {\n#{guard}"
|
|
1805
|
+
locals += ", #{index}__step = bounds[#{3 * axis + 2}]"
|
|
1806
|
+
advance = "#{index} += #{index}__step"
|
|
1801
1807
|
end
|
|
1808
|
+
comparison = ( step and step.negative? ) ? ">" : "<"
|
|
1809
|
+
"#{indent}for (int64_t #{locals}; #{index} #{comparison} #{limit}; " \
|
|
1810
|
+
"#{advance}) {\n#{guard}"
|
|
1802
1811
|
}.join
|
|
1803
1812
|
end
|
|
1804
1813
|
|