carray-jit 0.1.3 → 0.1.5

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.
@@ -361,6 +361,8 @@ class CArray
361
361
  when MaskTest
362
362
  walk_subscripts(node.subscripts)
363
363
  node.type = :boolean
364
+ when PointerTest
365
+ node.type = :boolean
364
366
  when NumericPredicate
365
367
  walk(node.operand)
366
368
  node.type = :boolean
@@ -430,7 +432,9 @@ class CArray
430
432
  node.alternative.each { |statement| walk(statement) }
431
433
  @bindings = merge_bindings(from_consequent, @bindings)
432
434
  when IntegerLiteral
433
- node.type = :int64
435
+ # Above 2**63-1 a literal is a uint64, as a captured Integer is;
436
+ # where it then meets an int64 it is refused (#refuse_wide_literal).
437
+ node.type = TypeAssignment.integer_type(node.value)
434
438
  when FloatLiteral
435
439
  node.type = :double
436
440
  when ImaginaryLiteral
@@ -565,6 +569,14 @@ class CArray
565
569
  walk(node.right)
566
570
  node.type =
567
571
  if Analyzer::COMPARISON_OPERATORS.include?(node.operator)
572
+ # Compared in the wider of the two, which for a uint64 against a
573
+ # negative literal is a uint64 the literal does not fit.
574
+ if [node.left.type, node.right.type].include?(:uint64)
575
+ refuse_negative_literal(node.left, node.right)
576
+ refuse_negative_literal(node.right, node.left)
577
+ refuse_wide_literal(node.left, node.right)
578
+ refuse_wide_literal(node.right, node.left)
579
+ end
568
580
  :boolean
569
581
  elsif [:<<, :>>].include?(node.operator)
570
582
  # A shift takes its count as a number and keeps the type of the
@@ -813,6 +825,11 @@ class CArray
813
825
  "too. Store `.real`, `.imag` or `.abs`",
814
826
  location)
815
827
  end
828
+ if wanted == :int64 && expression.is_a?(IntegerLiteral) &&
829
+ expression.value > INT64_MAX
830
+ raise Unsupported.new(wide_literal_message(expression.value),
831
+ location)
832
+ end
816
833
  return if !boolean?(wanted) && numeric?(given)
817
834
  if boolean?(wanted) && expression.is_a?(IntegerLiteral) &&
818
835
  [0, 1].include?(expression.value)
@@ -873,6 +890,8 @@ class CArray
873
890
  return scalar.type if KIND_RANK[scalar_kind] > KIND_RANK[typed_kind]
874
891
  if scalar_kind == typed_kind
875
892
  refuse_wide_capture(scalar, typed)
893
+ refuse_negative_literal(scalar, typed)
894
+ refuse_wide_literal(scalar, typed)
876
895
  # The scalar becomes that type rather than being widened to meet it,
877
896
  # and says so: a literal emitted as a double would take the C
878
897
  # expression back to double however this node is typed.
@@ -881,6 +900,37 @@ class CArray
881
900
  typed.type
882
901
  end
883
902
 
903
+ # A negative literal meeting a uint64 would take that width and compute
904
+ # as 2**64 minus its magnitude. CArray refuses the same operand on a
905
+ # uint64 array (`RangeError: -1 is out of range for uint64`); a captured
906
+ # one is refused when the kernel is called, since its value is not part
907
+ # of the kernel.
908
+ def refuse_negative_literal (scalar, typed)
909
+ return unless scalar.is_a?(IntegerLiteral) && scalar.value.negative? &&
910
+ typed.type == :uint64
911
+ raise Unsupported.new(
912
+ "#{scalar.value} is out of range for uint64: a negative Integer " \
913
+ "meeting a uint64 would compute as 2**64#{scalar.value}. CArray " \
914
+ "refuses the same operand. Convert the uint64 side first, " \
915
+ "`x.to_f` or a signed capture", scalar.location)
916
+ end
917
+
918
+ # An integer literal above 2**63-1 computed as an int64, where it would
919
+ # wrap to a negative number. In Ruby it is an Integer, which has no
920
+ # width; meeting a uint64 or a Float it keeps its value and is accepted.
921
+ def refuse_wide_literal (scalar, typed)
922
+ return unless scalar.is_a?(IntegerLiteral) &&
923
+ scalar.value > INT64_MAX && typed.type == :int64
924
+ raise Unsupported.new(wide_literal_message(scalar.value),
925
+ scalar.location)
926
+ end
927
+
928
+ def wide_literal_message (value)
929
+ "#{value} does not fit an int64, which is what it is computed in " \
930
+ "here, and would wrap to a negative number. Meet it with a uint64 " \
931
+ "or a Float, or store it into a uint64 array"
932
+ end
933
+
884
934
  # A captured Integer above int64 meeting an integer, which absorption
