carray-jit 0.1.2 → 0.1.4

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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +917 -3
  3. data/README.md +14 -12
  4. data/carray-jit.gemspec +1 -3
  5. data/docs/00_Introduction.md +5 -3
  6. data/docs/01_GettingStarted.md +1 -1
  7. data/docs/02_KernelShapes.md +94 -15
  8. data/docs/03_SupportedFeatures.md +665 -32
  9. data/docs/04_Compiling.md +34 -7
  10. data/docs/05_DesignNotes.md +3 -3
  11. data/docs/06_Cheatsheet.md +200 -7
  12. data/docs/07_StepByStep.ja.md +533 -0
  13. data/docs/07_StepByStep.md +534 -0
  14. data/docs/08_LanguageReference.md +427 -0
  15. data/examples/README.md +12 -0
  16. data/examples/applications/alarm.rb +121 -0
  17. data/examples/applications/collatz.rb +105 -0
  18. data/examples/applications/cubic_spline.rb +331 -0
  19. data/examples/applications/dithering.rb +144 -0
  20. data/examples/applications/group_stats.rb +115 -0
  21. data/examples/applications/lookup.rb +126 -0
  22. data/examples/applications/median_filter.rb +153 -0
  23. data/examples/applications/parcel_ascent.rb +220 -0
  24. data/examples/applications/point_in_polygon.rb +111 -0
  25. data/examples/applications/random_walk.rb +98 -0
  26. data/examples/applications/van_der_pol.rb +186 -0
  27. data/examples/applications/wet_bulb.rb +140 -0
  28. data/examples/features/10_complex.rb +14 -4
  29. data/examples/features/15_loops.rb +7 -1
  30. data/lib/carray/jit/access.rb +14 -0
  31. data/lib/carray/jit/analyzer.rb +2218 -140
  32. data/lib/carray/jit/block_reader.rb +37 -6
  33. data/lib/carray/jit/c_function.rb +961 -86
  34. data/lib/carray/jit/c_generator.rb +1970 -191
  35. data/lib/carray/jit/call.rb +68 -0
  36. data/lib/carray/jit/compiler.rb +102 -14
  37. data/lib/carray/jit/expression.rb +271 -33
  38. data/lib/carray/jit/kernel.rb +382 -32
  39. data/lib/carray/jit/node.rb +374 -9
  40. data/lib/carray/jit/sorting_networks.rb +182 -0
  41. data/lib/carray/jit/type_assignment.rb +424 -35
  42. data/lib/carray/jit/version.rb +1 -1
  43. data/lib/carray/jit.rb +547 -77
  44. metadata +23 -8
  45. data/ext/carray_jit_access/carray_jit_access.c +0 -460
  46. data/ext/carray_jit_access/extconf.rb +0 -8
@@ -115,12 +115,16 @@ class CArray
115
115
  # every one of these answers false for it, so a guard written as "refuse
116
116
  # unless this is a number" refuses it rather than letting it through.
117
117
  def self.integer? (type) = KINDS[type] == :integer
118
+ # A Float, and not an Integer standing on the same line: `nan?` is
119
+ # asked of one and refused by the other, as Ruby refuses it.
120
+ def self.floating? (type) = KINDS[type] == :real
118
121
  def self.real? (type) = REAL_TYPES.include?(type)
119
122
  def self.complex? (type) = KINDS[type] == :complex
120
123
  def self.boolean? (type) = KINDS[type] == :boolean
121
124
  def self.numeric? (type) = NUMERIC_TYPES.include?(type)
122
125
 
123
126
  def integer? (type) = self.class.integer?(type)
127
+ def floating? (type) = self.class.floating?(type)
124
128
  def real? (type) = self.class.real?(type)
125
129
  def complex? (type) = self.class.complex?(type)
126
130
  def boolean? (type) = self.class.boolean?(type)
@@ -133,17 +137,24 @@ class CArray
133
137
  def self.scalar_type (value)
134
138
  case value
135
139
  when Float then :double
136
- when Integer then :int64
140
+ when Integer then integer_type(value)
137
141
  when Complex then :complex
