carray-jit 0.1.5 → 0.1.6

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a4d8ccd59d268ba31816c5339867d7231b7051f7879d67faecdc4eb4d924b375
4
- data.tar.gz: d73b3600e59633ac69986019597626b42a27e5e18b0143e00d39406d3f1c562f
3
+ metadata.gz: e052fd85b01164a6147a30ca655b8997ba7c87ac9c7ec8354d73447d1208b5ca
4
+ data.tar.gz: 2a6ebb524fb6afee5c871c0a45ed13ecf2f120f9ef0194e36f486ac3b015b378
5
5
  SHA512:
6
- metadata.gz: 96e9c9585b2e93ed1a2f655ee92e3ef446a11d35f95895acf08dda59ed841c7570e5bc0361995c303eda8aa81b3461068fb93f0495814b6a973309d8f0f18bb0
7
- data.tar.gz: 4b6fd2250447527deca38a65b19555833cd5702e2f27a253aa9bcba7359e3862e3f3b95f921004911de2ff502422c522a37d3507efb5b69449d94a3b40f4ecad
6
+ metadata.gz: bd5017f5251ad7b9f4263bfc25091a938d3bccd7f18ef5d83a5a68b63f57e0df6c1ee4278ccd1b903a68cb77a5a156a47c98cbc4832edabbf144593778fed1b5
7
+ data.tar.gz: d8d2733f8a98376561ccb183895e1e963eff8a1caea636d066e88ce4852f167fb8755c6f1225beaf0c89aea51bf18dab4482bde0a079983ff507e9cd66769b2f
data/CHANGELOG.md CHANGED
@@ -37,6 +37,35 @@ version you have and a newer one.
37
37
  a release needs is said in the gemspec, and an entry says so only
38
38
  when the answer changes. -->
39
39
 
40
+ ## 0.1.6
41
+
42
+ - Change: needs CArray 3.0.5 or later in the 3.0 series; install it
43
+ alongside this release. A Float stored into an integer array in a block
44
+ is refused as CArray 3.0.5's own store refuses it, and under 3.0.4 the
45
+ block would raise where CArray's store does not.
46
+
47
+ - Change: a Float stored into an integer array in a block, captured or
48
+ local, that the type has no integer for raises `RangeError`, or
49
+ `FloatDomainError` for a NaN or an infinity, as CArray 3.0.5's own
50
+ store does. Before, it stored whatever the machine's C conversion gave,
51
+ which differed between x86 and ARM. Round and clamp first where the
52
+ value may leave the range. A masked cell, and a `jit_function`'s return
53
+ value or pointer parameter, are not checked.
54
+
55
+ - Fix: an int64 array handed to a C function declared to take
56
+ `long long *` compiles with gcc, as it did with clang; gcc 14 raised
57
+ CompilationError. An array of the wrong element type raises Unsupported,
58
+ naming the type the function takes, under gcc as under clang.
59
+
60
+ - Fix: `finite?` on an integer cell in a block compiles with gcc; it
61
+ raised CompilationError there, while clang accepted it. It is true for
62
+ every integer, as in Ruby.
63
+
64
+ - Fix: a lazy expression or `CArray.fuse` computed here now gives the
65
+ same bits as CArray's own walk where gcc is the compiler. On x86 with
66
+ `-march=native`, a multiply followed by an add could come out one
67
+ rounding off in the last bit. Builds with clang were not affected.
68
+
40
69
  ## 0.1.5
41
70
 
42
71
  - Change: needs CArray 3.0.4 or later in the 3.0 series; install it
data/README.md CHANGED
@@ -8,7 +8,7 @@ The block is read with Prism, translated to C if it falls inside that subset, co
8
8
 
9
9
  ## Status
10
10
 
11
- 0.1.5 is the current release, and it still moves: behaviour can change between releases.
11
+ 0.1.6 is the current release, and it still moves: behaviour can change between releases.
12
12
 