885
935
  # has no answer for: the scalar is supposed to take the other side's
886
936
  # width, and this value did not come from a width -- it came from a Ruby
@@ -932,6 +982,8 @@ class CArray
932
982
  # Nothing to check: no value is computed.
933
983
  when MaskTest
934
984
  # Nothing to check: it reads a mask byte.
985
+ when PointerTest
986
+ # Nothing to check: it compares an address with NULL.
935
987
  when Clamp
936
988
  node.children.each { |child| verify(child) }
937
989
  types = node.children.map(&:type)
@@ -1,5 +1,5 @@
1
1
  class CArray
2
2
  module JIT
3
- VERSION = "0.1.3"
3
+ VERSION = "0.1.5"
4
4
  end
5
5
  end
data/lib/carray/jit.rb CHANGED
@@ -37,9 +37,9 @@ class CArray
37
37
  # no extent, and is written with CArray.jit_each or CArray.jit_map instead.
38
38
  #
39
39
  # Arrays and scalars are the variables the block closes over, so nothing has
40
- # to be named twice. Which way each axis runs is derived from the kernel's
41
- # own dependencies, not chosen: reading a cell the kernel will later write
42
- # means that cell has to be reached in one particular order.
40
+ # to be named twice. Which way each axis runs is the extent's: a Range runs
41
+ # up, a step of -1 runs down, and the kernel runs the order it is given
42
+ # without checking it against the cells the body reads.
43
43
  #
44
44
  # A block outside the compilable subset raises CArray::JIT::Unsupported
45
45
  # rather than falling back to a Ruby loop. Nobody calls this method except
@@ -638,10 +638,10 @@ class CArray
638
638
  "the indices of a term are symbols naming its axes, as in " \
639
639
  "`[a, [:i, :k]]`; got #{subscripts.inspect}"
640
640
  end
641
- unless subscripts.size == array.rank
641
+ unless subscripts.size == array.ndim
642
642
  raise Unsupported,
643
643
  "a term names one index per axis: this array has rank " \
644
- "#{array.rank} and #{subscripts.size} " \
644
+ "#{array.ndim} and #{subscripts.size} " \
645
645
  "#{subscripts.size == 1 ? 'index' : 'indices'} were given"
646
646
  end
647
647
  end
@@ -680,9 +680,9 @@ class CArray
680
680
  raise Unsupported, "`into:` is an array to write into"
681
681
  end
682
682
  expected = free.empty? ? 1 : free.size
683
- unless into.rank == expected
683
+ unless into.ndim == expected
684
684
  raise Unsupported,
685
- "`into:` has rank #{into.rank}, and the result's axes are " \
685
+ "`into:` has rank #{into.ndim}, and the result's axes are " \
686
686
  "#{free.empty? ? 'none, which is one cell' : free.map { |name| "`#{name}`" }.join(', ')}"
687
687
  end
688
688
  end
@@ -909,7 +909,7 @@ class CArray
909
909
  return false if masked
910
910
  return false if arrays.empty? || arrays.size > Sweep::MAX_ARITY
911
911
  return false unless arrays.each_value.all? { |array|
912
- array.rank.zero? || array.dim == shape
912
+ array.ndim.zero? || array.dim == shape
913
913
  }