138
142
  else
139
143
  # A generator captured by name is worth its own answer: it is not a
140
144
  # value the kernel could carry, and what to do instead is the same
141
145
  # thing `rand` inside the block is told.
142
- if value.is_a?(Random) || value.equal?(Random) || value.equal?(Kernel)
146
+ #
147
+ # `::Random` rather than `Random`, though this file is inside
148
+ # `class CArray` and nothing there shadows it. A generator of
149
+ # CArray's own is `CArray::Rng` precisely so that it does not, and
150
+ # writing the `::` keeps the question this asks legible from the
151
+ # line rather than from that fact.
152
+ if value.is_a?(::Random) || value.equal?(::Random) ||
153
+ value.equal?(Kernel)
143
154
  raise Unsupported,
144
155
  "a generator draws in an order a kernel does not fix; fill " \
145
- "an array with `CArray#random!` and read a cell of it, as " \
146
- "the kernel reads any other array"
156
+ "an array with `CArray#random!` and read a cell of it, or " \
157
+ "draw from `CArray::Rng.new`, which a kernel can reach"
147
158
  end
148
159
  raise Unsupported,
149
160
  "captured scalars must be Float, Integer or Complex, got " \
@@ -151,6 +162,32 @@ class CArray
151
162
  end
152
163
  end
153
164
 
165
+ # Which of the two integer widths a captured Integer travels in. Ruby's
166
+ # Integer has none, so the value decides: int64 while it fits one, and
167
+ # uint64 above that, which is the width CArray has for those values and
168
+ # the one a kernel already computes them in. A value that fits neither
169
+ # is refused here rather than packed, because packing it is what would
170
+ # not say so -- `pack("q")` takes 2**64 modulo the width and hands back
171
+ # a zero.
172
+ #
173
+ # This is the value deciding a type, which nothing else about a capture
174
+ # does, and it is sound only because the value is in the kernel's cache
175
+ # key: 5 and 2**63 + 5 are two kernels, compiled and cached apart.
176
+ INT64_MIN = -(2**63)
177
+ INT64_MAX = 2**63 - 1
178
+ UINT64_MAX = 2**64 - 1
179
+
180
+ def self.integer_type (value)
181
+ return :int64 if value >= INT64_MIN && value <= INT64_MAX
182
+ return :uint64 if value >= 0 && value <= UINT64_MAX
183
+ raise Unsupported,
184
+ "#{value} is outside the integers a kernel computes in -- an " \
185
+ "int64 down to -2**63, a uint64 up to 2**64-1 -- and Ruby's " \
186
+ "Integer, which has no width, is the only thing here that can " \
187
+ "hold it. Capture it as a Float where the value's precision " \
188
+ "allows one"
189
+ end
190
+
154
191
  def self.storage_type (name)
155
192
  return STORAGE_COMPUTATION_TYPES[name] if STORAGE_COMPUTATION_TYPES[name]
156
193
  raise Unsupported, "unsupported array data type `#{name}`"
@@ -172,6 +209,12 @@ class CArray
172
209
  }
173
210
  @bindings = {}
174
211
  @binding_counts = Hash.new(0)
212
+ # The scope nodes open around the statement being typed, outermost
213
+ # first, which is what an assignment's scope counts along.
214
+ @scope_nodes = []
215
+ # A name a `while` gave its first value, by that `while`: after the
216
+ # loop it may not have one, and the refusal says why.
217
+ @assigned_in_while = {}
175
218
  assign
176
219
  end
177
220
 
@@ -188,23 +231,126 @@ class CArray
188
231
  verify(@body)
189
232
  end
190
233
 
234
+ # A local's bindings are numbered per name, from 1: `x` at its first
235
+ # type is binding 1 and at its second binding 2. What it is spelled in
236
+ # C is the generator's business.
191
237
  def bind (name, type)
192
238
  current = @bindings[name]
193
239
  return current.last if current && current.first == type
194
240
  @binding_counts[name] += 1
