carray-jit 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +771 -3
- data/README.md +7 -6
- data/carray-jit.gemspec +1 -3
- data/docs/00_Introduction.md +4 -3
- data/docs/01_GettingStarted.md +1 -1
- data/docs/02_KernelShapes.md +93 -14
- data/docs/03_SupportedFeatures.md +582 -26
- data/docs/04_Compiling.md +33 -6
- data/docs/05_DesignNotes.md +3 -3
- data/docs/06_Cheatsheet.md +198 -5
- data/docs/07_StepByStep.ja.md +534 -0
- data/docs/07_StepByStep.md +535 -0
- data/examples/README.md +12 -0
- data/examples/applications/alarm.rb +121 -0
- data/examples/applications/collatz.rb +105 -0
- data/examples/applications/cubic_spline.rb +331 -0
- data/examples/applications/dithering.rb +144 -0
- data/examples/applications/group_stats.rb +115 -0
- data/examples/applications/lookup.rb +126 -0
- data/examples/applications/median_filter.rb +153 -0
- data/examples/applications/parcel_ascent.rb +220 -0
- data/examples/applications/point_in_polygon.rb +111 -0
- data/examples/applications/random_walk.rb +98 -0
- data/examples/applications/van_der_pol.rb +186 -0
- data/examples/applications/wet_bulb.rb +140 -0
- data/examples/features/10_complex.rb +14 -4
- data/examples/features/15_loops.rb +7 -1
- data/lib/carray/jit/access.rb +14 -0
- data/lib/carray/jit/analyzer.rb +2077 -136
- data/lib/carray/jit/block_reader.rb +37 -6
- data/lib/carray/jit/c_function.rb +613 -76
- data/lib/carray/jit/c_generator.rb +1595 -156
- data/lib/carray/jit/call.rb +68 -0
- data/lib/carray/jit/compiler.rb +75 -11
- data/lib/carray/jit/kernel.rb +369 -32
- data/lib/carray/jit/node.rb +359 -9
- data/lib/carray/jit/sorting_networks.rb +182 -0
- data/lib/carray/jit/type_assignment.rb +371 -34
- data/lib/carray/jit/version.rb +1 -1
- data/lib/carray/jit.rb +560 -64
- metadata +22 -8
- data/ext/carray_jit_access/carray_jit_access.c +0 -460
- 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
|
|
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
|
-
|
|
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,
|
|
146
|
-
"
|
|
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
|
-
|
|
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.
|
|
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,29 @@ class CArray
|
|
|
215
361
|
when MaskTest
|
|
216
362
|
walk_subscripts(node.subscripts)
|
|
217
363
|
node.type = :boolean
|
|
364
|
+
when NumericPredicate
|
|
365
|
+
walk(node.operand)
|
|
366
|
+
node.type = :boolean
|
|
367
|
+
when Clamp
|
|
368
|
+
node.children.each { |child| walk(child) }
|
|
369
|
+
# The receiver's, which is exact where the three agree -- and
|
|
370
|
+
# where they do not, verify refuses rather than choosing.
|
|
371
|
+
node.type = node.value.type
|
|
218
372
|
when InnerLoop
|
|
219
373
|
walk(node.from)
|
|
220
374
|
walk(node.to)
|
|
221
375
|
entering = @bindings.dup
|
|
376
|
+
node.entering = entering
|
|
377
|
+
node.declarations = []
|
|
378
|
+
node.array_declarations = []
|
|
379
|
+
@scope_nodes.push(node)
|
|
222
380
|
node.statements.each { |statement| walk(statement) }
|
|
381
|
+
@scope_nodes.pop
|
|
223
382
|
verify_loop_carries_one_type(entering, node)
|
|
383
|
+
# What the block made is the block's. Past it, the names outside
|
|
384
|
+
# are what they were on the way in, carrying whatever type the
|
|
385
|
+
# body left them -- which the check above says is the same one.
|
|
386
|
+
@bindings = @bindings.select { |name, _| entering.key?(name) }
|
|
224
387
|
when While
|
|
225
388
|
# The condition first, and that is not only an order: a name the
|
|
226
389
|
# body introduces is not bound yet when the condition is walked, so
|
|
@@ -230,6 +393,12 @@ class CArray
|
|
|
230
393
|
entering = @bindings.dup
|
|
231
394
|
node.statements.each { |statement| walk(statement) }
|
|
232
395
|
verify_loop_carries_one_type(entering, node)
|
|
396
|
+
# The body may run no passes, so what it gave a first value to may
|
|
397
|
+
# have none after it -- Ruby's nil, and a C variable nothing wrote.
|
|
398
|
+
(@bindings.keys - entering.keys).each do |name|
|
|
399
|
+
@assigned_in_while[name] = node
|
|
400
|
+
end
|
|
401
|
+
@bindings = merge_bindings(entering, @bindings)
|
|
233
402
|
when Print
|
|
234
403
|
node.arguments.each { |argument| walk(argument) }
|
|
235
404
|
when CallStatement
|
|
@@ -286,11 +455,17 @@ class CArray
|
|
|
286
455
|
node.type = type
|
|
287
456
|
when LocalRead
|
|
288
457
|
current = @bindings[node.name]
|
|
458
|
+
if !current && (loop = @assigned_in_while[node.name])
|
|
459
|
+
raise Unsupported.new(
|
|
460
|
+
"`#{node.name}` is first assigned inside this `while`, which may " \
|
|
461
|
+
"run no passes; give it a value before the loop",
|
|
462
|
+
loop.location)
|
|
463
|
+
end
|
|
289
464
|
unless current
|
|
290
465
|
raise Unsupported.new("`#{node.name}` is read before it is assigned",
|
|
291
466
|
node.location)
|
|
292
467
|
end
|
|
293
|
-
node.type, node.
|
|
468
|
+
node.type, node.binding = current
|
|
294
469
|
when UnaryMinus
|
|
295
470
|
walk(node.operand)
|
|
296
471
|
node.type = node.operand.type
|
|
@@ -359,6 +534,11 @@ class CArray
|
|
|
359
534
|
node.location)
|
|
360
535
|
end
|
|
361
536
|
node.type = node.result_type
|
|
537
|
+
when RandomDraw
|
|
538
|
+
# What the generator's C returns for this draw. There is nothing
|
|
539
|
+
# to infer and no argument to convert: a draw reads the state and
|
|
540
|
+
# nothing the block wrote.
|
|
541
|
+
node.type = node.kind == :bits ? :uint64 : :double
|
|
362
542
|
when CFunctionCall
|
|
363
543
|
# Nothing is inferred here: the prototype said what the function
|
|
364
544
|
# returns and what it takes, and the arguments are converted to
|
|
@@ -507,6 +687,48 @@ class CArray
|
|
|
507
687
|
end
|
|
508
688
|
end
|
|
509
689
|
|
|
690
|
+
# Which element types each intrinsic has an answer for.
|
|
691
|
+
#
|
|
692
|
+
# `min` / `max` / `sort` put the cells in an order, and neither a
|
|
693
|
+
# Complex nor a boolean has one -- Ruby refuses `1i < 2i` and
|
|
694
|
+
# `true < false` as well. `sum` adds, which a boolean does not do
|
|
695
|
+
# either: CArray reads the sum of a boolean array as a count, and
|
|
696
|
+
# counting is a meaning this compiler would be borrowing rather than
|
|
697
|
+
# deciding. Write the count into an integer array and sum that.
|
|
698
|
+
def verify_intrinsic_element_type (node)
|
|
699
|
+
type = self.class.storage_type(node.storage)
|
|
700
|
+
if boolean?(type)
|
|
701
|
+
if node.intrinsic == :sum
|
|
702
|
+
raise Unsupported.new(
|
|
703
|
+
"`sum` adds numbers, and `#{node.name}` is a boolean array; " \
|
|
704
|
+
"CArray reads the sum of one as a count, which is a meaning " \
|
|
705
|
+
"this would be borrowing rather than deciding. Count into an " \
|
|
706
|
+
"integer array and sum that",
|
|
707
|
+
node.location)
|
|
708
|
+
end
|
|
709
|
+
raise Unsupported.new(
|
|
710
|
+
"`#{node.name}` holds `true` and `false`, which do not order " \
|
|
711
|
+
"each other -- in Ruby either -- so `#{node.intrinsic}` has " \
|
|
712
|
+
"nothing to compare",
|
|
713
|
+
node.location)
|
|
714
|
+
end
|
|
715
|
+
return unless complex?(type)
|
|
716
|
+
return if node.intrinsic == :sum
|
|
717
|
+
raise Unsupported.new(
|
|
718
|
+
"`#{node.intrinsic}` orders the cells, and `#{node.name}` holds " \
|
|
719
|
+
"Complex numbers: Ruby does not order Complex numbers either. " \
|
|
720
|
+
"Take `.abs` or `.real` into a real array first",
|
|
721
|
+
node.location)
|
|
722
|
+
end
|
|
723
|
+
|
|
724
|
+
# A local array's subscripts are walked the way an operand's are: a
|
|
725
|
+
# position that is an expression has to be an integer. An axis the
|
|
726
|
+
# analyzer settled carries a literal offset rather than a node, and
|
|
727
|
+
# there is nothing there to type.
|
|
728
|
+
def walk_local_array_subscripts (node)
|
|
729
|
+
walk_subscripts(node.subscripts)
|
|
730
|
+
end
|
|
731
|
+
|
|
510
732
|
def element_type (array, location)
|
|
511
733
|
@element_types[array] or
|
|
512
734
|
raise Unsupported.new("no array supplied for `#{array}`", location)
|
|
@@ -574,9 +796,11 @@ class CArray
|
|
|
574
796
|
# raises too. The one crossing Ruby does allow is `flags[i] = 1`, which
|
|
575
797
|
# CArray takes for true -- and only 0 and 1, which is why it has to be a
|
|
576
798
|
# literal here.
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
799
|
+
# What may be stored where, said about a storage type and a name rather
|
|
800
|
+
# than about an operand: an array the body made has a storage type of
|
|
801
|
+
# its own and no entry in the operand table.
|
|
802
|
+
def verify_storable (name, wanted, expression, location)
|
|
803
|
+
given = expression.type
|
|
580
804
|
return if boolean?(wanted) && boolean?(given)
|
|
581
805
|
# A Complex does not fit in a real cell, and Ruby says so: assigning
|
|
582
806
|
# one into a float64 CArray raises rather than dropping the imaginary
|
|
@@ -584,26 +808,26 @@ class CArray
|
|
|
584
808
|
# whose imaginary part is zero.
|
|
585
809
|
if complex?(given) && real?(wanted)
|
|
586
810
|
raise Unsupported.new(
|
|
587
|
-
"`#{
|
|
811
|
+
"`#{name}` holds real numbers, and the value stored into " \
|
|
588
812
|
"it is a Complex; storing one into a real CArray raises in Ruby " \
|
|
589
813
|
"too. Store `.real`, `.imag` or `.abs`",
|
|
590
|
-
|
|
814
|
+
location)
|
|
591
815
|
end
|
|
592
816
|
return if !boolean?(wanted) && numeric?(given)
|
|
593
|
-
if boolean?(wanted) &&
|
|
594
|
-
[0, 1].include?(
|
|
817
|
+
if boolean?(wanted) && expression.is_a?(IntegerLiteral) &&
|
|
818
|
+
[0, 1].include?(expression.value)
|
|
595
819
|
return
|
|
596
820
|
end
|
|
597
821
|
if boolean?(wanted)
|
|
598
822
|
raise Unsupported.new(
|
|
599
|
-
"`#{
|
|
823
|
+
"`#{name}` is a boolean array, so it holds `true` and " \
|
|
600
824
|
"`false`; storing #{given} into it is what Ruby refuses too",
|
|
601
|
-
|
|
825
|
+
location)
|
|
602
826
|
end
|
|
603
827
|
raise Unsupported.new(
|
|
604
|
-
"the value stored into `#{
|
|
828
|
+
"the value stored into `#{name}` is " \
|
|
605
829
|
"#{given || 'undetermined'}, not a number",
|
|
606
|
-
|
|
830
|
+
location)
|
|
607
831
|
end
|
|
608
832
|
|
|
609
833
|
# The nodes that arrive without a data type of their own: a literal and
|
|
@@ -648,6 +872,7 @@ class CArray
|
|
|
648
872
|
# side's width rather than the other side widening to Ruby's.
|
|
649
873
|
return scalar.type if KIND_RANK[scalar_kind] > KIND_RANK[typed_kind]
|
|
650
874
|
if scalar_kind == typed_kind
|
|
875
|
+
refuse_wide_capture(scalar, typed)
|
|
651
876
|
# The scalar becomes that type rather than being widened to meet it,
|
|
652
877
|
# and says so: a literal emitted as a double would take the C
|
|
653
878
|
# expression back to double however this node is typed.
|
|
@@ -656,6 +881,31 @@ class CArray
|
|
|
656
881
|
typed.type
|
|
657
882
|
end
|
|
658
883
|
|
|
884
|
+
# A captured Integer above int64 meeting an integer, which absorption
|
|
885
|
+
# has no answer for: the scalar is supposed to take the other side's
|
|
886
|
+
# width, and this value did not come from a width -- it came from a Ruby
|
|
887
|
+
# Integer, which has none.
|
|
888
|
+
#
|
|
889
|
+
# CArray refuses the same expression, `u + 2**63` raising `bignum too
|
|
890
|
+
# big to convert into 'long long'` whatever the array's own type is, and
|
|
891
|
+
# this follows it rather than inventing an answer beside it. Where the
|
|
892
|
+
# other side is a Float or a Complex there is nothing to refuse: the
|
|
893
|
+
# wider kind wins, CArray converts the same way, and the value reaches a
|
|
894
|
+
# double as it does in Ruby.
|
|
895
|
+
def refuse_wide_capture (scalar, typed)
|
|
896
|
+
return unless scalar.is_a?(CaptureRead) && scalar.type == :uint64
|
|
897
|
+
width = typed.type == :uint64 ? "a uint64" : "an int64"
|
|
898
|
+
raise Unsupported.new(
|
|
899
|
+
"`#{scalar.name}` is a captured Integer above 2**63-1, and it is " \
|
|
900
|
+
"meeting #{width}: a captured Integer brings no width of its own -- " \
|
|
901
|
+
"it takes the width of what it meets -- and this value came from " \
|
|
902
|
+
"none. CArray refuses the same expression, whatever the array's own " \
|
|
903
|
+
"type is. Give the value a width: `CScalar.uint64() " \
|
|
904
|
+
"{ #{scalar.name} }`, which a kernel reads as the one-cell array it " \
|
|
905
|
+
"is",
|
|
906
|
+
scalar.location)
|
|
907
|
+
end
|
|
908
|
+
|
|
659
909
|
def join (left, right)
|
|
660
910
|
return right if left.nil?
|
|
661
911
|
return left if right.nil?
|
|
@@ -682,6 +932,58 @@ class CArray
|
|
|
682
932
|
# Nothing to check: no value is computed.
|
|
683
933
|
when MaskTest
|
|
684
934
|
# Nothing to check: it reads a mask byte.
|
|
935
|
+
when Clamp
|
|
936
|
+
node.children.each { |child| verify(child) }
|
|
937
|
+
types = node.children.map(&:type)
|
|
938
|
+
unless types.all? { |type| numeric?(type) }
|
|
939
|
+
raise Unsupported.new("`clamp` orders numbers", node.location)
|
|
940
|
+
end
|
|
941
|
+
if complex?(node.value.type)
|
|
942
|
+
raise Unsupported.new(
|
|
943
|
+
"`clamp` has no meaning for a Complex, and raises " \
|
|
944
|
+
"NoMethodError in Ruby; take `.abs` or `.real` first",
|
|
945
|
+
node.location)
|
|
946
|
+
end
|
|
947
|
+
# Ruby hands back the receiver in one branch and a bound in the
|
|
948
|
+
# other, so where the two are different *classes* the answer's
|
|
949
|
+
# class is decided by the value: `1.clamp(0.0, 3.0)` is the
|
|
950
|
+
# Integer 1 and `5.clamp(0.0, 3.0)` the Float 3.0. No type
|
|
951
|
+
# assigned here is both, so that is refused rather than settled
|
|
952
|
+
# one way and wrong the other.
|
|
953
|
+
#
|
|
954
|
+
# Two widths of the same class are not that case. A float32 cell
|
|
955
|
+
# is a Ruby Float, as a double is, so `f[i].clamp(0.0, 1.0)` has
|
|
956
|
+
# one class throughout and the answer is the receiver's width --
|
|
957
|
+
# the rule every other expression over that cell already takes.
|
|
958
|
+
unless types.map { |type| integer?(type) }.uniq.size == 1
|
|
959
|
+
value, bound = if integer?(node.value.type)
|
|
960
|
+
["an Integer", "a Float"]
|
|
961
|
+
else
|
|
962
|
+
["a Float", "an Integer"]
|
|
963
|
+
end
|
|
964
|
+
raise Unsupported.new(
|
|
965
|
+
"`clamp` hands back the value or one of the bounds, so with " \
|
|
966
|
+
"#{value} value and #{bound} bound Ruby's answer is one class " \
|
|
967
|
+
"in one branch and the other in the other; write the bounds " \
|
|
968
|
+
"#{integer?(node.value.type) ? 'as Integers' : 'with a decimal point'}",
|
|
969
|
+
node.location)
|
|
970
|
+
end
|
|
971
|
+
when NumericPredicate
|
|
972
|
+
verify(node.operand)
|
|
973
|
+
unless numeric?(node.operand.type)
|
|
974
|
+
raise Unsupported.new("`#{node.name}` asks about a number",
|
|
975
|
+
node.location)
|
|
976
|
+
end
|
|
977
|
+
# `nan?` is Float's alone: an Integer has no NaN to be and a
|
|
978
|
+
# Complex has no such method, and both raise NoMethodError in Ruby
|
|
979
|
+
# rather than answering false. `finite?` is on all three.
|
|
980
|
+
if node.name == :nan? && !floating?(node.operand.type)
|
|
981
|
+
raise Unsupported.new(
|
|
982
|
+
"`nan?` is a Float's question -- " \
|
|
983
|
+
"#{complex?(node.operand.type) ? 'a Complex' : 'an Integer'} " \
|
|
984
|
+
"has no method by that name and raises NoMethodError in Ruby",
|
|
985
|
+
node.location)
|
|
986
|
+
end
|
|
685
987
|
when While
|
|
686
988
|
verify(node.condition)
|
|
687
989
|
unless boolean?(node.condition.type)
|
|
@@ -689,6 +991,8 @@ class CArray
|
|
|
689
991
|
node.condition.location)
|
|
690
992
|
end
|
|
691
993
|
node.statements.each { |statement| verify(statement) }
|
|
994
|
+
when ParallelAssignment
|
|
995
|
+
node.statements.each { |statement| verify(statement) }
|
|
692
996
|
when InnerLoop
|
|
693
997
|
[node.from, node.to].each do |bound|
|
|
694
998
|
verify(bound)
|
|
@@ -706,7 +1010,46 @@ class CArray
|
|
|
706
1010
|
node.children.each { |child| verify(child) }
|
|
707
1011
|
when ElementWrite
|
|
708
1012
|
verify(node.expression)
|
|
709
|
-
verify_storable(node)
|
|
1013
|
+
verify_storable(node.array, @element_types.fetch(node.array, nil),
|
|
1014
|
+
node.expression, node.location)
|
|
1015
|
+
when LocalArrayDeclaration
|
|
1016
|
+
# No value is computed here. What there can be is an extent the
|
|
1017
|
+
# block wrote as an expression, and a length is a whole number of
|
|
1018
|
+
# cells -- a Float or a Complex one is not a length at all.
|
|
1019
|
+
node.shape.each_with_index do |extent, axis|
|
|
1020
|
+
next if extent.is_a?(Integer)
|
|
1021
|
+
verify(extent.node)
|
|
1022
|
+
next if integer?(extent.node.type)
|
|
1023
|
+
where = node.shape.size == 1 ? "" : " on axis #{axis}"
|
|
1024
|
+
raise Unsupported.new(
|
|
1025
|
+
"`#{extent.text}` is the length of `#{node.name}`#{where}, and " \
|
|
1026
|
+
"a length is a whole number of cells; this is " \
|
|
1027
|
+
"#{extent.node.type == :boolean ? 'a true or false' : "a #{extent.node.type}"}",
|
|
1028
|
+
node.location)
|
|
1029
|
+
end
|
|
1030
|
+
when LocalArrayAddress
|
|
1031
|
+
# Nothing to check: the declaration was matched against the shape
|
|
1032
|
+
# and the storage type as the block was read.
|
|
1033
|
+
when IntrinsicCall, IntrinsicStatement
|
|
1034
|
+
# The element type was checked as the types were assigned, and the
|
|
1035
|
+
# argument is an array rather than an expression: there is nothing
|
|
1036
|
+
# under here to walk.
|
|
1037
|
+
when LocalArrayRead
|
|
1038
|
+
node.subscripts.each { |_index, offset|
|
|
1039
|
+
verify(offset) if offset.is_a?(Node)
|
|
1040
|
+
}
|
|
1041
|
+
when LocalArrayMaskWrite, LocalArrayMaskTest
|
|
1042
|
+
# Nothing to check: one writes a mask byte and the other reads one.
|
|
1043
|
+
node.subscripts.each { |_index, offset|
|
|
1044
|
+
verify(offset) if offset.is_a?(Node)
|
|
1045
|
+
}
|
|
1046
|
+
when LocalArrayWrite
|
|
1047
|
+
node.subscripts.each { |_index, offset|
|
|
1048
|
+
verify(offset) if offset.is_a?(Node)
|
|
1049
|
+
}
|
|
1050
|
+
verify(node.expression)
|
|
1051
|
+
verify_storable(node.name, self.class.storage_type(node.storage),
|
|
1052
|
+
node.expression, node.location)
|
|
710
1053
|
when LocalRead
|
|
711
1054
|
unless node.type
|
|
712
1055
|
raise Unsupported.new("`#{node.name}` is read before it is assigned",
|
|
@@ -763,6 +1106,14 @@ class CArray
|
|
|
763
1106
|
Analyzer::COMPARISON_OPERATORS.include?(node.operator)
|
|
764
1107
|
verify_bitwise(node) if
|
|
765
1108
|
Analyzer::BIT_OPERATORS.include?(node.operator)
|
|
1109
|
+
# `true * true` is a NoMethodError in Ruby. Said here, where the
|
|
1110
|
+
# operator is, rather than wherever the boolean it made ends up.
|
|
1111
|
+
if Analyzer::ARITHMETIC_OPERATORS.include?(node.operator) &&
|
|
1112
|
+
!numeric?(node.type)
|
|
1113
|
+
raise Unsupported.new(
|
|
1114
|
+
"`#{node.operator}` is arithmetic, and `true` and `false` have " \
|
|
1115
|
+
"none, in Ruby either", node.location)
|
|
1116
|
+
end
|
|
766
1117
|
# `Complex(1,2) % 2` is a NoMethodError in Ruby: a floored
|
|
767
1118
|
# remainder needs an order, and the plane has none.
|
|
768
1119
|
if node.operator == :% && complex?(node.type)
|
|
@@ -788,20 +1139,6 @@ class CArray
|
|
|
788
1139
|
"Ruby would not; make one of them a Float",
|
|
789
1140
|
node.location)
|
|
790
1141
|
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
1142
|
else
|
|
806
1143
|
node.children.each { |child| verify(child) }
|
|
807
1144
|
end
|
data/lib/carray/jit/version.rb
CHANGED