13
13
  ## Features
14
14
 
@@ -35,7 +35,7 @@ gem "carray-jit"
35
35
  Requires:
36
36
 
37
37
  - Ruby >= 3.2
38
- - CArray >= 3.0.4, < 3.1
38
+ - CArray >= 3.0.5, < 3.1
39
39
  - A C compiler
40
40
  - Prism and Fiddle (both ship with Ruby; Fiddle is a bundled gem)
41
41
 
data/carray-jit.gemspec CHANGED
@@ -33,7 +33,7 @@ Gem::Specification.new do |spec|
33
33
  spec.executables = ["carray-jit"]
34
34
 
35
35
  # A kernel reaches CArray's C by address, so the ceiling is the next minor.
36
- spec.add_dependency "carray", ">= 3.0.4", "< 3.1"
36
+ spec.add_dependency "carray", ">= 3.0.5", "< 3.1"
37
37
  # fiddle ships as a bundled gem; depend on it explicitly so Ruby 3.5+ resolves it.
38
38
  spec.add_dependency "fiddle"
39
39
  end
@@ -1223,6 +1223,6 @@ Everything else: `for` and `until`, `begin ... end while`, strings, hashes, symb
1223
1223
  - **A local array's subscript counts from the start.** CArray's `w[-1]` is the last cell; a kernel has no such reading, so a literal negative subscript is refused by name and one the kernel works out raises `IndexError`. This is the difference the captured arrays already have. Write `w[n - 1]`.
1224
1224
  - **`CArray.empty` leaves its cells as the stack left them.** Reading one before writing it is out of contract, the way reading under a mask is: the value is whatever was there. What it saves is the clearing, which for a 256-cell array inside a per-cell loop is 2 KiB a cell.
1225
1225
  - **The stack limits are per function, and a chain of them is not counted.** One local array up to 4 KiB stands in a frame, and the arrays of one kernel body or one function body up to 16 KiB together; past either, a kernel allocates the array at its entry, while a function body -- which is called once per cell -- refuses it and asks the calling kernel for a pointer instead. A pasted function called from a kernel is a second frame, and a chain of three such functions can therefore stand 48 KiB deep while each of them is inside its own limit -- counting along the chain was considered and dropped, since the depth is not knowable where recursion is in the subset at all. What that costs is the bargain a deep recursion already takes: too deep is a SIGSEGV rather than a `SystemStackError`, and a body carrying arrays reaches it sooner. Both numbers are provisional, and neither counts a future thread pool's stacks.
1226
- - **Storing a real number into an integer array is C's conversion.** A value past the type's range, or a NaN, is undefined behaviour rather than a number -- and the same is true of a captured integer array, so a kernel brings no new hazard here. Round and clamp before storing where the value may leave the range. The rounding methods are not this case: `floor`, `ceil`, `round`, `truncate` and `to_i` on a Float raise `FloatDomainError` for a NaN or an infinity, as Ruby does, and `RangeError` where the Integer is past int64 -- except written straight into a float cell, which holds it whole (`1e20.floor` is `1e20`). The check costs a comparison per call, and in a loop that does little else it shows.
1226
+ - **Storing a real number into an integer array is checked.** It keeps the integer part, as CArray's own store does, and a value past the type's range raises `RangeError` -- `FloatDomainError` for a NaN or an infinity -- where CArray's store raises the same; C leaves that conversion undefined, and x86 and ARM answer it differently. Like any error at a cell it is raised when the pass ends, and the cell holds 0 in the meantime. A masked cell is not checked. The check is a comparison per store; round and clamp before storing where the value may leave the range and the cost shows. A compiled function's return value and a write through its pointer parameter are not this case: they are C's conversion, as the declaration says. Nor are the rounding methods: `floor`, `ceil`, `round`, `truncate` and `to_i` on a Float raise `FloatDomainError` for a NaN or an infinity, as Ruby does, and `RangeError` where the Integer is past int64 -- except written straight into a float cell, which holds it whole (`1e20.floor` is `1e20`). The check costs a comparison per call, and in a loop that does little else it shows.
1227
1227
  - **A block's source must be recoverable.** Blocks defined in `eval` or in a console have no file to read back; set `RubyVM.keep_script_lines = true` before defining them, or hand the text to `CArray::JIT.compile`, which takes a kernel as a string rather than as a block. This also ties the gem to CRuby, which CArray requires anyway.