195
- suffix = @binding_counts[name] == 1 ? "" : "__#{@binding_counts[name]}"
196
- @bindings[name] = [type, :"#{name}#{suffix}"]
241
+ @bindings[name] = [type, @binding_counts[name]]
197
242
  @bindings[name].last
198
243
  end
199
244
 
245
+ # A local is declared once per C variable, at the head of the block for
246
+ # its scope. A binding the scope already declared is not declared again.
247
+ def declare (assignment)
248
+ scope = @scope_nodes.fetch(assignment.scope)
249
+ entry = [assignment.name, assignment.binding, assignment.type]
250
+ scope.declarations << entry unless scope.declarations.include?(entry)
251
+ end
252
+
253
+ # The same for an array the body made. Writing the same constructor
254
+ # twice in one scope is one C array and one declaration; what it emits
255
+ # at each statement is the clearing, which is the generator's.
256
+ def declare_array (declaration)
257
+ scope = @scope_nodes.fetch(declaration.scope)
258
+ entry = [declaration.name, declaration.binding,
259
+ declaration.storage, declaration.shape, declaration.heap]
260
+ scope.array_declarations << entry unless scope.array_declarations.include?(entry)
261
+ end
262
+
263
+ # An array's binding is its name's, and what counts as the same one is
264
+ # the storage type together with the shape: `w` at two shapes is two C
265
+ # arrays, exactly as `x` at two types is two C variables.
266
+ def bind_array (declaration)
267
+ bind(declaration.name,
268
+ [:local_array, declaration.storage, declaration.shape])
269
+ end
270
+
271
+ # Which of the name's variables a read or a write of an array resolves
272
+ # to. The analyzer has already refused a name that holds a number here
273
+ # and an array there, so what is left to say is that the array reached
274
+ # for is still bound -- which is what a branch that made it in one arm
275
+ # only leaves untrue.
276
+ def array_binding (node)
277
+ current = @bindings[node.name]
278
+ wanted = [:local_array, node.storage, node.shape]
279
+ unless current && current.first == wanted
280
+ raise Unsupported.new(
281
+ "`#{node.name}` is not an array here: it is made on only one path " \
282
+ "to this line, or at another shape, so there is no one C array " \
283
+ "for it to be. Make it before the branch",
284
+ node.location)
285
+ end
286
+ current.last
287
+ end
288
+
200
289
  def walk (node)
201
290
  case node
202
291
  when KernelBody
292
+ node.declarations = []
293
+ node.array_declarations = []
294
+ @scope_nodes.push(node)
203
295
  node.statements.each { |statement| walk(statement) }
296
+ @scope_nodes.pop
204
297
  when Assignment
205
298
  walk(node.expression)
206
299
  node.type = node.expression.type
207
- node.binding_name = bind(node.name, node.type)
300
+ node.binding = bind(node.name, node.type)
301
+ @assigned_in_while.delete(node.name)
302
+ declare(node)
303
+ when ParallelAssignment
304
+ # The values were made statements of their own by the analyzer, and
305
+ # stand before the writes. Walking them in that order is what gives
306
+ # a name on the left the binding it had on the right: `a, b = b, a`
307
+ # reads `b` while `b` is still the old one, because the statement
308
+ # that reads it comes first.
309
+ node.statements.each { |statement| walk(statement) }
310
+ when LocalArrayDeclaration
311
+ # A declaration has no value, so it has no type: what it says is
312
+ # what to put at the head of the block. An extent the block wrote
313
+ # as an expression is an expression, and is typed as one.
314
+ node.children.each { |extent| walk(extent) }
315
+ node.binding = bind_array(node)
316
+ @assigned_in_while.delete(node.name)
317
+ declare_array(node)
318
+ when LocalArrayAddress
319
+ # The address of an array the body made. Its binding is what says
320
+ # which C name the callee is handed; there is no value here, so no
321
+ # computation type either -- the declaration settled what it is.
322
+ node.binding = array_binding(node)
323
+ node.type = :address
324
+ when IntrinsicCall
325
+ node.binding = array_binding(node)
326
+ node.type = self.class.storage_type(node.storage)
327
+ verify_intrinsic_element_type(node)
328
+ when IntrinsicStatement
329
+ node.binding = array_binding(node)
330
+ # What it did is in the array, so there is no value and no type --
331
+ # the same slot a call to a `void` function leaves empty.
332
+ node.type = :void
333
+ verify_intrinsic_element_type(node)
334
+ when LocalArrayRead
335
+ node.binding = array_binding(node)
336
+ walk_local_array_subscripts(node)
337
+ node.type = self.class.storage_type(node.storage)
338
+ when LocalArrayWrite
339
+ node.binding = array_binding(node)
340
+ walk_local_array_subscripts(node)
341
+ walk(node.expression)
342
+ node.type = self.class.storage_type(node.storage)
343
+ when LocalArrayMaskWrite
344
+ # A mark rather than a value: it says the cell is missing and
345
+ # leaves the bytes under it alone, so there is nothing to type
346
+ # beyond the cell's own storage.
347
+ node.binding = array_binding(node)
348
+ walk_local_array_subscripts(node)
349
+ node.type = self.class.storage_type(node.storage)
350
+ when LocalArrayMaskTest
351
+ node.binding = array_binding(node)
352
+ walk_local_array_subscripts(node)
353
+ node.type = :boolean
208
354
  when ElementWrite
