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.
@@ -1,18 +1,17 @@
1
1
  require "digest"
2
- require "fiddle"
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
- # Fiddle's parser answers what the ABI needs and no more: every pointer
11
- # comes back as `TYPE_VOIDP`, so `const double *`, `double *` and `void *`
12
- # are one thing to it. That is enough to *call* a function and not enough
13
- # to *write* one, which is why the declarator's own text is kept beside
14
- # the code Fiddle assigned it.
15
- CType = Struct.new(:text, :fiddle, :computation, :pointer, :array,
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. Fiddle cannot carry one,
30
- # so a call from Ruby goes through a shim; a call from a kernel is C
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?(fiddle)
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
- fiddle == :float_complex ? "CMPLXF" : "CMPLX"
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 Fiddle -- `Fiddle::Handle` finds the
74
- # library and the symbol, and `Fiddle::CParser` decides the ABI code of a
75
- # type, which is the same path `Fiddle::Importer#extern` takes. The
76
- # *call* is not borrowed: reaching a function through `Fiddle::Function`
77
- # costs a few hundred nanoseconds per cell, against single digits from
78
- # compiled C. Fiddle is asked where the function is; the kernel calls it.
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 Fiddle types, for calling it from Ruby.
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(&:fiddle)
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 Fiddle has " \
316
- "no type for, so it cannot be called from Ruby; a kernel " \
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 ||= Fiddle::Function.new(@pointer, argument_types,
320
- @return_type.fiddle,
321
- name: label.to_s)
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
- Fiddle::Pointer.new(bases[slot += 1][:pointer])
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
- # Fiddle carries no complex, so the shim takes each complex argument as
370
- # a pair of doubles and writes a complex result back the same way.
371
- # Everything else keeps the type the declaration gave it and goes
372
- # through Fiddle as before -- including a pointer parameter, which is
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? ? Fiddle::TYPE_VOIDP : type.fiddle
380
+ type.complex? ? :pointer : type.abi
381
381
  }
382
- types << Fiddle::TYPE_VOIDP if returns_complex
382
+ types << :pointer if returns_complex
383
383
  @shim_function ||=