914
914
  walkable_in_place?(arrays.each_value)
915
915
  end
@@ -933,7 +933,7 @@ class CArray
933
933
  # 0.9 ns/cell with the driver here against 6.1 ns/cell swept, and no
934
934
  # scratch either way -- there was nothing the re-gather was buying.
935
935
  def walkable_in_place? (arrays)
936
- tiers = arrays.map { |array| Access.classify(array)[:tier] }
936
+ tiers = arrays.map { |array| Access.tier(array) }
937
937
  return true if tiers.include?(Access::TIER_XFER)
938
938
  tiers.none? { |tier| tier == Access::TIER_STRIDE }
939
939
  end
@@ -1214,6 +1214,23 @@ class CArray
1214
1214
  # common case allocates nothing.
1215
1215
  EMPTY_ARRAYS = {}.freeze
1216
1216
 
1217
+ # What an element-wise pass works out from its operands' shapes and
1218
+ # types, and not from their data: which of them are walked and which
1219
+ # are handed to a C function whole, the shape the pass covers, whether
1220
+ # lining the walked ones up to it changes any of them, which loop runs
1221
+ # it, the kernel, and what type a map's result is made as.
1222
+ #
1223
+ # @private
1224
+ RunPlan = Data.define(:walked, :addressed, :stretched, :shape,
1225
+ :sweeping, :kernel, :result_type)
1226
+
1227
+ # How many plans one block keeps. Not one: a caller alternating
1228
+ # between two model states would miss on every call, and pay for the
1229
+ # asking on top of what every call paid before. Eight, as a kernel
1230
+ # keeps of its own calls (CompiledKernel::PLAN_LIMIT), and for no
1231
+ # better-measured reason.
1232
+ RUN_PLAN_LIMIT = 8
1233
+
1217
1234
  # @private
1218
1235
  def run_over_whole_arrays (block, map: false)
1219
1236
  node, source, origin = read_block(block)
@@ -1238,11 +1255,185 @@ class CArray
1238
1255
  # is before the broadcast.
1239
1256
  made = local_array_names(source, node)
1240
1257
  refuse_a_shadowed_array(made, outside)
1241
- arrays = arrays.merge(assigned_arrays(assigned - made, outside))
1258
+ written = assigned_arrays(made.empty? ? assigned : assigned - made,
1259
+ outside)
1260
+ arrays = arrays.merge(written) unless written.empty?
1242
1261
  if arrays.empty?
1243
1262
  raise Unsupported, "the block reaches no array"
1244
1263
  end
1245
1264
 