209
355
  walk_subscripts(write_subscripts(node))
210
356
  walk(node.expression)
@@ -215,12 +361,31 @@ class CArray
215
361
  when MaskTest
216
362
  walk_subscripts(node.subscripts)
217
363
  node.type = :boolean
364
+ when PointerTest
365
+ node.type = :boolean
366
+ when NumericPredicate
367
+ walk(node.operand)
368
+ node.type = :boolean
369
+ when Clamp
370
+ node.children.each { |child| walk(child) }
371
+ # The receiver's, which is exact where the three agree -- and
372
+ # where they do not, verify refuses rather than choosing.
373
+ node.type = node.value.type
218
374
  when InnerLoop
219
375
  walk(node.from)
220
376
  walk(node.to)
221
377
  entering = @bindings.dup
378
+ node.entering = entering
379
+ node.declarations = []
380
+ node.array_declarations = []
381
+ @scope_nodes.push(node)
222
382
  node.statements.each { |statement| walk(statement) }
383
+ @scope_nodes.pop
223
384
  verify_loop_carries_one_type(entering, node)
385
+ # What the block made is the block's. Past it, the names outside
386
+ # are what they were on the way in, carrying whatever type the
387
+ # body left them -- which the check above says is the same one.
388
+ @bindings = @bindings.select { |name, _| entering.key?(name) }
224
389
  when While
225
390
  # The condition first, and that is not only an order: a name the
226
391
  # body introduces is not bound yet when the condition is walked, so
@@ -230,6 +395,12 @@ class CArray
230
395
  entering = @bindings.dup
231
396
  node.statements.each { |statement| walk(statement) }
232
397
  verify_loop_carries_one_type(entering, node)
398
+ # The body may run no passes, so what it gave a first value to may
399
+ # have none after it -- Ruby's nil, and a C variable nothing wrote.
400
+ (@bindings.keys - entering.keys).each do |name|
401
+ @assigned_in_while[name] = node
402
+ end
403
+ @bindings = merge_bindings(entering, @bindings)
233
404
  when Print
234
405
  node.arguments.each { |argument| walk(argument) }
235
406
  when CallStatement
@@ -261,7 +432,9 @@ class CArray
261
432
  node.alternative.each { |statement| walk(statement) }
262
433
  @bindings = merge_bindings(from_consequent, @bindings)
263
434
  when IntegerLiteral
264
- 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)
265
438
  when FloatLiteral
266
439
  node.type = :double
267
440
  when ImaginaryLiteral
@@ -286,11 +459,17 @@ class CArray
286
459
  node.type = type
287
460
  when LocalRead
288
461
  current = @bindings[node.name]
462
+ if !current && (loop = @assigned_in_while[node.name])
463
+ raise Unsupported.new(
464
+ "`#{node.name}` is first assigned inside this `while`, which may " \
465
+ "run no passes; give it a value before the loop",
466
+ loop.location)
467
+ end
289
468
  unless current
