carray-jit 0.1.3 → 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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +146 -0
- data/README.md +11 -10
- data/carray-jit.gemspec +1 -1
- data/docs/00_Introduction.md +2 -1
- data/docs/02_KernelShapes.md +1 -1
- data/docs/03_SupportedFeatures.md +88 -11
- data/docs/04_Compiling.md +2 -2
- data/docs/05_DesignNotes.md +1 -1
- data/docs/06_Cheatsheet.md +3 -3
- data/docs/07_StepByStep.ja.md +14 -15
- data/docs/07_StepByStep.md +14 -15
- data/docs/08_LanguageReference.md +427 -0
- data/lib/carray/jit/analyzer.rb +144 -7
- data/lib/carray/jit/c_function.rb +359 -21
- data/lib/carray/jit/c_generator.rb +398 -58
- data/lib/carray/jit/compiler.rb +27 -3
- data/lib/carray/jit/expression.rb +271 -33
- data/lib/carray/jit/kernel.rb +17 -4
- data/lib/carray/jit/node.rb +15 -0
- data/lib/carray/jit/type_assignment.rb +53 -1
- data/lib/carray/jit/version.rb +1 -1
- data/lib/carray/jit.rb +7 -33
- metadata +4 -3
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
# Language reference
|
|
2
|
+
|
|
3
|
+
This chapter states what a block may contain, what each construct means, and what is refused. The other chapters teach the language by example. This one is for looking things up, and it tries to be complete rather than gentle.
|
|
4
|
+
|
|
5
|
+
The block language is a subset of Ruby. A construct in the subset means what Ruby means by it, except where §10 says otherwise; §11 lists where it differs from what CArray's own array operations answer. A construct outside the subset is refused when the block is read, with `CArray::JIT::Unsupported`, before anything is compiled or run. Nothing falls back to running the block as Ruby.
|
|
6
|
+
|
|
7
|
+
## 1. Notation and terms
|
|
8
|
+
|
|
9
|
+
- **Kernel**: the C function a block becomes.
|
|
10
|
+
- **Cell**: one element of an array.
|
|
11
|
+
- **Capture**: a name the block reads from the scope it was written in.
|
|
12
|
+
- **Index**: a block parameter, or the parameter of an inner loop. An index is an integer.
|
|
13
|
+
- **Local**: a name assigned inside the block.
|
|
14
|
+
- **Local array**: an array the block makes for itself with a constructor (§5.9).
|
|
15
|
+
- **Refused**: the block raises `CArray::JIT::Unsupported`, a subclass of `CArray::JIT::Error` and so of `StandardError`. Where the refusal can point at the source, the message ends with "(at line L, column C)".
|
|
16
|
+
|
|
17
|
+
## 2. Entry points
|
|
18
|
+
|
|
19
|
+
A block reaches the compiler through one of these methods. They share one language; they differ in what the block's parameters are and what the block's value means.
|
|
20
|
+
|
|
21
|
+
| Method | Parameters | Value of the block |
|
|
22
|
+
|---|---|---|
|
|
23
|
+
| `CArray.jit_for(*extents) { \|i, j, ...\| }` | one index per extent | none; the block writes arrays |
|
|
24
|
+
| `CArray.jit_each { }` | none | none; the block writes arrays |
|
|
25
|
+
| `CArray.jit_map { }` | none | the last statement, collected into a new array |
|
|
26
|
+
| `array.jit_init { \|i, j, ...\| }` | one index per axis of the receiver | the last statement, written into the receiver's cell |
|
|
27
|
+
| `CArray.jit_stencil(*arrays, border:, type:, into:) { \|w, ...\| }` | one window per array | the last statement, collected into the result |
|
|
28
|
+
| `CArray.jit_contract(*free) { \|i, j, k\| }` | the indices | one expression, or one write as the last statement |
|
|
29
|
+
| `CArray.jit_function(prototype) { \|params\| }` | the prototype's parameters | the last statement, unless the return type is `void` |
|
|
30
|
+
| `CArray.jit_call(prototype) { }` | none; the prototype names them | as `jit_function` |
|
|
31
|
+
|
|
32
|
+
`CArray.jit_extern` binds a function compiled elsewhere and takes no block.
|
|
33
|
+
|
|
34
|
+
**Rules for every block:**
|
|
35
|
+
|
|
36
|
+
- The block is never called. Its source is read from the file, or from `RubyVM.keep_script_lines`. A block whose source cannot be found or no longer matches its file is refused.
|
|
37
|
+
- Only required parameters are allowed. Optional, rest, keyword and block parameters are refused.
|
|
38
|
+
- An empty body is refused.
|
|
39
|
+
- A body that writes no array and calls nothing is refused, for the methods whose block has no value.
|
|
40
|
+
- An index may not be named after a C keyword or a name the generated code uses (`pointers`, `strides`, `bounds`, `reals`, `integers`, `functions`, `data`, `mask_pointers`, `mask_strides`, `error`, any name containing `__`, any name starting with `carray_jit_`). A local with such a name is renamed silently.
|
|
41
|
+
|
|
42
|
+
### 2.1 `jit_for`
|
|
43
|
+
|
|
44
|
+
Each extent is an Integer `n`, meaning `0...n`; a Range, neither endless nor descending; or an `Enumerator::ArithmeticSequence` with a non-zero step. There must be as many extents as indices, and at least one. The loop runs in the direction of the extent's step; the direction is not derived from the body.
|
|
45
|
+
|
|
46
|
+
Arrays are captured and indexed: `out[i] = a[i] + 1`. A bare array name is refused. A CScalar is a cell with no index, written `s[]` or `s`.
|
|
47
|
+
|
|
48
|
+
### 2.2 `jit_each` and `jit_map`
|
|
49
|
+
|
|
50
|
+
The block takes no parameters. Every captured array name stands for the current cell, written bare (`a`) or as `a[]`. `a[i]` is refused, and so is `out[] = ...`: a write is `out = ...`. Arrays broadcast; a CScalar stretches. Writing an array that is stretched is refused.
|
|
51
|
+
|
|
52
|
+
For `jit_map`, the last statement is the value. It may be an assignment, whose value is what was assigned. A boolean value is refused, because no array type holds it. A last statement that makes a local array is refused.
|
|
53
|
+
|
|
54
|
+
A name the block makes a local array under, where the same name is a CArray outside, is refused.
|
|
55
|
+
|
|
56
|
+
### 2.3 `jit_stencil`
|
|
57
|
+
|
|
58
|
+
The parameters are windows onto the given arrays, which must share a shape. A window is read with one literal offset per axis: `w[-1, 0]`. Each offset is an integer literal or `+`, `-` and `*` over literals. A captured array read with no subscript, or with `[]`, is the current cell; read with subscripts it is an absolute position, not an offset. A stencil writes no array.
|
|
59
|
+
|
|
60
|
+
`border:` is one of `:mask`, `:skip`, `:zero`, `:clamp` and `:wrap`. `type:` and `into:` may not both be given.
|
|
61
|
+
|
|
62
|
+
### 2.4 `jit_contract`
|
|
63
|
+
|
|
64
|
+
The body is one expression, or a write `res[i, j] = expr` as its last statement. Called with no arguments, an index that appears once is free and an index that repeats is summed. Called with Symbols, the named indices are free and the block parameters are summed; a name in both is refused. An array that is both written and read is refused, as are locals before the summand, local arrays, generators, and an index inside a computed subscript.
|
|
65
|
+
|
|
66
|
+
### 2.5 `jit_function` and `jit_call`
|
|
67
|
+
|
|
68
|
+
The prototype is a C declaration. Its types are the keywords `const unsigned signed void char short int long float double _Complex complex`, the fixed-width types `int8_t` to `uint64_t`, and `size_t ssize_t ptrdiff_t intptr_t uintptr_t`. `long double` and a pointer return type are refused.
|
|
69
|
+
|
|
70
|
+
A number parameter computes as `double` (from `float` or `double`), `int64` (from `char` to `long long`), `uint64` (from `unsigned long`, `unsigned long long` and `size_t`) or `complex`. A pointer to numbers is indexed like an array; `const` makes it read-only, and `void *` cannot be indexed. A bare pointer is a value only as a condition, where it tests for NULL (`p == nil` is accepted).
|
|
71
|
+
|
|
72
|
+
A function body captures nothing except C functions. It calls itself as `name.call(...)`. It may not use `UNDEF` or allocate a local array on the heap.
|
|
73
|
+
|
|
74
|
+
For `jit_call` the block takes no parameters: the prototype's names are the parameters, and their values are read from the locals of the same names where the call is written. Assigning one is refused.
|
|
75
|
+
|
|
76
|
+
## 3. Captures
|
|
77
|
+
|
|
78
|
+
A name the block reads but does not assign is a capture. A name starting with a capital letter (including a path such as `Float::INFINITY`) is evaluated where the block was written. Any other name must be a defined local there, or the block is refused.
|
|
79
|
+
|
|
80
|
+
What a captured value becomes depends on its class:
|
|
81
|
+
|
|
82
|
+
| Class | Becomes |
|
|
83
|
+
|---|---|
|
|
84
|
+
| `CArray`, including `CScalar` | an array |
|
|
85
|
+
| `CArray::JIT::CFunction` | a C function |
|
|
86
|
+
| `CArray::Rng` | a random number generator |
|
|
87
|
+
| `Float` | a `double` |
|
|
88
|
+
| `Integer` within int64 | an `int64` |
|
|
89
|
+
| `Integer` in `2**63 ... 2**64` | a `uint64` |
|
|
90
|
+
| `Complex` | a `complex` |
|
|
91
|
+
|
|
92
|
+
Any other value is refused, including `true`, `false`, `nil`, `Rational`, `String`, `Symbol`, `Range`, `Array` and `Proc`. An Integer outside both ranges is refused. `UNDEF`, `Math` and `Math::*` are not captures.
|
|
93
|
+
|
|
94
|
+
A captured scalar is read-only in the sense that the kernel cannot change the outer variable. Assigning a name that is also a local outside makes a new local inside the kernel; the outer variable keeps its value. Reading the name before that assignment is refused.
|
|
95
|
+
|
|
96
|
+
A generator may be captured in `jit_for`, `jit_each` and `jit_map`. It is refused in a stencil, a contraction and a function body. Ruby's `Random`, `rand` and `srand` are refused with a pointer to `CArray::Rng`.
|
|
97
|
+
|
|
98
|
+
## 4. Lexical elements
|
|
99
|
+
|
|
100
|
+
**Accepted anywhere an expression is:**
|
|
101
|
+
|
|
102
|
+
- integer literals, which compute as `int64`. A literal in `2**63 ... 2**64` is a `uint64`: it may meet a `uint64` or a real, or be stored into a uint64 or real array, and is refused where it would be computed as an `int64`. A larger one is refused;
|
|
103
|
+
- float literals, which compute as `double`. An infinite literal such as `1e400` is `INFINITY`;
|
|
104
|
+
- imaginary literals (`2i`, `2.5i`), which compute as `complex`;
|
|
105
|
+
- `true` and `false`;
|
|
106
|
+
- parentheses around exactly one expression.
|
|
107
|
+
|
|
108
|
+
**Accepted only in one position:**
|
|
109
|
+
|
|
110
|
+
| Element | Where |
|
|
111
|
+
|---|---|
|
|
112
|
+
| a string literal | the format of `printf`, and the message of `raise` |
|
|
113
|
+
| a Symbol | the type of `CArray.new` / `CArray.empty`, and the key `rng:` |
|
|
114
|
+
| an Array literal | the shape of `CArray.new` / `CArray.empty`, and the right-hand side of a parallel assignment |
|
|
115
|
+
| a Range literal | the receiver of an inner `each` or `step` |
|
|
116
|
+
| `nil` | a pointer test in a function body |
|
|
117
|
+
| `UNDEF` | `x = UNDEF`, `a[...] = UNDEF`, and `== UNDEF` / `!= UNDEF` |
|
|
118
|
+
| `Math::PI`, `Math::E` | anywhere; other `Math::` constants are refused |
|
|
119
|
+
|
|
120
|
+
Everything else in an expression position is refused: `nil` elsewhere, Rational literals, strings and interpolation, Symbols, Hashes, `self`, instance, global and class variables, `defined?`.
|
|
121
|
+
|
|
122
|
+
## 5. Statements
|
|
123
|
+
|
|
124
|
+
### 5.1 Assignment
|
|
125
|
+
|
|
126
|
+
`x = e` binds a local. The right-hand side may be a constructor, which makes a local array (§5.9). Assigning an index is refused, and so is assigning a number to a name that is a local array in sight. A chained assignment `x = y = e` is refused.
|
|
127
|
+
|
|
128
|
+
In `jit_each`, `jit_map` and `jit_init`, `x = e` where `x` names a captured array writes the current cell, and `x = UNDEF` masks it.
|
|
129
|
+
|
|
130
|
+
### 5.2 Operator assignment
|
|
131
|
+
|
|
132
|
+
`x op= e` means `x = x op e`, with `op` one of `+ - * / % ** & | ^ << >>`. `x` must already be assigned. `a[...] op= e` is the same on a cell. `||=` and `&&=` are refused: no number in a kernel is `nil` or `false`.
|
|
133
|
+
|
|
134
|
+
### 5.3 Parallel assignment
|
|
135
|
+
|
|
136
|
+
`a, b = e1, e2` evaluates every value on the right before writing anything on the left. The targets are locals or index targets. The right-hand side is unbracketed, has no splat, and has as many values as there are targets. Nested targets, splats, a trailing comma and attribute targets are refused. A parallel assignment cannot be a block's value.
|
|
137
|
+
|
|
138
|
+
### 5.4 Writing a cell
|
|
139
|
+
|
|
140
|
+
`a[subs] = e` writes a captured array, a local array or a pointer parameter. A captured array is written at a cell the kernel walks to (an index, with or without an offset), at a fixed position, or at a computed position. `a[subs] = UNDEF` masks the cell and leaves its value.
|
|
141
|
+
|
|
142
|
+
### 5.5 Conditionals
|
|
143
|
+
|
|
144
|
+
`if`, `elsif` and `else` work as statements, and `else` is optional there. The modifier `stmt if cond` is the same statement. A condition must be boolean (§6.3); in a function body a pointer is also a condition.
|
|
145
|
+
|
|
146
|
+
As an expression, `if` and the ternary need an `else`, and each branch is one expression. `elsif` is refused in an expression; a nested ternary is accepted.
|
|
147
|
+
|
|
148
|
+
`unless`, in either form, is refused.
|
|
149
|
+
|
|
150
|
+
### 5.6 `while`
|
|
151
|
+
|
|
152
|
+
`while cond ... end` and the modifier `stmt while cond`. `begin ... end while` is refused. `while true` is refused unless a `break` or `raise` can leave it. `until` is refused.
|
|
153
|
+
|
|
154
|
+
A local first assigned inside a `while` cannot be read after it.
|
|
155
|
+
|
|
156
|
+
### 5.7 Inner loops
|
|
157
|
+
|
|
158
|
+
| Form | Runs `k` over |
|
|
159
|
+
|---|---|
|
|
160
|
+
| `(a...b).each { \|k\| }` | `a` up to `b`, excluding `b` |
|
|
161
|
+
| `(a..b).each { \|k\| }` | `a` up to `b`, including `b` |
|
|
162
|
+
| `n.times { \|k\| }` | `0` up to `n`, excluding `n` |
|
|
163
|
+
| `from.step(to, s) { \|k\| }` | `from` towards `to`, including `to`, in steps of `s` |
|
|
164
|
+
| `(a...b).step(s) { \|k\| }` | `a` up to `b`, excluding `b`, in steps of `s` |
|
|
165
|
+
|
|
166
|
+
The block takes exactly one parameter. A step is a non-zero integer literal, optionally negated; a Range may not step down. The bounds are integers, and may be any integer expression.
|
|
167
|
+
|
|
168
|
+
Where the bounds are built from literals, captured scalars and the ranges of the enclosing indices, the range is known before the kernel runs, and a subscript the index walks is checked against its array before the first cell (§6.6). Where a bound reads a local or a cell, the range is known only when the loop is reached; a subscript that index walks is checked at each access instead, as a computed position is. An inner range written over such an index is in the same position.
|
|
169
|
+
|
|
170
|
+
An inner index may not reuse the name of an index in scope. Two loops side by side may reuse a name.
|
|
171
|
+
|
|
172
|
+
`downto`, `upto`, `reverse_each`, `each_with_index`, `each_with_object` and `for` are refused, each with the spelling to use instead.
|
|
173
|
+
|
|
174
|
+
A local first assigned inside an inner loop cannot be read after it.
|
|
175
|
+
|
|
176
|
+
### 5.8 `break`, `next`, `return`, `raise` and `printf`
|
|
177
|
+
|
|
178
|
+
- `break` leaves the innermost inner loop or `while`. At the top of the block it is refused.
|
|
179
|
+
- `next` continues the innermost loop. At the top of the block it skips the rest of the cell.
|
|
180
|
+
- Neither takes a value.
|
|
181
|
+
- `return` is refused: a body's value is its last statement.
|
|
182
|
+
- `raise "message"` is a statement. The message is a string literal, with no exception class. The kernel stops at the end of the pass and raises `RuntimeError` with that message. A `raise` reached under a masked cell is ignored.
|
|
183
|
+
- `printf("format", args...)` prints and flushes. Integer arguments take `d i u x X o`; real arguments take `e E f F g G a A`; a complex argument prints as two reals; a boolean prints as an integer. A wrong count is refused.
|
|
184
|
+
|
|
185
|
+
A call to a C function may stand as a statement; its value is discarded, and a `void` function may only appear here. Any other expression as a statement is refused.
|
|
186
|
+
|
|
187
|
+
### 5.9 Local arrays
|
|
188
|
+
|
|
189
|
+
A local array is made by assigning a constructor:
|
|
190
|
+
|
|
191
|
+
- `CArray.<type>(d1, ...)`, with the type names CArray uses (`int8` ... `uint64`, `float32`, `float64`, `cmplx64`, `cmplx128`, `boolean`) and its aliases `byte`, `short`, `int`, `float` (float32), `double` (float64), `complex` (cmplx64) and `dcomplex` (cmplx128). The cells start at zero.
|
|
192
|
+
- `CArray.new(:type, [d1, ...])`, whose cells start at zero.
|
|
193
|
+
- `CArray.empty(:type, [d1, ...])`, whose cells are not set.
|
|
194
|
+
|
|
195
|
+
`object` and `fixlen` are refused, as are `zeros`, `ones`, `full`, a constructor with a block, and a constructor with keywords.
|
|
196
|
+
|
|
197
|
+
An extent is an integer literal, or `+`, `-` and `*` over literals, and must be at least 1. An extent over captured integers is computed when the kernel starts, and that array is allocated on the heap. An extent from an index, a local or a cell is refused.
|
|
198
|
+
|
|
199
|
+
A local array of up to 4 KiB stands in the kernel's frame, with up to 16 KiB in all; larger arrays are allocated on the heap. A function body may not allocate on the heap.
|
|
200
|
+
|
|
201
|
+
A local array is read and written with as many subscripts as it has axes. A literal subscript is checked when the block is read; any other subscript is checked as it runs and raises `IndexError` out of range. A negative literal subscript is refused. A local array cannot be read bare, has no methods, and is out of scope after the block that made it. One made on only one branch of a conditional cannot be read after it.
|
|
202
|
+
|
|
203
|
+
### 5.10 Intrinsics
|
|
204
|
+
|
|
205
|
+
`sum(w)`, `min(w)` and `max(w)` are values. `sort(w)` is a statement. Each takes exactly one local array, of one axis.
|
|
206
|
+
|
|
207
|
+
- `sum` adds in index order and returns the element's computation type.
|
|
208
|
+
- `min` and `max` skip NaN, return NaN for an array of nothing but NaN, and keep the first of two equal values.
|
|
209
|
+
- `sort` puts every NaN after every number.
|
|
210
|
+
|
|
211
|
+
They are refused on a captured array, a scalar, an expression, two arguments, a boolean array, and (except `sum`) a complex array. A kernel that carries masks refuses all four.
|
|
212
|
+
|
|
213
|
+
## 6. Expressions
|
|
214
|
+
|
|
215
|
+
### 6.1 Operators
|
|
216
|
+
|
|
217
|
+
| Kind | Operators |
|
|
218
|
+
|---|---|
|
|
219
|
+
| arithmetic | `+ - * / %`, `**` |
|
|
220
|
+
| comparison | `< <= > >= == !=` |
|
|
221
|
+
| logical | `&&`, `and`, `\|\|`, `or`, `!`, `not` |
|
|
222
|
+
| bitwise | `& \| ^ << >> ~` |
|
|
223
|
+
| unary | `-x`, `+x` |
|
|
224
|
+
|
|
225
|
+
`**` with a negative literal exponent is refused (Ruby would answer a Rational). An integer raised to a power that is not a literal is refused. `<=>`, `===` and `eql?` are refused.
|
|
226
|
+
|
|
227
|
+
### 6.2 Methods on numbers
|
|
228
|
+
|
|
229
|
+
| Method | Meaning |
|
|
230
|
+
|---|---|
|
|
231
|
+
| `abs` | absolute value; a complex gives a real |
|
|
232
|
+
| `floor ceil round truncate to_i to_int` | to an integer, as Ruby rounds |
|
|
233
|
+
| `to_f` | to a double |
|
|
234
|
+
| `real imaginary imag conjugate conj arg angle phase` | the parts of a number |
|
|
235
|
+
| `clamp(lo, hi)` | Ruby's `clamp`; the Range form is refused |
|
|
236
|
+
| `nan? finite?` | predicates on reals; `finite?` also on complex |
|
|
237
|
+
| `sqrt exp log log10 sin cos tan sinh cosh tanh asin acos atan asinh acosh atanh` | written after the number: `x.sqrt` |
|
|
238
|
+
|
|
239
|
+
None takes an argument except `clamp`; `round(2)` is refused. `infinite?` is refused, with advice. Other methods are refused, among them `zero?`, `positive?`, `negative?`, `div`, `fdiv`, `divmod`, `modulo`, `remainder`, `expm1`, `log1p` and `square`. A block passed to any call is refused.
|
|
240
|
+
|
|
241
|
+
### 6.3 Conditions
|
|
242
|
+
|
|
243
|
+
A condition is a comparison, a mask test (§8), `nan?`/`finite?`, a boolean cell or local, or one of these combined with the logical operators. A number is not a condition: there is no truthiness. The operands of `&&`, `||` and `!` must be boolean.
|
|
244
|
+
|
|
245
|
+
### 6.4 `Math`
|
|
246
|
+
|
|
247
|
+
`Math.sqrt cbrt exp log log2 log10 sin cos tan asin acos atan atan2 sinh cosh tanh hypot erf erfc gamma asinh acosh atanh`. `atan2` and `hypot` take two arguments; the rest take one. `Math.log(x, base)`, `lgamma`, `frexp` and `ldexp` are refused, as is any other name.
|
|
248
|
+
|
|
249
|
+
Fifteen of them take a complex argument: `sqrt exp log` and the six trigonometric and six hyperbolic functions. A complex argument to any other is refused.
|
|
250
|
+
|
|
251
|
+
`Math.gamma` answers a whole number up to 23 exactly, as Ruby does, and raises `Math::DomainError` for a negative whole number and for negative infinity.
|
|
252
|
+
|
|
253
|
+
### 6.5 Complex numbers
|
|
254
|
+
|
|
255
|
+
`Complex(x, y)` and `Complex(x)` (which is `Complex(x, 0.0)`). The parts must be real. The result computes as `complex`.
|
|
256
|
+
|
|
257
|
+
### 6.6 Subscripts of captured arrays
|
|
258
|
+
|
|
259
|
+
A subscript on each axis is one of:
|
|
260
|
+
|
|
261
|
+
- an index, `i`;
|
|
262
|
+
- an index with a literal offset, `i + c` or `i - c`, where `c` is a non-negative literal (`i + -1` is refused);
|
|
263
|
+
- an index with an offset over literals and captures, `i + n`; an index inside the offset is refused;
|
|
264
|
+
- a fixed position: a literal, a capture, or `+`, `-` and `*` over them;
|
|
265
|
+
- a computed position: anything else, such as `a[b[i]]`.
|
|
266
|
+
|
|
267
|
+
A subscript must be an integer. An array must be indexed with the same number of subscripts everywhere.
|
|
268
|
+
|
|
269
|
+
**Bounds:**
|
|
270
|
+
|
|
271
|
+
- Walked subscripts and fixed positions are checked before the kernel runs. A position out of range is refused; a negative position is refused rather than counted from the end.
|
|
272
|
+
- A computed position, and a subscript walked by an inner index whose range reads a local or a cell (§5.7), is checked as it runs. Out of range, a read gives cell 0, a write does nothing, and the kernel raises `IndexError` at the end of the pass.
|
|
273
|
+
|
|
274
|
+
In `jit_for`, an array that is written may be read through an inner index only if every axis it is written on uses the same outer index.
|
|
275
|
+
|
|
276
|
+
### 6.7 C functions
|
|
277
|
+
|
|
278
|
+
A C function is called as `f.call(args)`, `f.(args)` or `f[args]`, with the declared number of arguments. A pointer parameter takes a captured array by name, or a local array; an expression in that position is refused. A local array passed to a sized parameter must match its element type and be at least as long. A captured array passed to a C function must have the declared element type and length and carry no mask.
|
|
279
|
+
|
|
280
|
+
### 6.8 Random numbers
|
|
281
|
+
|
|
282
|
+
On a captured `CArray::Rng` `r`: `r.random` (a uniform double), `r.randomn` (a normal double) and `r.bits` (a `uint64`). `random(rng: r)` and `randomn(rng: r)` are the same draws. No other method and no arguments are accepted.
|
|
283
|
+
|
|
284
|
+
## 7. Types
|
|
285
|
+
|
|
286
|
+
### 7.1 Computation types
|
|
287
|
+
|
|
288
|
+
A value computes in one of seven types:
|
|
289
|
+
|
|
290
|
+
| Type | C | From storage |
|
|
291
|
+
|---|---|---|
|
|
292
|
+
| `int64` | `int64_t` | int8, int16, int32, int64, uint8, uint16, uint32 |
|
|
293
|
+
| `uint64` | `uint64_t` | uint64 |
|
|
294
|
+
| `float` | `float` | float32 |
|
|
295
|
+
| `double` | `double` | float64 |
|
|
296
|
+
| `float_complex` | `float _Complex` | cmplx64 |
|
|
297
|
+
| `complex` | `double _Complex` | cmplx128 |
|
|
298
|
+
| `boolean` | `int` | boolean |
|
|
299
|
+
|
|
300
|
+
Any other array type is refused. A cell is widened to its computation type when read and converted back when stored; a boolean cell stores 0 or 1.
|
|
301
|
+
|
|
302
|
+
### 7.2 How a leaf gets its type
|
|
303
|
+
|
|
304
|
+
- A literal: §4.
|
|
305
|
+
- An index, and an inner loop's bound: `int64`.
|
|
306
|
+
- A cell: its array's computation type.
|
|
307
|
+
- A capture: §3.
|
|
308
|
+
- A pointer read: the declared element type.
|
|
309
|
+
- A C call: the declared return type.
|
|
310
|
+
|
|
311
|
+
### 7.3 Combining two values
|
|
312
|
+
|
|
313
|
+
The numeric types are ordered `int64 < uint64 < float < double < float_complex < complex`. Two values of the same type give that type. Otherwise the result depends on whether each side is **weak** (a literal or a captured scalar) or **strong** (anything else):
|
|
314
|
+
|
|
315
|
+
- Both weak, or both strong: the later type in the order.
|
|
316
|
+
- One weak and one strong:
|
|
317
|
+
- if the weak side is of a higher kind (integer < real < complex), the result is the weak side's type: `int32_cell * 2.0` is `double`, `f32_cell * 1i` is `complex`;
|
|
318
|
+
- if they are of the same kind, the weak side takes the strong side's width: `f32_cell * 2.0` is `float`;
|
|
319
|
+
- if the weak side is of a lower kind, the result is the strong side's type.
|
|
320
|
+
|
|
321
|
+
Two cases at the same kind are refused: a negative literal meeting a `uint64`, and a captured Integer above `2**63 - 1` meeting any integer (it has no width of its own to give; wrap it in a `CScalar.uint64`).
|
|
322
|
+
|
|
323
|
+
This rule applies to arithmetic, `& | ^`, `**` and the two branches of a conditional expression.
|
|
324
|
+
|
|
325
|
+
- A shift takes the type of its left operand.
|
|
326
|
+
- A comparison is `boolean`, and compares in the wider of its operands.
|
|
327
|
+
- `-x` and `~x` keep the operand's type.
|
|
328
|
+
- `clamp` takes the receiver's type.
|
|
329
|
+
- A `Math` function of real arguments is at least `double`. Of a complex argument, it has that argument's complex width.
|
|
330
|
+
- `floor`, `ceil`, `round`, `truncate`, `to_i` and `to_int` give `int64`, except that an integer operand keeps its own type. `to_f` gives `double`.
|
|
331
|
+
- `real`, `imag` and `arg` of a complex give its real width. `conj` keeps the type. Of a real number, `real` and `conj` keep the type, `imaginary` is `int64` and `arg` is `double`.
|
|
332
|
+
|
|
333
|
+
A boolean does not combine with a number, so `flag + 1` is refused, and so is a boolean compared with a number.
|
|
334
|
+
|
|
335
|
+
**Also refused:**
|
|
336
|
+
|
|
337
|
+
- an ordering comparison or `%` on complex values;
|
|
338
|
+
- a conversion, `clamp` or `nan?` on a complex;
|
|
339
|
+
- a bitwise operator on non-integers;
|
|
340
|
+
- storing a complex into a real array (store `.real`, `.imag` or `.abs`);
|
|
341
|
+
- storing a boolean into a numeric array, or a number into a boolean array (the literals 0 and 1 are accepted there).
|
|
342
|
+
|
|
343
|
+
### 7.4 Locals
|
|
344
|
+
|
|
345
|
+
A local's type is the type of what was last assigned to it. Assigning a value of another type makes a new local under the same name.
|
|
346
|
+
|
|
347
|
+
- **After a conditional**, a local can be read only if every branch assigned it, and at the same type.
|
|
348
|
+
- **A type change that comes round a loop** is refused: for example, a local that enters a loop as an integer and leaves the body as a float.
|
|
349
|
+
|
|
350
|
+
## 8. Masks
|
|
351
|
+
|
|
352
|
+
A kernel carries masks when an array it walks has a mask, or when the block mentions `UNDEF`. A stencil's `border: :mask` does not make it carry masks.
|
|
353
|
+
|
|
354
|
+
- A value's mask follows the expression. Literals, indices, captures and mask tests contribute no mask. A conditional's mask follows the branch taken.
|
|
355
|
+
- A write to a cell sets its mask from the value's mask. Writing a present value clears the cell's mask.
|
|
356
|
+
- A write under a branch or a `while` that was decided by a masked value is masked too.
|
|
357
|
+
- Locals and the cells of local arrays carry masks the same way.
|
|
358
|
+
- `a[...] == UNDEF` and `!= UNDEF` test the mask and are never masked themselves. In `jit_each` and `jit_map`, `a == UNDEF` is the same test on the current cell. Comparing anything else with `UNDEF` is refused.
|
|
359
|
+
- Under a masked cell, a division by zero, an index out of range, `clamp`, `gamma` and `raise` report nothing, and a C call standing as a statement is skipped.
|
|
360
|
+
|
|
361
|
+
**Refused in a kernel that carries masks:** the intrinsics, and passing a local array to a C function. A captured array with a mask may not be passed to a C function; strip the mask first with `strip_mask(fill)`. A function body may not use `UNDEF`.
|
|
362
|
+
|
|
363
|
+
## 9. Errors at run time
|
|
364
|
+
|
|
365
|
+
A block that is accepted can still raise when the kernel runs. The kernel finishes its pass and then raises:
|
|
366
|
+
|
|
367
|
+
| Exception | When |
|
|
368
|
+
|---|---|
|
|
369
|
+
| `ZeroDivisionError` | integer `/`, integer or float `%` by zero |
|
|
370
|
+
| `IndexError` | a computed subscript or a local-array subscript out of range |
|
|
371
|
+
| `ArgumentError` | `clamp` with `lo > hi` or a NaN bound; a heap-allocated local array whose extent is below 1 |
|
|
372
|
+
| `Math::DomainError` | `Math.gamma` at a negative whole number or negative infinity |
|
|
373
|
+
| `FloatDomainError` | rounding NaN or an infinity to an integer |
|
|
374
|
+
| `RangeError` | rounding a Float past int64; a negative captured Integer used where a `uint64` is computed |
|
|
375
|
+
| `NoMemoryError` | a heap-allocated local array that cannot be allocated |
|
|
376
|
+
| `RuntimeError` | `raise "message"` |
|
|
377
|
+
|
|
378
|
+
## 10. Where the answer differs from Ruby
|
|
379
|
+
|
|
380
|
+
These are deliberate. Everything not listed here gives Ruby's answer, to the last bit.
|
|
381
|
+
|
|
382
|
+
**Integers**
|
|
383
|
+
|
|
384
|
+
- **Integers have a width.** A kernel computes integers in `int64` (or `uint64` for a uint64 cell). Where Ruby's Integer would grow past 2^63, a kernel wraps modulo 2^64. `INT64_MIN / -1` is `INT64_MIN` and `INT64_MIN % -1` is 0; `INT64_MIN.abs` is `INT64_MIN`.
|
|
385
|
+
- **A value stored into a narrower cell wraps.** `int8` cell `= 200` stores `-56`, where Ruby keeps 200.
|
|
386
|
+
- **An Integer that no width holds is refused** where Ruby would carry on: a literal or a captured Integer above 2^64 - 1 or below -2^63; a literal above 2^63 - 1 computed as an `int64`; a captured Integer above 2^63 - 1 meeting an integer; and a negative Integer meeting a `uint64` (a literal is refused when the block is read, a captured one raises `RangeError` when the kernel is called).
|
|
387
|
+
- **An integer raised to a power that is not a literal** is refused; a literal power is exact by squaring and wraps like any product. A negative literal power is refused (Ruby answers a Rational).
|
|
388
|
+
- **An Integer compared with a Float** is compared as two doubles. Below 2^53 every `int64` is a double exactly and nothing differs; above it, `(2**53 + 1) == 2.0**53` is true here and false in Ruby.
|
|
389
|
+
|
|
390
|
+
**Floating point**
|
|
391
|
+
|
|
392
|
+
- **Narrow types compute narrow.** A float32 cell computes as `float`, and a cmplx64 cell as `float _Complex`. Ruby has no narrower Float, so the same expression run in Ruby rounds at double width.
|
|
393
|
+
- **`Math` answers NaN outside its domain** rather than raising `Math::DomainError`, except `Math.gamma`. `Math.sqrt(-0.0)` is `-0.0`, where Ruby answers `0.0`.
|
|
394
|
+
- **A negative real raised to a fractional power** is NaN where the result is read as a real. Stored into a complex cell, or joined with a complex value, it is Ruby's Complex.
|
|
395
|
+
- **A complex raised to a power** uses C's `cpow`, which can differ from Ruby in the last bit.
|
|
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
|
+
- **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.
|
|
399
|
+
|
|
400
|
+
**The language**
|
|
401
|
+
|
|
402
|
+
- **Numbers are not conditions.** There is no truthiness; a condition is boolean, and `&&`, `||` and `!` take booleans.
|
|
403
|
+
- **Assigning a captured name** makes a local inside the kernel; the variable outside keeps its value.
|
|
404
|
+
- **A local first assigned on one branch** cannot be read after the branch, where Ruby would give `nil`.
|
|
405
|
+
- **A loop's direction** is the step of its extent, whatever the body reads; the kernel runs the order it is given, as the same Ruby loop would.
|
|
406
|
+
- **A subscript counts from the start.** A negative position is refused or raises `IndexError`, where Ruby's `Array` would count from the end. (CArray's `a[-1]` counts from the end too.)
|
|
407
|
+
- **An error raised at a cell** is raised when the pass ends, not at the cell. A computed subscript out of range has read cell 0, or skipped its write, in the meantime.
|
|
408
|
+
|
|
409
|
+
## 11. Where the answer differs from CArray's array operations
|
|
410
|
+
|
|
411
|
+
A block reads like Ruby run over one cell at a time, and it answers as that Ruby would. CArray's own operations on whole arrays answer some of the same expressions differently. Where the two disagree, the block takes Ruby's side.
|
|
412
|
+
|
|
413
|
+
| Expression | CArray on arrays | A block |
|
|
414
|
+
|---|---|---|
|
|
415
|
+
| float `x % 0.0` | NaN | `ZeroDivisionError`, as Ruby |
|
|
416
|
+
| float `-4.0 % 2.0` | `0.0` (the divisor's sign) | `-0.0` (the dividend's sign), as Ruby |
|
|
417
|
+
| `int8` cells: `a * 2 / 4` with `a = 100` | `-14`: each operation wraps at 8 bits | `50`: computed in `int64`, wrapped only when stored |
|
|
418
|
+
| `int8` + `uint8` | computed and returned in `uint8` | computed in `int64` |
|
|
419
|
+
| a uint8 cell `+ 300` | `RangeError`: 300 does not fit uint8 | `301`, computed in `int64` |
|
|
420
|
+
| `x.round`, `floor`, `ceil`, `truncate` | a float array | an Integer, as Ruby |
|
|
421
|
+
| boolean arithmetic, `b + b` | `2`: a boolean is 0/1 | refused, as Ruby refuses `true + true` |
|
|
422
|
+
| a boolean compared with a number, `b == 1` | compares as 0/1 | refused: in Ruby `true == 1` is false |
|
|
423
|
+
| storing a boolean into a numeric array | 0/1 | refused |
|
|
424
|
+
| `\|` and `&` of a masked boolean (`UNDEF \| true`) | Kleene: `true` | `\|\|` and `&&`: masked whenever an operand is |
|
|
425
|
+
| a `uint64` compared with a negative `int64` | compares by value: `7 < -7` is false | compared as two `uint64`: `7 < -7` is true |
|
|
426
|
+
|
|
427
|
+
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.
|