1265
+ # Everything from here to the kernel answers the same for two calls
1266
+ # whose operands agree on shape and type, so it is worked out once
1267
+ # per such agreement rather than once per call (#plan_run). What the
1268
+ # plan leaves to the call is what it has to: the operands themselves,
1269
+ # the views the broadcast makes of them, a generator's state, the
1270
+ # map's result, and the masks.
1271
+ key = run_key(arrays, scalars, c_functions, randoms, map)
1272
+ plan = key && found_run_plan(source, key)
1273
+ unless plan
1274
+ plan = plan_run(source, node, origin, arrays, scalars, c_functions,
1275
+ randoms, map)
1276
+ keep_run_plan(source, key, plan) if key
1277
+ end
1278
+
1279
+ walked = plan.addressed.empty? ? arrays : arrays.slice(*plan.walked)
1280
+ # Where no operand is stretched the broadcast hands back the operands
1281
+ # it was given, so it is not asked. Where one is, the views it makes
1282
+ # are views of these arrays and are made again: a view is exactly
1283
+ # what a plan must not keep.
1284
+ #
1285
+ # Unstretched, `aligned` can be `arrays` itself. That is sound only
1286
+ # because every write into it below puts back the array already
1287
+ # there.
1288
+ aligned = plan.stretched ? broadcast(walked).first : walked
1289
+ plan.addressed.each { |name| aligned[name] = arrays.fetch(name) }
1290
+ # A generator's state joins after the broadcast, never before it.
1291
+ # It is passed whole rather than walked, so the expression's shape
1292
+ # has nothing to say about it -- and it has a shape of its own that
1293
+ # would not line up anyway: four cells, against however many the
1294
+ # expression covers. Lining it up first is what `jit_each` did to a
1295
+ # one-cell state before 314d6cb, and a four-cell one does not even
1296
+ # stretch.
1297
+ unless randoms.empty?
1298
+ states = random_states(randoms)
1299
+ arrays = arrays.merge(states)
1300
+ aligned = aligned.merge(states)
1301
+ end
1302
+ shape = plan.shape
1303
+ if map
1304
+ result = CArray.send(plan.result_type, *(shape.empty? ? [1] : shape))
1305
+ arrays = arrays.merge(MAP_RESULT => result)
1306
+ aligned = aligned.merge(MAP_RESULT => result)
1307
+ end
1308
+
1309
+ kernel = plan.kernel
1310
+ # The shape of each of these was checked against the pass's when the
1311
+ # plan was made, and the key holds the shapes.
1312
+ kernel.written_arrays.each do |name|
1313
+ next if name == MAP_RESULT
1314
+ aligned[name] = arrays.fetch(name)
1315
+ end
1316
+
1317
+ aligned = read_once_where_the_pass_writes(aligned, kernel.written_arrays)
1318
+
1319
+ # An array handed to a C function by address is passed whole rather
1320
+ # than walked, so the expression's shape has nothing to say about it.
1321
+ # Broadcasting it would stretch a one-cell state array into a
1322
+ # read-only `CARepeat`, which the copy-back after the call cannot
1323
+ # write through -- and a state array is exactly the shape a caller
1324
+ # reaches for.
1325
+ kernel.address_arrays.each do |name|
1326
+ aligned[name] = arrays.fetch(name)
1327
+ end
1328
+
1329
+ if kernel.masked
1330
+ kernel.written_arrays.each do |name|
1331
+ array = arrays.fetch(name)
1332
+ array.mask = 0 unless array.has_mask?
1333
+ end
1334
+ end
1335
+
1336
+ result.mask = 0 if map && kernel.masked && !result.has_mask?
1337
+ drive(kernel, aligned, scalars, c_functions, shape, plan.sweeping)
1338
+ map ? result : kernel
1339
+ end
1340
+
1341
+ # What two calls of one block have to agree on for one to be run as
1342
+ # the other was.
1343
+ #
1344
+ # Each operand's name, type and shape, because that is what the kernel
1345
+ # is compiled for and the broadcast lines up; whether it is a CScalar,
1346
+ # because the broadcast stretches one where it leaves a one-cell array
1347
+ # standing. Whether it has a mask, because a mask is runtime state that
1348
+ # leaves the shape and the type alone -- one appearing makes a masked
1349
+ # kernel, and nothing else here would notice it. And the tier it would
1350
+ # be opened at, because that decides whether CArray's sweep or the
1351
+ # tiers here run the pass (#walkable_in_place?). All of it read off
1352
+ # the operand on this call; none of it kept from the last.
1353
+ #
1354
+ # Then the type of each scalar, each C function's signature and each
1355
+ # generator's kind, which is what the kernel is compiled for of those.
1356
+ # Not a scalar's value: a clock or a coefficient changes every step,
1357
+ # and keying on it would miss on every call of the loop this is for.
1358
+ #
1359
+ # Values only, never an operand: a block's plans live as long as the
1360
+ # process, and holding an array would hold its memory as long. Shapes
1361
+ # from `ndim`, `elements` and `dim0`.. rather than `dim`, which builds
1362
+ # an Array on every call.
1363
+ #
1364
+ # nil where a scalar is not one a kernel takes, so that the call is
1365
+ # refused where and as it always was rather than here.
1366
+ def run_key (arrays, scalars, c_functions, randoms, map)
1367
+ key = [map, arrays.size]
1368
+ arrays.each do |name, array|
1369
+ rank = array.ndim
1370
+ key << name << array.data_type << rank << array.is_a?(CScalar) <<
1371
+ array.has_mask? << Access.tier(array)
1372
+ case rank
1373
+ when 1 then key << array.elements
1374
+ when 2 then key << array.dim0 << array.dim1
1375
+ when 3 then key << array.dim0 << array.dim1 << array.dim2
1376
+ else key.concat(array.dim)
1377
+ end
1378
+ end
1379
+ key << scalars.size
1380
+ scalars.each do |name, value|
1381
+ key << name << TypeAssignment.scalar_type(value)
1382
+ end
1383
+ key << c_functions.size
1384
+ c_functions.each { |name, function| key << name << function.kernel_key }
1385
+ randoms.each { |name, generator| key << name << generator.generator }
1386
+ key
1387
+ rescue Unsupported
1388
+ nil
1389
+ end
1390
+
1391
+ # @private
1392
+ def run_plans
1393
+ @run_plans ||= {}
1394
+ end
1395
+
1396
+ # The block's plan for this key, if it has one. The list a plan sits
1397
+ # in is never changed once it is published -- a kernel's call lets go
1398
+ # of the GVL, so another thread may be reading it -- and bringing a
1399
+ # plan to the front replaces the list rather than editing it.
1400
+ def found_run_plan (source, key)
1401
+ plans = run_plans[source]
1402
+ return nil unless plans
1403
+ found = plans.assoc(key)
1404
+ return nil unless found
1405
+ unless plans.first.equal?(found)
1406
+ run_plans[source] =
1407
+ [found, *plans.reject { |entry| entry.equal?(found) }].freeze
1408
+ end
1409
+ found[1]
1410
+ end
1411
+
1412
+ # @private
1413
+ def keep_run_plan (source, key, plan)
1414
+ entry = [frozen_throughout(key), plan].freeze
1415
+ run_plans[source] =
1416
+ [entry, *run_plans[source]].first(RUN_PLAN_LIMIT).freeze
1417
+ end
1418
+
1419
+ # A copy that nothing outside can change under it, and that changes
1420
+ # nothing outside by being frozen: a C function's signature is its own
1421
+ # strings, and freezing those in place would freeze them in the
1422
+ # function.
1423
+ def frozen_throughout (value)
1424
+ case value
1425
+ when Array then value.map { |item| frozen_throughout(item) }.freeze
1426
+ when String then value.frozen? ? value : value.dup.freeze
1427
+ else value
1428
+ end
1429
+ end
1430
+
1431
+ # Works out what #run_over_whole_arrays keeps between calls, the way it
1432
+ # always worked it out on every call. Every refusal a pass can meet is
1433
+ # met here, before a plan exists, so a plan is only ever of a call that
1434
+ # ran.
1435
+ def plan_run (source, node, origin, arrays, scalars, c_functions,
1436
+ randoms, map)
1246
1437
  # An array the block only hands to a C function whole is not walked,