290
469
  raise Unsupported.new("`#{node.name}` is read before it is assigned",
291
470
  node.location)
292
471
  end
293
- node.type, node.binding_name = current
472
+ node.type, node.binding = current
294
473
  when UnaryMinus
295
474
  walk(node.operand)
296
475
  node.type = node.operand.type
@@ -359,6 +538,11 @@ class CArray
359
538
  node.location)
360
539
  end
361
540
  node.type = node.result_type
541
+ when RandomDraw
542
+ # What the generator's C returns for this draw. There is nothing
543
+ # to infer and no argument to convert: a draw reads the state and
544
+ # nothing the block wrote.
545
+ node.type = node.kind == :bits ? :uint64 : :double
362
546
  when CFunctionCall
363
547
  # Nothing is inferred here: the prototype said what the function
364
548
  # returns and what it takes, and the arguments are converted to
@@ -385,6 +569,14 @@ class CArray
385
569
  walk(node.right)
386
570
  node.type =
387
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
388
580
  :boolean
389
581
  elsif [:<<, :>>].include?(node.operator)
390
582
  # A shift takes its count as a number and keeps the type of the
@@ -507,6 +699,48 @@ class CArray
507
699
  end
508
700
  end
509
701
 
702
+ # Which element types each intrinsic has an answer for.
703
+ #
704
+ # `min` / `max` / `sort` put the cells in an order, and neither a
705
+ # Complex nor a boolean has one -- Ruby refuses `1i < 2i` and
706
+ # `true < false` as well. `sum` adds, which a boolean does not do
707
+ # either: CArray reads the sum of a boolean array as a count, and
708
+ # counting is a meaning this compiler would be borrowing rather than
709
+ # deciding. Write the count into an integer array and sum that.
710
+ def verify_intrinsic_element_type (node)
711
+ type = self.class.storage_type(node.storage)
712
+ if boolean?(type)
713
+ if node.intrinsic == :sum
714
+ raise Unsupported.new(
715
+ "`sum` adds numbers, and `#{node.name}` is a boolean array; " \
716
+ "CArray reads the sum of one as a count, which is a meaning " \
717
+ "this would be borrowing rather than deciding. Count into an " \
718
+ "integer array and sum that",
719
+ node.location)
720
+ end
721
+ raise Unsupported.new(
722
+ "`#{node.name}` holds `true` and `false`, which do not order " \
723
+ "each other -- in Ruby either -- so `#{node.intrinsic}` has " \
724
+ "nothing to compare",
725
+ node.location)
726
+ end
727
+ return unless complex?(type)
728
+ return if node.intrinsic == :sum
729
+ raise Unsupported.new(
730
+ "`#{node.intrinsic}` orders the cells, and `#{node.name}` holds " \
731
+ "Complex numbers: Ruby does not order Complex numbers either. " \
732
+ "Take `.abs` or `.real` into a real array first",
733
+ node.location)
734
+ end
735
+
736
+ # A local array's subscripts are walked the way an operand's are: a
737
+ # position that is an expression has to be an integer. An axis the
738
+ # analyzer settled carries a literal offset rather than a node, and
739
+ # there is nothing there to type.
740
+ def walk_local_array_subscripts (node)
741
+ walk_subscripts(node.subscripts)
742
+ end
743
+
510
744
  def element_type (array, location)
511
745
  @element_types[array] or
512
746
  raise Unsupported.new("no array supplied for `#{array}`", location)
@@ -574,9 +808,11 @@ class CArray
574
808
  # raises too. The one crossing Ruby does allow is `flags[i] = 1`, which
575
809
  # CArray takes for true -- and only 0 and 1, which is why it has to be a
576
810
  # literal here.
577
- def verify_storable (node)
578
- wanted = @element_types.fetch(node.array, nil)
579
- given = node.expression.type
811
+ # What may be stored where, said about a storage type and a name rather
812
+ # than about an operand: an array the body made has a storage type of
813
+ # its own and no entry in the operand table.
814
+ def verify_storable (name, wanted, expression, location)
815
+ given = expression.type
580
816
  return if boolean?(wanted) && boolean?(given)