384
- Fiddle::Function.new(@shim, types,
385
- returns_complex ? Fiddle::TYPE_VOID
386
- : @return_type.fiddle)
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
- Fiddle::Pointer.new(bases[slot += 1][:pointer])
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 *)` -- a Fiddle::Pointer,
533
- # an Integer, or anything answering #to_i -- and `data` is the one
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, Fiddle::SIZEOF_VOIDP] =
556
- [data.nil? ? 0 : data.to_i].pack("J")
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
- # One thread's copy of the four the compiled object carries -- the
565
- # flag, the hook's address, the pointer it is handed, and what a failed
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 * Fiddle::SIZEOF_VOIDP))
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(Fiddle::Pointer.new(flag, 4),
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[0, 4].unpack1("l") : 0
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[0, 4] = [code].pack("l") if state
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[0, Fiddle::SIZEOF_VOIDP]
623
- state.hook[0, Fiddle::SIZEOF_VOIDP] = "\0" * Fiddle::SIZEOF_VOIDP
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[0, Fiddle::SIZEOF_VOIDP] = held if state && held
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[0, Fiddle::SIZEOF_VOIDP],
666
- state.data[0, Fiddle::SIZEOF_VOIDP], state.held]
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[0, Fiddle::SIZEOF_VOIDP] = held[0]
675
- state.data[0, Fiddle::SIZEOF_VOIDP] = held[1]
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[0, 8]
686
- state.standin[0, 8] = [value].pack("q")
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[0, 8] = held if state && held
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.fiddle)
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
- # Fiddle's parser is used for what it is good at -- deciding the ABI code
777
- # of a type -- and this decides what that parser throws away: whether a
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 Fiddle knows them; there is no `float64_t`
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
- # Fiddle has no code for a C99 complex -- its parser does not know the
800
- # word and the ABI it answers for has nowhere to put one -- so these
801
- # stand where a Fiddle code stands elsewhere. Symbols rather than
802
- # numbers, so that one reaching Fiddle by mistake is a TypeError there
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
- # Fiddle answers `long double` with the code for `long`, silently, so it
813
- # is refused by name rather than trusted.
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: Fiddle " \
816
- "reports it as `long`, which would be the wrong " \
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 Fiddle reads only some of those
826
- # orders, fails on a bare `unsigned`, and reads `int unsigned` as a
827
- # signed int without saying so. So a spelling made only of these is
828
- # counted here and handed to Fiddle in the one order it reads.
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 code Fiddle
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 => :cmplx128,
837
- :float_complex => :cmplx64,
838
- Fiddle::TYPE_DOUBLE => :float64,
839
- Fiddle::TYPE_FLOAT => :float32,
840
- Fiddle::TYPE_CHAR => :int8,
841
- Fiddle::TYPE_UCHAR => :uint8,
842
- Fiddle::TYPE_SHORT => :int16,
843
- Fiddle::TYPE_USHORT => :uint16,
844
- Fiddle::TYPE_INT => :int32,
845
- Fiddle::TYPE_UINT => :uint32,
846
- Fiddle::TYPE_LONG => :int64,
847
- Fiddle::TYPE_LONG_LONG => :int64,
848
- # `unsigned long` and `size_t` arrive as the same code, and
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
- Fiddle::TYPE_ULONG => :uint64,
851
- Fiddle::TYPE_ULONG_LONG => :uint64,
843
+ :ulong => :uint64,
844
+ :ulong_long => :uint64,
852
845
  }.freeze
853
846
 
854
- # Codes Fiddle may return, mapped to what a kernel computes them in.
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 => :complex,
862
- :float_complex => :complex,
863
- Fiddle::TYPE_DOUBLE => :double,
864
- Fiddle::TYPE_FLOAT => :double,
865
- Fiddle::TYPE_CHAR => :int64,
866
- Fiddle::TYPE_UCHAR => :int64,
867
- Fiddle::TYPE_SHORT => :int64,
868
- Fiddle::TYPE_USHORT => :int64,
869
- Fiddle::TYPE_INT => :int64,
870
- Fiddle::TYPE_UINT => :int64,
871
- Fiddle::TYPE_LONG => :int64,
872
- Fiddle::TYPE_LONG_LONG => :int64,
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
- Fiddle::TYPE_ULONG => :uint64,
876
- Fiddle::TYPE_ULONG_LONG => :uint64,
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
- fiddle_code(pointer ? "void *" : spelling, text, prototype)
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
- fiddle_code(spelling, text, prototype)
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 Fiddle reads, for a spelling made only of INTEGER_WORDS.
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 Fiddle reads both as the one byte they are.
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 fiddle_code (spelling, text, prototype)
1057
- PARSER.parse_ctype(spelling)
1058
- rescue StandardError
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 Fiddle knows. " \
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 Fiddle where the function is. Nothing is
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[name.to_s]
1323
- rescue Fiddle::DLError => error
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.fiddle == Fiddle::TYPE_VOID &&
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 Fiddle has no such type to call
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
- Fiddle::Function.new(handle[accessor],
1541
- [Fiddle::TYPE_VOIDP],
1542
- Fiddle::TYPE_VOID, name: accessor)
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, handle[symbol],
1551
+ CFunction.new(name, prototype, return_type, parameters,
1552
+ Native.address(handle, symbol),
1559
1553
  symbol: symbol,
1560
- shim: shim_symbol && handle[shim_symbol],
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
- /* Fiddle has no type for a C99 complex, so a call from Ruby comes
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 then Fiddle::Handle::DEFAULT
1670
- when Fiddle::Handle then from
1671
- when String then Fiddle::Handle.new(from)
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 Fiddle::Handle, " \
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 Fiddle::Function shape
13
- # serves all of them; the per-kernel detail is unpacked into named locals
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 = "bounds[#{3 * axis + 1}]"
1799
+ limit = "#{index}__limit"
1792
1800
  step = @steps[axis]
1793
- if step && step.negative?
1794
- advance = step == -1 ? "#{index}--" : "#{index} += bounds[#{3 * axis + 2}]"
1795
- "#{indent}for (int64_t #{index} = #{start}; #{index} > #{limit}; " \
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
- advance = step == 1 ? "#{index}++" : "#{index} += bounds[#{3 * axis + 2}]"
1799
- "#{indent}for (int64_t #{index} = #{start}; #{index} < #{limit}; " \
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