1247
1438
  # so the expression's shape has nothing to say about it -- and it has
1248
1439
  # a shape of its own that need not line up with anything. Left in,
@@ -1250,15 +1441,10 @@ class CArray
1250
1441
  # axis rather than the call it was written for.
1251
1442
  #
1252
1443
  # So it joins after the broadcast, which is what a generator's state
1253
- # already does and for the same reason. The re-substitution further
1254
- # down stays: this is a static reading, and the analyzer's answer is
1255
- # the final one.
1444
+ # already does and for the same reason. The re-substitution in the
1445
+ # caller stays: this is a static reading, and the analyzer's answer
1446
+ # is the final one.
1256
1447
  handed_over = address_array_names(source, node, c_functions)
1257
- # The common case is that there are none, and it pays nothing: no
1258
- # hash is built and the broadcast is handed the operands it always
1259
- # was. Every call comes through here -- `jit_each` is re-entered per
1260
- # call -- so the work that is only needed sometimes is done only
1261
- # then.
1262
1448
  addressed = handed_over.empty? ? EMPTY_ARRAYS
1263
1449
  : arrays.slice(*handed_over)
1264
1450
  walked = addressed.empty? ? arrays
@@ -1274,22 +1460,13 @@ class CArray
1274
1460
  end
1275
1461
 