581
817
  # A Complex does not fit in a real cell, and Ruby says so: assigning
582
818
  # one into a float64 CArray raises rather than dropping the imaginary
@@ -584,26 +820,31 @@ class CArray
584
820
  # whose imaginary part is zero.
585
821
  if complex?(given) && real?(wanted)
586
822
  raise Unsupported.new(
587
- "`#{node.array}` holds real numbers, and the value stored into " \
823
+ "`#{name}` holds real numbers, and the value stored into " \
588
824
  "it is a Complex; storing one into a real CArray raises in Ruby " \
589
825
  "too. Store `.real`, `.imag` or `.abs`",
590
- node.location)
826
+ location)
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)
591
832
  end
592
833
  return if !boolean?(wanted) && numeric?(given)
593
- if boolean?(wanted) && node.expression.is_a?(IntegerLiteral) &&
594
- [0, 1].include?(node.expression.value)
834
+ if boolean?(wanted) && expression.is_a?(IntegerLiteral) &&
835
+ [0, 1].include?(expression.value)
595
836
  return
596
837
  end
597
838
  if boolean?(wanted)
598
839
  raise Unsupported.new(
599
- "`#{node.array}` is a boolean array, so it holds `true` and " \
840
+ "`#{name}` is a boolean array, so it holds `true` and " \
600
841
  "`false`; storing #{given} into it is what Ruby refuses too",
601
- node.location)
842
+ location)
602
843
  end
603
844
  raise Unsupported.new(
604
- "the value stored into `#{node.array}` is " \
845
+ "the value stored into `#{name}` is " \
605
846
  "#{given || 'undetermined'}, not a number",
606
- node.location)
847
+ location)
607
848
  end
608
849
 
609
850
  # The nodes that arrive without a data type of their own: a literal and
@@ -648,6 +889,9 @@ class CArray
648
889
  # side's width rather than the other side widening to Ruby's.
649
890
  return scalar.type if KIND_RANK[scalar_kind] > KIND_RANK[typed_kind]
650
891
  if scalar_kind == typed_kind
892
+ refuse_wide_capture(scalar, typed)
893
+ refuse_negative_literal(scalar, typed)
894
+ refuse_wide_literal(scalar, typed)
651
895
  # The scalar becomes that type rather than being widened to meet it,
652
896
  # and says so: a literal emitted as a double would take the C
653
897
  # expression back to double however this node is typed.
@@ -656,6 +900,62 @@ class CArray
656
900
  typed.type
657
901
  end
658
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
+
934
+ # A captured Integer above int64 meeting an integer, which absorption
935
+ # has no answer for: the scalar is supposed to take the other side's
936
+ # width, and this value did not come from a width -- it came from a Ruby
937
+ # Integer, which has none.
938
+ #
939
+ # CArray refuses the same expression, `u + 2**63` raising `bignum too
940
+ # big to convert into 'long long'` whatever the array's own type is, and
941
+ # this follows it rather than inventing an answer beside it. Where the
942
+ # other side is a Float or a Complex there is nothing to refuse: the
943
+ # wider kind wins, CArray converts the same way, and the value reaches a
944
+ # double as it does in Ruby.
945
+ def refuse_wide_capture (scalar, typed)
946
+ return unless scalar.is_a?(CaptureRead) && scalar.type == :uint64
947
+ width = typed.type == :uint64 ? "a uint64" : "an int64"
948
+ raise Unsupported.new(
949
+ "`#{scalar.name}` is a captured Integer above 2**63-1, and it is " \
950
+ "meeting #{width}: a captured Integer brings no width of its own -- " \
951
+ "it takes the width of what it meets -- and this value came from " \
952
+ "none. CArray refuses the same expression, whatever the array's own " \
953
+ "type is. Give the value a width: `CScalar.uint64() " \
954
+ "{ #{scalar.name} }`, which a kernel reads as the one-cell array it " \
955
+ "is",
956
+ scalar.location)
957
+ end
958
+
659
959
  def join (left, right)