1228
1228
  - **Nothing existing is replaced.** `jit_for` is a new method, not a faster `each_index`: the two differ in what they reject, and a caller should be able to choose.
data/docs/04_Compiling.md CHANGED
@@ -67,7 +67,7 @@ In `~/.cache/carray-jit` (honouring `XDG_CACHE_HOME`), one directory per version
67
67
 
68
68
  A kernel is only good for the version that generated it and the architecture it was built for, so those get their own directories. CArray's version is there for the same reason and one of its own: a kernel is handed CArray's memory, on layouts CArray decides, so one compiled against one version and run against the next would answer wrongly rather than fail to load.
69
69
 
70
- The two numbers say different things and move on their own clocks: the first is the version that generated the kernel, the second the version it was generated against. This gem is not versioned with CArray, and one release of it meets more than one -- the dependency is `>= 3.0.4, < 3.1`, so 3.0.4 and 3.0.5 both satisfy it -- and a layout the kernel reaches into can move between them. A new release does not spend its cache budget on entries nothing can reach any more, and a home directory shared between machines -- over NFS, or between Rosetta and native -- does not have one architecture evicting the other's kernels. A directory whose newest entry has not been touched in 30 days (`CARRAY_JIT_CACHE_MAX_AGE_DAYS`) is removed.
70
+ The two numbers say different things and move on their own clocks: the first is the version that generated the kernel, the second the version it was generated against. This gem is not versioned with CArray, and one release of it meets more than one -- the dependency is `>= 3.0.5, < 3.1`, so 3.0.5 and 3.0.6 both satisfy it -- and a layout the kernel reaches into can move between them. A new release does not spend its cache budget on entries nothing can reach any more, and a home directory shared between machines -- over NFS, or between Rosetta and native -- does not have one architecture evicting the other's kernels. A directory whose newest entry has not been touched in 30 days (`CARRAY_JIT_CACHE_MAX_AGE_DAYS`) is removed.
71
71
 