1276
1462
  aligned, shape = broadcast(walked)
1277
- addressed.each { |name, array| aligned[name] = array }
1278
- # A generator's state joins after the broadcast, never before it.
1279
- # It is passed whole rather than walked, so the expression's shape
1280
- # has nothing to say about it -- and it has a shape of its own that
1281
- # would not line up anyway: four cells, against however many the
1282
- # expression covers. Lining it up first is what `jit_each` did to a
1283
- # one-cell state before 314d6cb, and a four-cell one does not even
1284
- # stretch.
1285
- states = random_states(randoms)
1286
- arrays = arrays.merge(states)
1287
- aligned = aligned.merge(states)
1463
+ stretched = aligned.any? { |name, array| !array.equal?(walked[name]) }
1464
+ arrays = arrays.merge(random_states(randoms))
1288
1465
  if map
1289
- result = allocate_map_result(source, node, arrays, scalars, shape,
1290
- c_functions, randoms)
1466
+ result_type = probe_map(source, node, arrays, scalars, c_functions,
1467
+ randoms)
1468
+ result = CArray.send(result_type, *(shape.empty? ? [1] : shape))
1291
1469
  arrays = arrays.merge(MAP_RESULT => result)
1292
- aligned = aligned.merge(MAP_RESULT => result)
1293
1470
  end
1294
1471
  # Both questions are about the arrays the loop walks. An array
1295
1472
  # handed over whole is not one of them: its mask is not something the
@@ -1321,31 +1498,13 @@ class CArray
1321
1498
  "expression covers #{shape.inspect}; a stretched array " \
1322
1499
  "cannot be written to"
1323
1500
  end
1324
- aligned[name] = written
1325
- end
1326
-
1327
- aligned = read_once_where_the_pass_writes(aligned, kernel.written_arrays)
1328
-
1329
- # An array handed to a C function by address is passed whole rather
1330
- # than walked, so the expression's shape has nothing to say about it.
1331
- # Broadcasting it would stretch a one-cell state array into a
1332
- # read-only `CARepeat`, which the copy-back after the call cannot
1333
- # write through -- and a state array is exactly the shape a caller
1334
- # reaches for.
1335
- kernel.address_arrays.each do |name|
1336
- aligned[name] = arrays.fetch(name)
1337
- end
1338
-
1339
- if kernel.masked
1340
- kernel.written_arrays.each do |name|
1341
- array = arrays.fetch(name)
1342
- array.mask = 0 unless array.has_mask?
1343
- end
1344
1501
  end
1345
1502
 
1346
- result.mask = 0 if map && kernel.masked && !result.has_mask?
1347
- drive(kernel, aligned, scalars, c_functions, shape, sweeping)
1348
- map ? result : kernel
1503
+ RunPlan.new(walked: walked.keys.freeze,
1504
+ addressed: addressed.keys.freeze,
1505
+ stretched: stretched, shape: shape.freeze,
1506
+ sweeping: sweeping, kernel: kernel,
1507
+ result_type: result_type)
1349
1508
  end
1350
1509
 
1351
1510
  # `a = b * 2.0` reads the whole of the right-hand side and then assigns,
@@ -1394,7 +1553,7 @@ class CArray
1394
1553
  return if written.empty?
1395
1554
  roots = written.map { |array| root_of(array) }
1396
1555
  arrays.each do |name, array|
1397
- next unless Access.classify(array)[:tier] == Access::TIER_XFER
1556
+ next unless Access.tier(array) == Access::TIER_XFER
1398
1557
  root = root_of(array)
1399
1558
  # Another operand on the same storage, and one of the two written:
1400
1559
  # a copy taken before the loop cannot answer for either of them.