660
960
  return right if left.nil?
661
961
  return left if right.nil?
@@ -682,6 +982,60 @@ class CArray
682
982
  # Nothing to check: no value is computed.
683
983
  when MaskTest
684
984
  # Nothing to check: it reads a mask byte.
985
+ when PointerTest
986
+ # Nothing to check: it compares an address with NULL.
987
+ when Clamp
988
+ node.children.each { |child| verify(child) }
989
+ types = node.children.map(&:type)
990
+ unless types.all? { |type| numeric?(type) }
991
+ raise Unsupported.new("`clamp` orders numbers", node.location)
992
+ end
993
+ if complex?(node.value.type)
994
+ raise Unsupported.new(
995
+ "`clamp` has no meaning for a Complex, and raises " \
996
+ "NoMethodError in Ruby; take `.abs` or `.real` first",
997
+ node.location)
998
+ end
999
+ # Ruby hands back the receiver in one branch and a bound in the
1000
+ # other, so where the two are different *classes* the answer's
1001
+ # class is decided by the value: `1.clamp(0.0, 3.0)` is the
1002
+ # Integer 1 and `5.clamp(0.0, 3.0)` the Float 3.0. No type
1003
+ # assigned here is both, so that is refused rather than settled
1004
+ # one way and wrong the other.
1005
+ #
1006
+ # Two widths of the same class are not that case. A float32 cell
1007
+ # is a Ruby Float, as a double is, so `f[i].clamp(0.0, 1.0)` has
1008
+ # one class throughout and the answer is the receiver's width --
1009
+ # the rule every other expression over that cell already takes.
1010
+ unless types.map { |type| integer?(type) }.uniq.size == 1
1011
+ value, bound = if integer?(node.value.type)
1012
+ ["an Integer", "a Float"]
1013
+ else
1014
+ ["a Float", "an Integer"]
1015
+ end
1016
+ raise Unsupported.new(
1017
+ "`clamp` hands back the value or one of the bounds, so with " \
1018
+ "#{value} value and #{bound} bound Ruby's answer is one class " \
1019
+ "in one branch and the other in the other; write the bounds " \
1020
+ "#{integer?(node.value.type) ? 'as Integers' : 'with a decimal point'}",
1021
+ node.location)
1022
+ end
1023
+ when NumericPredicate
1024
+ verify(node.operand)
1025
+ unless numeric?(node.operand.type)
1026
+ raise Unsupported.new("`#{node.name}` asks about a number",
1027
+ node.location)
1028
+ end
1029
+ # `nan?` is Float's alone: an Integer has no NaN to be and a
1030
+ # Complex has no such method, and both raise NoMethodError in Ruby
1031
+ # rather than answering false. `finite?` is on all three.
1032
+ if node.name == :nan? && !floating?(node.operand.type)
1033
+ raise Unsupported.new(
1034
+ "`nan?` is a Float's question -- " \
1035
+ "#{complex?(node.operand.type) ? 'a Complex' : 'an Integer'} " \
1036
+ "has no method by that name and raises NoMethodError in Ruby",
1037
+ node.location)
1038
+ end
685
1039
  when While
686
1040
  verify(node.condition)
687
1041
  unless boolean?(node.condition.type)
@@ -689,6 +1043,8 @@ class CArray
689
1043
  node.condition.location)
690
1044
  end
691
1045
  node.statements.each { |statement| verify(statement) }
1046
+ when ParallelAssignment
1047
+ node.statements.each { |statement| verify(statement) }
692
1048
  when InnerLoop
693
1049
  [node.from, node.to].each do |bound|
694
1050
  verify(bound)
@@ -706,7 +1062,46 @@ class CArray
706
1062
  node.children.each { |child| verify(child) }
707
1063
  when ElementWrite
708
1064
  verify(node.expression)