72
72
  ```ruby
73
73
  CArray::JIT.cache_root #=> "/home/you/.cache/carray-jit"
@@ -395,7 +395,7 @@ These are deliberate. Everything not listed here gives Ruby's answer, to the las
395
395
  - **A complex raised to a power** uses C's `cpow`, which can differ from Ruby in the last bit.
396
396
  - **A sine beside a cosine of the same argument** may be computed together by the C compiler, which can change the last bit.
397
397
  - **A reduction is reassociated** into partial sums unless `reassociate: false` is given, so its last bits can differ from adding in order.
398
- - **A real stored into an integer array** is C's conversion: past the type's range, or for a NaN, the result depends on the machine. Ruby would raise or grow.
398
+ - **A real stored into an integer array** keeps its integer part, as CArray's store does, and one past the type's range raises `RangeError` where Ruby's `to_i` would grow. A NaN or an infinity raises `FloatDomainError`, as `to_i` does.
399
399
 
400
400
  **The language**
401
401
 
@@ -425,4 +425,4 @@ A block reads like Ruby run over one cell at a time, and it answers as that Ruby
425
425
  | `\|` and `&` of a masked boolean (`UNDEF \| true`) | Kleene: `true` | `\|\|` and `&&`: masked whenever an operand is |
426
426
  | a `uint64` compared with a negative `int64` | compares by value: `7 < -7` is false | compared as two `uint64`: `7 < -7` is true |
427
427
 
428
- Where they agree, the block keeps CArray's answer: integer `/` and `%` floor, a shift means what it means to `Integer`, `INT64_MIN / -1` is `INT64_MIN`, overflow wraps, an Integer meets a Float as two doubles, narrow types compute narrow, `Math` outside its domain is NaN, a real out of range stored into an integer cell is C's conversion, and a masked operand gives a masked result.
428
+ Where they agree, the block keeps CArray's answer: integer `/` and `%` floor, a shift means what it means to `Integer`, `INT64_MIN / -1` is `INT64_MIN`, overflow wraps, an Integer meets a Float as two doubles, narrow types compute narrow, `Math` outside its domain is NaN, a real stored into an integer cell keeps its integer part and raises `RangeError` past the type's range (`FloatDomainError` for a NaN or an infinity), and a masked operand gives a masked result.
@@ -55,6 +55,13 @@ class CArray
55
55
  "#{text.sub(/\s*\[.*\]\z/, "")} #{name}[#{array if sized?}]"
56
56
  end
57
57
 
58
+ # The pointer an array argument decays to, as C spells it:
59
+ # `const double coef[3]` takes a `const double *`.
60
+ def decayed
61
+ return text unless array
62
+ "#{text.sub(/\s*\[.*\]\z/, "")} *"
63
+ end
64
+
58
65
  # @return [String] the type, as C spells it.
59
66
  def to_s
60
67
  text
@@ -421,6 +421,7 @@ class CArray
421
421
  @uses_real_arg = false
422
422
  @uses_real_power_complex = false
423
423
  @clamp_types = []
424
+ @store_types = []
424
425
  @gamma_types = []
425
426
  @complex_helpers = []
426
427
  @uses_floor_divide = false
@@ -727,6 +728,7 @@ class CArray
727
728
  :real_arg => @uses_real_arg,
728
729
  :real_power_complex => @uses_real_power_complex,
729
730
  :clamp => @clamp_types.dup,
731
+ :store => @store_types.dup,
730
732
  :gamma => @gamma_types.dup,
731
733
  :complex => @complex_helpers.dup,
732
734
  :index_check => @uses_index_check,
@@ -839,6 +841,7 @@ class CArray
839
841
  @uses_real_arg ||= needs[:real_arg]
840
842
  @uses_real_power_complex ||= needs[:real_power_complex]
841
843
  @clamp_types |= needs[:clamp] || []
844
+ @store_types |= needs[:store] || []
842
845
  @gamma_types |= needs[:gamma] || []
843
846
  @complex_helpers |= needs[:complex] || []
844
847
  @uses_index_check ||= needs[:index_check]
@@ -1095,6 +1098,31 @@ class CArray
1095
1098
  C
1096
1099
  end
1097
1100
  text << intrinsic_helpers
1101
+ @store_types.each do |storage|
1102
+ low, high = STORE_BOUNDS.fetch(storage)
1103
+ c_type = storage_c_type(storage)
1104
+ text << <<~C
1105
+ /* A real number stored into an #{storage} cell keeps its integer
1106
+ part, as CArray's store does. One with no #{storage} to keep --
1107
+ a NaN, an infinity, a number past the range -- is refused as
1108
+ CArray refuses it: C leaves that conversion undefined, and the
1109
+ machines answer it differently. */
1110
+ static inline #{c_type}
1111
+ carray_jit_store_#{storage} (double value, int32_t *error)
1112
+ {
1113
+ if ( value > #{low} && value < #{high} ) {
1114
+ return (#{c_type}) value;
1115
+ }
1116
+ if ( error ) {
1117
+ *error = isnan(value) ? #{FLOAT_NAN_CODE} :
1118
+ isinf(value) ? ( value > 0 ? #{FLOAT_INFINITY_CODE} : #{FLOAT_NEGATIVE_INFINITY_CODE} ) :
1119
+ #{STORE_RANGE_CODES.fetch(storage)};
1120
+ }
1121
+ return 0;
1122
+ }
1123
+
1124
+ C
1125
+ end
1098
1126
  @clamp_types.each do |type|
1099
1127
  suffix, c_type, has_nan = CLAMP_C_TYPES.fetch(type)
1100
1128
  refusal = lambda { |test, code|
@@ -1834,6 +1862,14 @@ class CArray
1834
1862
  FLOAT_NEGATIVE_INFINITY_CODE = -5
1835
1863
  INTEGER_RANGE_CODE = -6
1836
1864
 
1865
+ # And what a real number stored into an integer cell reports where the
1866
+ # cell's type has no integer for it, one code for each type, so the
1867
+ # message can name the type as CArray's own store does.
1868
+ STORE_RANGE_CODES = {
1869
+ "int8" => -7, "int16" => -8, "int32" => -9, "int64" => -10,
1870
+ "uint8" => -11, "uint16" => -12, "uint32" => -13, "uint64" => -14,
1871
+ }.freeze
1872
+
1837
1873
  # The fixed failures a body reports, as the exception each one raises.
1838
1874
  # A kernel and a compiled function both look here, so the two say the
1839
1875
  # same thing for the same cell.
@@ -1842,6 +1878,24 @@ class CArray
1842
1878
  FLOAT_INFINITY_CODE => [FloatDomainError, "Infinity"],
1843
1879
  FLOAT_NEGATIVE_INFINITY_CODE => [FloatDomainError, "-Infinity"],
1844
1880
  INTEGER_RANGE_CODE => [RangeError, "a rounded Float is past the range of int64"],
1881
+ **STORE_RANGE_CODES.to_h { |storage, code|
1882
+ [code, [RangeError, "a Float is out of range for #{storage}"]]
1883
+ },
1884
+ }.freeze
1885
+
1886
+ # The bounds a real number has to fall strictly between for its integer
1887
+ # part to be a value of the type, written as doubles, which hold every
1888
+ # one of them exactly. CArray's store keeps the integer part, so -0.5
1889
+ # goes into a uint8 cell as 0, and 255.9 as 255.
1890
+ STORE_BOUNDS = {
1891
+ "int8" => ["-129.0", "128.0"],
1892
+ "int16" => ["-32769.0", "32768.0"],
1893
+ "int32" => ["-2147483649.0", "2147483648.0"],
1894
+ "int64" => ["-9223372036854777856.0", "9223372036854775808.0"],
1895
+ "uint8" => ["-1.0", "256.0"],
1896
+ "uint16" => ["-1.0", "65536.0"],
1897
+ "uint32" => ["-1.0", "4294967296.0"],
1898
+ "uint64" => ["-1.0", "18446744073709551616.0"],
1845
1899
  }.freeze
1846
1900
 
1847
1901
  RAISE_CODE_FLOOR = 3
@@ -2588,9 +2642,9 @@ class CArray
2588
2642
  # expression written into it carried, and the branches the write
2589
2643
  # stands in. A write reached because a missing cell said so was
2590
2644
  # reached for no reason, whichever of the three it writes.
2645
+ masked = @masked ? combine_masks(@carried_masks + [emit_mask(write.expression)]) : nil
2591
2646
  mask = if @masked
2592
- "#{indent}#{local_array_reference(write, :mask)} = " \
2593
- "#{combine_masks(@carried_masks + [emit_mask(write.expression)])};\n"
2647
+ "#{indent}#{local_array_reference(write, :mask)} = #{masked};\n"
2594
2648
  else
2595
2649
  ""
2596
2650
  end
@@ -2600,10 +2654,10 @@ class CArray
2600
2654
  "#{indent}#{COMPUTATION_C_TYPES.fetch(type)} #{temporary};\n" +
2601
2655
  emit_conditional_statement(expression, temporary, type, indent) +
2602
2656
  "#{indent}#{target} = " \
2603
- "#{cast_to_storage(temporary, type, write.storage)};\n" + mask
2657
+ "#{cast_to_storage(temporary, type, write.storage, masked: masked)};\n" + mask
2604
2658
  else
2605
2659
  type = complex_storage_of_power(expression, write.storage) || expression.type
2606
- value = cast_to_storage(emit(expression, type), type, write.storage)
2660
+ value = cast_to_storage(emit(expression, type), type, write.storage, masked: masked)
2607
2661
  "#{indent}#{target} = #{value};\n" + mask
2608
2662
  end
2609
2663
  end
@@ -2999,13 +3053,41 @@ class CArray
2999
3053
  @storage_types.fetch(array)
3000
3054
  end
3001
3055
 
3002
- def cast_to_storage (text, type, storage)
3056
+ # A captured array's element type is matched at the call
3057
+ # (`address_buffer`), after the build, and raises before the kernel
3058
+ # runs. Where the declaration spells the pointer differently from the
3059
+ # array's own, the address is cast to it, so that check is the one that
3060
+ # answers: gcc refuses an incompatible pointer at compile time where
3061
+ # clang only warns. `long long *` is such a spelling for an int64
3062
+ # array, which the check accepts and gcc does not. A body's own
3063
+ # pointer parameter is no captured array and is handed on as it is:
3064
+ # its type was declared in C.
3065
+ def emit_array_argument (argument, parameter)
3066
+ address = emit(argument, :address)
3067
+ return address unless @storage_types.key?(argument.array)
3068
+ own = "#{storage_c_type(array_storage(argument.array))} *"
3069
+ declared = parameter.decayed.sub(/\Aconst\s+/, "")
3070
+ declared == own ? address : "(#{parameter.decayed})#{address}"
3071
+ end
3072
+
3073
+ # `masked` is the C of the written cell's mask, for a write whose mask
3074
+ # the error slot does not already know about: a missing cell holds
3075
+ # whatever was under its mask, which is no value to refuse.
3076
+ def cast_to_storage (text, type, storage, masked: nil)
3003
3077
  # Storing into a boolean array normalises: CArray's boolean is a byte
3004
3078
  # holding 0 or 1, and a kernel is not the place that invariant stops
3005
3079
  # being true.
3006
3080
  return "(uint8_t)((#{text}) ? 1 : 0)" if boolean_storage?(storage)
3007
3081
  target = storage_c_type(storage)
3008
3082
  return text if COMPUTATION_C_TYPES.fetch(type) == target
3083
+ if [:double, :float].include?(type) and STORE_BOUNDS.key?(storage)
3084
+ @store_types |= [storage]
3085
+ error = error_argument
3086
+ if masked and masked != "0"
3087
+ error = "(#{masked} ? (int32_t *) 0 : #{error})"
3088
+ end
3089
+ return "carray_jit_store_#{storage}(#{text}, #{error})"
3090
+ end
3009
3091
  "(#{target})(#{text})"
3010
3092
  end
3011
3093
 
@@ -3563,8 +3645,9 @@ class CArray
3563
3645
  c_function = @c_functions.fetch(node.name)
3564
3646
  arguments = node.arguments.zip(c_function.parameters)
3565
3647
  .map { |argument, parameter|
3566
- if argument.is_a?(ArrayAddress) ||
3567
- argument.is_a?(LocalArrayAddress)
3648
+ if argument.is_a?(ArrayAddress)
3649
+ emit_array_argument(argument, parameter)
3650
+ elsif argument.is_a?(LocalArrayAddress)
3568
3651
  emit(argument, :address)
3569
3652
  else
3570
3653
  emit(argument, parameter.computation)
@@ -3623,12 +3706,15 @@ class CArray
3623
3706
  "#{emit(node.high, type)}, #{error_argument})", LEAF_PRECEDENCE]
3624
3707
  end
3625
3708
 
3626
- # `isnan` and `isfinite` are C's own, and take a number of any width.
3627
- # A Complex answers `finite?` the way Ruby answers it -- both parts
3628
- # finite -- through a helper, so that the number is computed once.
3629
- # An Integer is finite whatever it holds, and goes through `isfinite`
3630
- # all the same rather than being answered here: the read it stands for
3631
- # is a read, and dropping it would drop what the read reports.
3709
+ # `isnan` and `isfinite` are C's own, and take a real floating number
3710
+ # of either width. A Complex answers `finite?` the way Ruby answers
3711
+ # it -- both parts finite -- through a helper, so that the number is
3712
+ # computed once. An Integer is finite whatever it holds, and goes
3713
+ # through `isfinite` all the same rather than being answered here: the
3714
+ # read it stands for is a read, and dropping it would drop what the
3715
+ # read reports. It goes in as a double, because C asks for a floating
3716
+ # argument and gcc refuses an integer one where clang lets it pass;
3717
+ # every integer converts to a finite double.
3632
3718
  def emit_numeric_predicate (node)
3633
3719
  type = node.operand.type
3634
3720
  function = node.name == :nan? ? "isnan" : "isfinite"
@@ -3636,7 +3722,11 @@ class CArray
3636
3722
  return emit_helper_call("complex_finite", [emit(node.operand, type)],
3637
3723
  type == :float_complex)
3638
3724
  end
3639
- ["#{function}(#{emit(node.operand, type)})", LEAF_PRECEDENCE]
3725
+ operand = emit(node.operand, type)
3726
+ if TypeAssignment::INTEGER_TYPES.include?(type)
3727
+ operand = "(double)(#{operand})"
3728
+ end
3729
+ ["#{function}(#{operand})", LEAF_PRECEDENCE]
3640
3730
  end
3641
3731
 
3642
3732
  def emit_mask_test (node)
@@ -40,7 +40,17 @@ class CArray
40
40
  # multiply and an add into one rounding, so it compiles with
41
41
  # -ffp-contract=off. Here the reference is the eager kernel, which was
42
42
  # built with whatever CArray settled on.
43
- FLAGS = ["-fPIC", "-shared", *CArray::BUILD_FLAGS.split, *DYNAMIC_LOOKUP].freeze
43
+ #
44
+ # Except between nodes. CArray computes each node in a kernel of its
45
+ # own, so no node's multiply meets the next node's add; this object
46
+ # writes the expression as one statement per node in a single loop.
47
+ # clang contracts within a statement by default, which leaves that
48
+ # alone, but gcc defaults to -ffp-contract=fast and contracts across
49
+ # statements: on x86 with -march=native, `a * b + c` lost a rounding in
50
+ # 3296 of 30000 cells. -ffp-contract=on keeps a contraction inside a
51
+ # node's own body, as CArray's kernel has it, and none between nodes.
52
+ FLAGS = ["-fPIC", "-shared", *CArray::BUILD_FLAGS.split,
53
+ "-ffp-contract=on", *DYNAMIC_LOOKUP].freeze
44
54
 
45
55
  C_TYPES = {
46
56
  float64: "double", float32: "float",
@@ -1,5 +1,5 @@
1
1
  class CArray
2
2
  module JIT
3
- VERSION = "0.1.5"
3
+ VERSION = "0.1.6"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: carray-jit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - himotoyoshi
@@ -15,7 +15,7 @@ dependencies:
15
15
  requirements:
16
16
  - - ">="
17
17
  - !ruby/object:Gem::Version
18
- version: 3.0.4
18
+ version: 3.0.5
19
19
  - - "<"
20
20
  - !ruby/object:Gem::Version
21
21
  version: '3.1'
@@ -25,7 +25,7 @@ dependencies:
25
25
  requirements:
26
26
  - - ">="
27
27
  - !ruby/object:Gem::Version
28
- version: 3.0.4
28
+ version: 3.0.5
29
29
  - - "<"
30
30
  - !ruby/object:Gem::Version
31
31
  version: '3.1'