@@ -1434,7 +1593,7 @@ class CArray
1434
1593
  def broadcast (arrays)
1435
1594
  values = arrays.values
1436
1595
  rank = values.reject { |value| value.is_a?(CScalar) }
1437
- .map(&:rank).max || 1
1596
+ .map(&:ndim).max || 1
1438
1597
  values = values.map { |value|
1439
1598
  value.is_a?(CScalar) ? value.refer(value.data_type, [1] * rank) : value
1440
1599
  }
@@ -1695,6 +1854,9 @@ class CArray
1695
1854
  @undef_cache = {}
1696
1855
  @probe_cache = {}
1697
1856
  @local_array_name_cache = {}
1857
+ # A plan holds the kernel it runs, so one left behind would hand the
1858
+ # next call a kernel the registry no longer has.
1859
+ @run_plans = {}
1698
1860
  end
1699
1861
 
1700
1862
  # @return [String] the directory this environment's kernels are kept in,
@@ -1837,31 +1999,13 @@ class CArray
1837
1999
  case value
1838
2000
  when CArray then arrays[name] = value
1839
2001
  when CFunction then c_functions[name] = value
1840
- else
1841
- if generator_class && value.is_a?(generator_class)
1842
- randoms[name] = refuse_an_unpasteable(name, value)
1843
- else
1844
- scalars[name] = value
1845
- end
2002
+ when CArray::Rng then randoms[name] = refuse_an_unpasteable(name, value)
2003
+ else scalars[name] = value
1846
2004
  end
1847
2005
  end
1848
2006
  [arrays, scalars, c_functions, randoms]
1849
2007
  end
1850
2008
 
1851
- # `CArray::Rng`, or nil where the CArray in use has none.
1852
- #
1853
- # Asked of the object rather than of the version, because the version
1854
- # cannot answer: 3.0.2 is where the class arrived and 3.0.2 is also
1855
- # what the release before it called itself. Asked once and remembered,
1856
- # and reached only after a capture has failed to be an array or a
1857
- # function -- a bare `when CArray::Rng` is evaluated for every captured
1858
- # number, and against a CArray without the class that is a NameError
1859
- # loose in a kernel that never mentions a generator.
1860
- def generator_class
1861
- return @generator_class unless @generator_class.nil?
1862
- @generator_class = defined?(CArray::Rng) ? CArray::Rng : false
1863
- end
1864
-
1865
2009
  # A generator CArray can run but cannot hand the source of.
1866
2010
  #
1867
2011
  # Checked here rather than where a generator is made, because a
@@ -2071,13 +2215,5 @@ end
2071
2215
  # installed, and the same answer without it.
2072
2216
  # ---------------------------------------------------------------------------
2073
2217
 
2074
- # The expression front end needs things CArray gained after 3.0.0 -- the
2075
- # plan, the kernel bodies as text, the flags they were built with, and
2076
- # somewhere to register. Without them the Prism front end above is the
2077
- # whole of this gem, exactly as before.
2078
- if CArray.respond_to?(:expression_evaluator) &&
2079
- CArray.respond_to?(:__kernel_body__) &&
2080
- CArray.const_defined?(:BUILD_FLAGS)
2081
- require "carray/jit/expression"
2082
- CArray.expression_evaluator = CArray::JIT::Expression.new
2083
- end
2218
+ require "carray/jit/expression"
2219
+ CArray.expression_evaluator = CArray::JIT::Expression.new
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.3
4
+ version: 0.1.5
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.2
18
+ version: 3.0.4
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.2
28
+ version: 3.0.4
29
29
  - - "<"
30
30
  - !ruby/object:Gem::Version
31
31
  version: '3.1'
@@ -71,6 +71,7 @@ files:
71
71
  - docs/06_Cheatsheet.md
72
72
  - docs/07_StepByStep.ja.md
73
73
  - docs/07_StepByStep.md
74
+ - docs/08_LanguageReference.md
74
75
  - examples/README.md
75
76
  - examples/applications/alarm.rb
76
77
  - examples/applications/collatz.rb