709
- verify_storable(node)
1065
+ verify_storable(node.array, @element_types.fetch(node.array, nil),
1066
+ node.expression, node.location)
1067
+ when LocalArrayDeclaration
1068
+ # No value is computed here. What there can be is an extent the
1069
+ # block wrote as an expression, and a length is a whole number of
1070
+ # cells -- a Float or a Complex one is not a length at all.
1071
+ node.shape.each_with_index do |extent, axis|
1072
+ next if extent.is_a?(Integer)
1073
+ verify(extent.node)
1074
+ next if integer?(extent.node.type)
1075
+ where = node.shape.size == 1 ? "" : " on axis #{axis}"
1076
+ raise Unsupported.new(
1077
+ "`#{extent.text}` is the length of `#{node.name}`#{where}, and " \
1078
+ "a length is a whole number of cells; this is " \
1079
+ "#{extent.node.type == :boolean ? 'a true or false' : "a #{extent.node.type}"}",
1080
+ node.location)
1081
+ end
1082
+ when LocalArrayAddress
1083
+ # Nothing to check: the declaration was matched against the shape
1084
+ # and the storage type as the block was read.
1085
+ when IntrinsicCall, IntrinsicStatement
1086
+ # The element type was checked as the types were assigned, and the
1087
+ # argument is an array rather than an expression: there is nothing
1088
+ # under here to walk.
1089
+ when LocalArrayRead
1090
+ node.subscripts.each { |_index, offset|
1091
+ verify(offset) if offset.is_a?(Node)
1092
+ }
1093
+ when LocalArrayMaskWrite, LocalArrayMaskTest
1094
+ # Nothing to check: one writes a mask byte and the other reads one.
1095
+ node.subscripts.each { |_index, offset|
1096
+ verify(offset) if offset.is_a?(Node)
1097
+ }
1098
+ when LocalArrayWrite
1099
+ node.subscripts.each { |_index, offset|
1100
+ verify(offset) if offset.is_a?(Node)
1101
+ }
1102
+ verify(node.expression)
1103
+ verify_storable(node.name, self.class.storage_type(node.storage),
1104
+ node.expression, node.location)
710
1105
  when LocalRead
711
1106
  unless node.type
712
1107
  raise Unsupported.new("`#{node.name}` is read before it is assigned",
@@ -763,6 +1158,14 @@ class CArray
763
1158
  Analyzer::COMPARISON_OPERATORS.include?(node.operator)
764
1159
  verify_bitwise(node) if
765
1160
  Analyzer::BIT_OPERATORS.include?(node.operator)
1161
+ # `true * true` is a NoMethodError in Ruby. Said here, where the
1162
+ # operator is, rather than wherever the boolean it made ends up.
1163
+ if Analyzer::ARITHMETIC_OPERATORS.include?(node.operator) &&
1164
+ !numeric?(node.type)
1165
+ raise Unsupported.new(
1166
+ "`#{node.operator}` is arithmetic, and `true` and `false` have " \
1167
+ "none, in Ruby either", node.location)
1168
+ end
766
1169
  # `Complex(1,2) % 2` is a NoMethodError in Ruby: a floored
767
1170
  # remainder needs an order, and the plane has none.
768
1171
  if node.operator == :% && complex?(node.type)
@@ -788,20 +1191,6 @@ class CArray
788
1191
  "Ruby would not; make one of them a Float",
789
1192
  node.location)
790
1193
  end
791
- when BinaryOperation
792
- node.children.each { |child| verify(child) }
793
- if Analyzer::COMPARISON_OPERATORS.include?(node.operator)
794
- unless numeric?(node.left.type) &&
795
- numeric?(node.right.type)
796
- raise Unsupported.new("comparison operands must be numbers",
797
- node.location)
798
- end
799
- else
800
- unless numeric?(node.type)
801
- raise Unsupported.new("`#{node.operator}` operands must be numbers",
802
- node.location)
803
- end
804
- end
805
1194
  else
806
1195
  node.children.each { |child| verify(child) }
807
1196
  end
@@ -1,5 +1,5 @@
1
1
  class CArray
2
2
  module JIT
3
- VERSION = "0.1.2"
3
+ VERSION = "0.1.4"
4
4
  end
5
5
  end