gisel 0.2.0__py3-none-any.whl
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.
- gisel/__init__.py +3 -0
- gisel/__main__.py +6 -0
- gisel/backend/__init__.py +0 -0
- gisel/backend/check_json.py +57 -0
- gisel/backend/core_json.py +192 -0
- gisel/backend/llvm_inc.py +495 -0
- gisel/backend/table.py +617 -0
- gisel/cli.py +238 -0
- gisel/data/generic_opcodes.json +290 -0
- gisel/dsl.py +1469 -0
- gisel/eval.py +1008 -0
- gisel/td.py +915 -0
- gisel/vocab.py +28 -0
- gisel-0.2.0.dist-info/METADATA +379 -0
- gisel-0.2.0.dist-info/RECORD +19 -0
- gisel-0.2.0.dist-info/WHEEL +5 -0
- gisel-0.2.0.dist-info/entry_points.txt +2 -0
- gisel-0.2.0.dist-info/licenses/LICENSE +279 -0
- gisel-0.2.0.dist-info/top_level.txt +1 -0
gisel/eval.py
ADDED
|
@@ -0,0 +1,1008 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Independent evaluator for the Core IR normal form (schema 0.4).
|
|
3
|
+
|
|
4
|
+
This is the Phase 2 reference implementation. It reads a
|
|
5
|
+
`legalizer-spec-core` JSON document (the `-gen-legalizer-core` normal form),
|
|
6
|
+
a per-configuration feature truth table, and a list of queries (one JSON
|
|
7
|
+
object per line, the same shape as the frozen AArch64 oracle dump), and
|
|
8
|
+
answers each query with the triple
|
|
9
|
+
|
|
10
|
+
(action, type_idx, new_type)
|
|
11
|
+
|
|
12
|
+
exactly as the hand-written C++ builder chain does. It does not import
|
|
13
|
+
TableGen, does not read the emitter, and does not parse any generated `.inc`.
|
|
14
|
+
|
|
15
|
+
Evaluation is first-match: rule sets in document order, rules inside a rule
|
|
16
|
+
set in `rule_index` order, the first rule whose effective guard is true wins.
|
|
17
|
+
|
|
18
|
+
The effective guard of a rule is `guard && shim_guard`, where `guard` is the
|
|
19
|
+
serialised `predicate` and `shim_guard` is the implicit size/count/vector test
|
|
20
|
+
the C++ shim adds for lattice operators (for example `widen slot pow2` only
|
|
21
|
+
fires when the slot is a scalar whose size is not a power of two). Terminal
|
|
22
|
+
actions and whole-type `replace` mutations carry no implicit guard.
|
|
23
|
+
|
|
24
|
+
An unknown feature string, predicate node, value node, target form, or action
|
|
25
|
+
raises `EvalError` instead of guessing, matching the deliberate-no-defaults
|
|
26
|
+
contract of the earlier 0.3 evaluator.
|
|
27
|
+
|
|
28
|
+
Usage:
|
|
29
|
+
python tools/core_eval_04.py \
|
|
30
|
+
--doc ~/legalizer-baseline/aarch64.core.json \
|
|
31
|
+
--features tests/aarch64-oracle/features.json \
|
|
32
|
+
--cases tests/aarch64-oracle/a64_ref_dump.jsonl
|
|
33
|
+
"""
|
|
34
|
+
import argparse
|
|
35
|
+
import json
|
|
36
|
+
import re
|
|
37
|
+
import sys
|
|
38
|
+
|
|
39
|
+
NO_MUTATION_ACTIONS = {"Legal", "Lower", "Libcall", "Custom", "Unsupported"}
|
|
40
|
+
SCALARIZE_ACTION = "FewerElements" # scalarize projects to FewerElements
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class EvalError(Exception):
|
|
44
|
+
def __init__(self, code, detail):
|
|
45
|
+
Exception.__init__(self, "%s: %s" % (code, detail))
|
|
46
|
+
self.code = code
|
|
47
|
+
self.detail = detail
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# ---------------------------------------------------------------------------
|
|
51
|
+
# LLT model
|
|
52
|
+
#
|
|
53
|
+
# LLTs are plain tuples; everything below is closed over this representation.
|
|
54
|
+
# ("scalar", bits) ANY_SCALAR
|
|
55
|
+
# ("integer", bits) INTEGER (extended LLT)
|
|
56
|
+
# ("float", bits) IEEE float, bits in {16,32,64,128}
|
|
57
|
+
# ("bf16",) bfloat16
|
|
58
|
+
# ("pointer", addrspace, bits) POINTER
|
|
59
|
+
# ("vector", lanes, scalable, elem) fixed or scalable vector
|
|
60
|
+
# ("invalid",) LLT_invalid
|
|
61
|
+
# ---------------------------------------------------------------------------
|
|
62
|
+
|
|
63
|
+
_FLOAT_BITS = {16, 32, 64, 128}
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def is_scalar(t):
|
|
67
|
+
"""LLT::isScalar(): ANY_SCALAR, INTEGER and FLOAT are all scalar."""
|
|
68
|
+
return t[0] in ("scalar", "integer", "float", "bf16")
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def is_integer(t):
|
|
72
|
+
return t[0] == "integer"
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def is_float(t):
|
|
76
|
+
return t[0] == "float" or t[0] == "bf16"
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def is_float_ieee(t):
|
|
80
|
+
return t[0] == "float"
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def is_bf16(t):
|
|
84
|
+
return t[0] == "bf16"
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def is_pointer(t):
|
|
88
|
+
return t[0] == "pointer"
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def is_vector(t):
|
|
92
|
+
return t[0] == "vector"
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def is_scalable(t):
|
|
96
|
+
return is_vector(t) and t[2]
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def is_fixed_vector(t):
|
|
100
|
+
return is_vector(t) and not t[2]
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def is_pointer_vector(t):
|
|
104
|
+
return is_vector(t) and t[3][0] == "pointer"
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def is_valid(t):
|
|
108
|
+
return t[0] != "invalid"
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def scalar_size(t):
|
|
112
|
+
"""getScalarSizeInBits: element size for vectors, scalar/pointer width else."""
|
|
113
|
+
if t[0] == "vector":
|
|
114
|
+
return scalar_size(t[3])
|
|
115
|
+
if t[0] == "pointer":
|
|
116
|
+
return t[2]
|
|
117
|
+
if t[0] == "bf16":
|
|
118
|
+
return 16
|
|
119
|
+
return t[1]
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def size_in_bits(t):
|
|
123
|
+
"""(known_min_bits, scalable) for getSizeInBits."""
|
|
124
|
+
if t[0] == "vector":
|
|
125
|
+
elt, scalable = size_in_bits(t[3])
|
|
126
|
+
return (elt * t[1], scalable or t[2])
|
|
127
|
+
if t[0] == "pointer":
|
|
128
|
+
return (t[2], False)
|
|
129
|
+
if t[0] == "bf16":
|
|
130
|
+
return (16, False)
|
|
131
|
+
return (t[1], False)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def size_in_bytes(t):
|
|
135
|
+
"""getSizeInBytes: (min + 7) // 8."""
|
|
136
|
+
bits, scalable = size_in_bits(t)
|
|
137
|
+
return ((bits + 7) // 8, scalable)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def num_elems(t):
|
|
141
|
+
# In the NDEBUG build the oracle runs in, getElementCount()'s
|
|
142
|
+
# isVector() assert is compiled out and a scalar/pointer stores
|
|
143
|
+
# ElementCount::getFixed(0), so getNumElements() returns 0 there.
|
|
144
|
+
if is_vector(t):
|
|
145
|
+
return t[1]
|
|
146
|
+
return 0
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def get_scalar_type(t):
|
|
150
|
+
return t[3] if is_vector(t) else t
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def get_element_type(t):
|
|
154
|
+
assert is_vector(t)
|
|
155
|
+
return t[3]
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def get_address_space(t):
|
|
159
|
+
assert t[0] == "pointer" or (is_vector(t) and t[3][0] == "pointer")
|
|
160
|
+
if is_vector(t):
|
|
161
|
+
return t[3][2]
|
|
162
|
+
return t[1]
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def change_element_type(t, new_elt):
|
|
166
|
+
"""changeElementType: vector keeps its lanes; scalar becomes new_elt."""
|
|
167
|
+
if is_vector(t):
|
|
168
|
+
return ("vector", t[1], t[2], new_elt)
|
|
169
|
+
return new_elt
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def change_element_size(t, new_size):
|
|
173
|
+
"""changeElementSize: invalid for pointers, keeps vector lanes."""
|
|
174
|
+
assert t[0] != "pointer" and not is_pointer_vector(t)
|
|
175
|
+
if is_vector(t):
|
|
176
|
+
return ("vector", t[1], t[2], change_element_size(t[3], new_size))
|
|
177
|
+
if is_integer(t):
|
|
178
|
+
return ("integer", new_size)
|
|
179
|
+
if is_float_ieee(t):
|
|
180
|
+
return ("float", new_size)
|
|
181
|
+
# bf16 and ANY_SCALAR both degrade to a plain scalar here, matching
|
|
182
|
+
# LLT::changeElementSize (isFloatIEEE() is false for bf16).
|
|
183
|
+
return ("scalar", new_size)
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def scalar_or_vector(lanes, scalable, elem):
|
|
187
|
+
"""LLT::scalarOrVector: one fixed lane is the element type itself."""
|
|
188
|
+
if lanes == 1 and not scalable:
|
|
189
|
+
return elem
|
|
190
|
+
return ("vector", lanes, scalable, elem)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def change_element_count_div(t, n):
|
|
194
|
+
"""divLanes: element count / n, keeping the scalable flag."""
|
|
195
|
+
assert is_vector(t)
|
|
196
|
+
return scalar_or_vector(t[1] // n, t[2], t[3])
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def fixed_vector(lanes, elem):
|
|
200
|
+
return ("vector", lanes, False, elem)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def scalable_vector(lanes, elem):
|
|
204
|
+
return ("vector", lanes, True, elem)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def integer_type(bits):
|
|
208
|
+
return ("integer", bits)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def pow2ceil(x):
|
|
212
|
+
# LLVM PowerOf2Ceil: 1 << ceil(log2(x)); for 0 returns 1.
|
|
213
|
+
if x <= 1:
|
|
214
|
+
return 1
|
|
215
|
+
p = 1
|
|
216
|
+
while p < x:
|
|
217
|
+
p <<= 1
|
|
218
|
+
return p
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def is_pow2_32(x):
|
|
222
|
+
return x != 0 and (x & (x - 1)) == 0
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
def align_to(x, n):
|
|
226
|
+
return ((x + n - 1) // n) * n
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
# ---------------------------------------------------------------------------
|
|
230
|
+
# Parsers
|
|
231
|
+
# ---------------------------------------------------------------------------
|
|
232
|
+
|
|
233
|
+
_CORE_VEC_RE = re.compile(r"^(nxv|v)(\d+)(.*)$")
|
|
234
|
+
_CORE_SCALAR_RE = re.compile(r"^(s|i|f)(\d+)$")
|
|
235
|
+
_CORE_PTR_RE = re.compile(r"^p(\d+):(\d+|\*)$")
|
|
236
|
+
_ORACLE_VEC_RE = re.compile(r"^<(\d+|vscale x \d+) x (.*)>$")
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def _parse_elem_spelling(s, oracle):
|
|
240
|
+
if s == "bf16":
|
|
241
|
+
return ("bf16",)
|
|
242
|
+
m = _CORE_SCALAR_RE.match(s)
|
|
243
|
+
if m:
|
|
244
|
+
kind, bits = m.group(1), int(m.group(2))
|
|
245
|
+
if kind == "s":
|
|
246
|
+
return ("scalar", bits)
|
|
247
|
+
if kind == "i":
|
|
248
|
+
return ("integer", bits)
|
|
249
|
+
return ("float", bits)
|
|
250
|
+
if not oracle:
|
|
251
|
+
m = _CORE_PTR_RE.match(s)
|
|
252
|
+
if m:
|
|
253
|
+
w = m.group(2)
|
|
254
|
+
return ("pointer", int(m.group(1)), int(w) if w != "*" else "*")
|
|
255
|
+
else:
|
|
256
|
+
m = re.match(r"^p(\d+)$", s)
|
|
257
|
+
if m:
|
|
258
|
+
# The frozen oracle was produced with PointerBits=64.
|
|
259
|
+
return ("pointer", int(m.group(1)), 64)
|
|
260
|
+
raise EvalError("bad_llt", "cannot parse LLT element %r" % (s,))
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def parse_core_llt(s):
|
|
264
|
+
"""Canonical Core IR spelling: s32, i32, f32, bf16, p0:64, v4s32, nxv4s32."""
|
|
265
|
+
if s == "bf16":
|
|
266
|
+
return ("bf16",)
|
|
267
|
+
m = _CORE_VEC_RE.match(s)
|
|
268
|
+
if m:
|
|
269
|
+
lanes = int(m.group(2))
|
|
270
|
+
scalable = m.group(1) == "nxv"
|
|
271
|
+
elem = _parse_elem_spelling(m.group(3), oracle=False)
|
|
272
|
+
return ("vector", lanes, scalable, elem)
|
|
273
|
+
m = _CORE_PTR_RE.match(s)
|
|
274
|
+
if m:
|
|
275
|
+
w = m.group(2)
|
|
276
|
+
return ("pointer", int(m.group(1)), int(w) if w != "*" else "*")
|
|
277
|
+
return _parse_elem_spelling(s, oracle=False)
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def parse_oracle_llt(s):
|
|
281
|
+
"""LLT::print spelling: s32, i32, f32, bf16, p0, <4 x s32>, <vscale x 4 x s32>."""
|
|
282
|
+
if s == "LLT_invalid":
|
|
283
|
+
return ("invalid",)
|
|
284
|
+
if s == "bf16":
|
|
285
|
+
return ("bf16",)
|
|
286
|
+
m = _ORACLE_VEC_RE.match(s)
|
|
287
|
+
if m:
|
|
288
|
+
head, elem = m.group(1), m.group(2)
|
|
289
|
+
if head.startswith("vscale"):
|
|
290
|
+
lanes = int(head.split()[2])
|
|
291
|
+
scalable = True
|
|
292
|
+
else:
|
|
293
|
+
lanes = int(head)
|
|
294
|
+
scalable = False
|
|
295
|
+
return ("vector", lanes, scalable, _parse_elem_spelling(elem, oracle=True))
|
|
296
|
+
return _parse_elem_spelling(s, oracle=True)
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def parse_case_llt(s):
|
|
300
|
+
"""A frozen-case type: Core IR spelling or LLT::print spelling.
|
|
301
|
+
|
|
302
|
+
Our own oracle dumps write `p0:32`/`v4s32` (kind and pointer width are
|
|
303
|
+
explicit); the older AArch64 dump writes LLT::print (`<4 x s32>`, `p0`).
|
|
304
|
+
"""
|
|
305
|
+
if s == "LLT_invalid":
|
|
306
|
+
return ("invalid",)
|
|
307
|
+
if s.startswith("<"):
|
|
308
|
+
return parse_oracle_llt(s)
|
|
309
|
+
try:
|
|
310
|
+
return parse_core_llt(s)
|
|
311
|
+
except EvalError:
|
|
312
|
+
return parse_oracle_llt(s) # `p0`: LLT::print drops the pointer width
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def _is_scalar_like(t):
|
|
316
|
+
"""LLT::isScalar(): ANY_SCALAR, INTEGER and FLOAT are all 'scalar'."""
|
|
317
|
+
return t[0] in ("scalar", "integer", "float", "bf16")
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def has_wildcard(t):
|
|
321
|
+
"""A pointer whose width the configuration decides (`p0:*`)."""
|
|
322
|
+
if t[0] == "pointer":
|
|
323
|
+
return t[2] == "*"
|
|
324
|
+
if t[0] == "vector":
|
|
325
|
+
return has_wildcard(t[3])
|
|
326
|
+
return False
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
def llt_eq(a, b):
|
|
330
|
+
"""LLT equality; a `p<as>:*` element matches any width (see core JSON)."""
|
|
331
|
+
"""LLT operator==, including the ANY_SCALAR bag-of-bits rule.
|
|
332
|
+
|
|
333
|
+
If either side is ANY_SCALAR the comparison is `isScalar() && same
|
|
334
|
+
scalar size`, so s32 == i32 == f32 while i32 != f32. Vector equality
|
|
335
|
+
recurses through element types, so <8 x s8> == v8i8.
|
|
336
|
+
"""
|
|
337
|
+
if a == b:
|
|
338
|
+
return True
|
|
339
|
+
if a[0] == "pointer" and b[0] == "pointer":
|
|
340
|
+
return a[1] == b[1] and (a[2] == "*" or b[2] == "*" or a[2] == b[2])
|
|
341
|
+
if a[0] == "scalar" or b[0] == "scalar":
|
|
342
|
+
return (_is_scalar_like(a) and _is_scalar_like(b)
|
|
343
|
+
and scalar_size(a) == scalar_size(b))
|
|
344
|
+
if is_vector(a) and is_vector(b):
|
|
345
|
+
return (a[1] == b[1] and a[2] == b[2]
|
|
346
|
+
and llt_eq(a[3], b[3]))
|
|
347
|
+
return False
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
def values_equal(a, b):
|
|
351
|
+
"""Compare two Core IR values: LLTs use LLT operator==, rest use ==."""
|
|
352
|
+
if isinstance(a, tuple) and isinstance(b, tuple):
|
|
353
|
+
return llt_eq(a, b)
|
|
354
|
+
return a == b
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
def fmt_oracle_llt(t):
|
|
358
|
+
if not is_valid(t):
|
|
359
|
+
return "LLT_invalid"
|
|
360
|
+
if is_vector(t):
|
|
361
|
+
if t[2]:
|
|
362
|
+
return "<vscale x %d x %s>" % (t[1], fmt_oracle_llt(t[3]))
|
|
363
|
+
return "<%d x %s>" % (t[1], fmt_oracle_llt(t[3]))
|
|
364
|
+
if t[0] == "pointer":
|
|
365
|
+
return "p%d" % t[1]
|
|
366
|
+
if t[0] == "bf16":
|
|
367
|
+
return "bf16"
|
|
368
|
+
if t[0] == "float":
|
|
369
|
+
return "f%d" % t[1]
|
|
370
|
+
if t[0] == "integer":
|
|
371
|
+
return "i%d" % t[1]
|
|
372
|
+
if t[0] == "scalar":
|
|
373
|
+
return "s%d" % t[1]
|
|
374
|
+
return "LLT_invalid"
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
# ---------------------------------------------------------------------------
|
|
378
|
+
# Atomic ordering lattice (matching llvm/Support/AtomicOrdering.h)
|
|
379
|
+
# ---------------------------------------------------------------------------
|
|
380
|
+
|
|
381
|
+
_ORDERING_INDEX = {
|
|
382
|
+
"NotAtomic": 0, "Unordered": 1, "Monotonic": 2, "Acquire": 4,
|
|
383
|
+
"Release": 5, "AcquireRelease": 6, "SequentiallyConsistent": 7,
|
|
384
|
+
}
|
|
385
|
+
_ORDERING_FROM_TAG = {
|
|
386
|
+
"orderingUnordered": "Unordered",
|
|
387
|
+
"orderingMonotonic": "Monotonic",
|
|
388
|
+
"orderingAcquire": "Acquire",
|
|
389
|
+
"orderingRelease": "Release",
|
|
390
|
+
"orderingAcquireRelease": "AcquireRelease",
|
|
391
|
+
"orderingSequentiallyConsistent": "SequentiallyConsistent",
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
_STRONGER = {
|
|
395
|
+
0: [False] * 8,
|
|
396
|
+
1: [True, False, False, False, False, False, False, False],
|
|
397
|
+
2: [True, True, False, False, False, False, False, False],
|
|
398
|
+
4: [True, True, True, True, False, False, False, False],
|
|
399
|
+
5: [True, True, True, False, False, False, False, False],
|
|
400
|
+
6: [True, True, True, True, True, True, False, False],
|
|
401
|
+
7: [True, True, True, True, True, True, True, False],
|
|
402
|
+
}
|
|
403
|
+
_AT_LEAST = {
|
|
404
|
+
0: [True, False, False, False, False, False, False, False],
|
|
405
|
+
1: [True, True, False, False, False, False, False, False],
|
|
406
|
+
2: [True, True, True, False, False, False, False, False],
|
|
407
|
+
4: [True, True, True, True, True, False, False, False],
|
|
408
|
+
5: [True, True, True, False, False, True, False, False],
|
|
409
|
+
6: [True, True, True, True, True, True, True, False],
|
|
410
|
+
7: [True] * 8,
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
def is_stronger_than(a, b):
|
|
415
|
+
return _STRONGER[a][b]
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
def is_at_least_or_stronger_than(a, b):
|
|
419
|
+
return _AT_LEAST[a][b]
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
# ---------------------------------------------------------------------------
|
|
423
|
+
# Feature truth table
|
|
424
|
+
# ---------------------------------------------------------------------------
|
|
425
|
+
|
|
426
|
+
class Features:
|
|
427
|
+
def __init__(self, table):
|
|
428
|
+
self.table = table # config -> {feature_string: bool}
|
|
429
|
+
|
|
430
|
+
def get(self, config, feat):
|
|
431
|
+
row = self.table.get(config)
|
|
432
|
+
if row is None:
|
|
433
|
+
raise EvalError("unknown_config", "no feature row for %r" % (config,))
|
|
434
|
+
if feat not in row:
|
|
435
|
+
raise EvalError("unknown_feature", "no truth value for %r in %r" % (feat, config))
|
|
436
|
+
return row[feat]
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
# ---------------------------------------------------------------------------
|
|
440
|
+
# Document / query model
|
|
441
|
+
# ---------------------------------------------------------------------------
|
|
442
|
+
|
|
443
|
+
class MemDesc:
|
|
444
|
+
__slots__ = ("memory_ty", "align", "ordering", "failure_ordering")
|
|
445
|
+
|
|
446
|
+
def __init__(self, memory_ty, align, ordering="NotAtomic",
|
|
447
|
+
failure_ordering="NotAtomic"):
|
|
448
|
+
self.memory_ty = memory_ty
|
|
449
|
+
self.align = align
|
|
450
|
+
self.ordering = ordering
|
|
451
|
+
self.failure_ordering = failure_ordering
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
class Evaluator:
|
|
455
|
+
def __init__(self, doc, features):
|
|
456
|
+
self.features = features
|
|
457
|
+
self.rulesets = doc["rulesets"]
|
|
458
|
+
self.by_opcode = {}
|
|
459
|
+
for rs_idx, rs in enumerate(self.rulesets):
|
|
460
|
+
for op in rs["opcodes"]:
|
|
461
|
+
self.by_opcode.setdefault(op, []).append(rs_idx)
|
|
462
|
+
|
|
463
|
+
# -- value nodes -------------------------------------------------------
|
|
464
|
+
def value(self, node, types, mems, config):
|
|
465
|
+
k = node["kind"]
|
|
466
|
+
a = node.get("args", [])
|
|
467
|
+
if k == "int":
|
|
468
|
+
return node["value"]
|
|
469
|
+
if k == "type":
|
|
470
|
+
return self.type_value(node["value"], types, mems, config)
|
|
471
|
+
if k == "slot":
|
|
472
|
+
return types[node["index"]]
|
|
473
|
+
if k == "memSlot":
|
|
474
|
+
return mems[a[0]["value"]]
|
|
475
|
+
if k == "elemOf":
|
|
476
|
+
return get_element_type(self.value(a[0], types, mems, config))
|
|
477
|
+
if k == "scalarOf":
|
|
478
|
+
return get_scalar_type(self.value(a[0], types, mems, config))
|
|
479
|
+
if k == "numElems":
|
|
480
|
+
return num_elems(self.value(a[0], types, mems, config))
|
|
481
|
+
if k == "bitsOf":
|
|
482
|
+
return scalar_size(self.value(a[0], types, mems, config))
|
|
483
|
+
if k == "sizeOf":
|
|
484
|
+
arg = a[0]
|
|
485
|
+
if arg.get("kind") == "memSlot":
|
|
486
|
+
m = self.value(arg, types, mems, config)
|
|
487
|
+
return size_in_bits(m.memory_ty)[0]
|
|
488
|
+
return size_in_bits(self.value(arg, types, mems, config))[0]
|
|
489
|
+
if k == "sizeInBytesOf":
|
|
490
|
+
return size_in_bytes(self.value(a[0], types, mems, config))[0]
|
|
491
|
+
if k == "alignOf":
|
|
492
|
+
return self.value(a[0], types, mems, config).align
|
|
493
|
+
if k == "addrSpace":
|
|
494
|
+
return get_address_space(self.value(a[0], types, mems, config))
|
|
495
|
+
if k == "memTy":
|
|
496
|
+
return self.value(a[0], types, mems, config).memory_ty
|
|
497
|
+
if k == "memOrdering":
|
|
498
|
+
return self.value(a[0], types, mems, config).ordering
|
|
499
|
+
if k == "memFailureOrdering":
|
|
500
|
+
return self.value(a[0], types, mems, config).failure_ordering
|
|
501
|
+
if k == "ty":
|
|
502
|
+
return self.value(a[0], types, mems, config)
|
|
503
|
+
if k == "sameAs":
|
|
504
|
+
return types[a[0]["value"]]
|
|
505
|
+
if k == "select":
|
|
506
|
+
if self.bool_value(a[0], types, mems, config):
|
|
507
|
+
return self.value(a[1], types, mems, config)
|
|
508
|
+
return self.value(a[2], types, mems, config)
|
|
509
|
+
if k == "mul":
|
|
510
|
+
return self.int_value(a[0], types, mems, config) * self.int_value(a[1], types, mems, config)
|
|
511
|
+
if k == "urem":
|
|
512
|
+
return self.int_value(a[0], types, mems, config) % self.int_value(a[1], types, mems, config)
|
|
513
|
+
if k == "udiv":
|
|
514
|
+
return self.int_value(a[0], types, mems, config) // self.int_value(a[1], types, mems, config)
|
|
515
|
+
if k == "pow2Ceil":
|
|
516
|
+
return pow2ceil(self.int_value(a[0], types, mems, config))
|
|
517
|
+
if k == "intOfSize":
|
|
518
|
+
return integer_type(self.int_value(a[0], types, mems, config))
|
|
519
|
+
if k == "vecOf":
|
|
520
|
+
return fixed_vector(self.int_value(a[0], types, mems, config),
|
|
521
|
+
self.llt_value(a[1], types, mems, config))
|
|
522
|
+
if k == "scalarOrVecOf":
|
|
523
|
+
return scalar_or_vector(self.int_value(a[0], types, mems, config),
|
|
524
|
+
False,
|
|
525
|
+
self.llt_value(a[1], types, mems, config))
|
|
526
|
+
if k == "eltTypeTo":
|
|
527
|
+
return change_element_type(self.llt_value(a[0], types, mems, config),
|
|
528
|
+
self.llt_value(a[1], types, mems, config))
|
|
529
|
+
if k == "eltSizeTo":
|
|
530
|
+
return change_element_size(self.llt_value(a[0], types, mems, config),
|
|
531
|
+
self.int_value(a[1], types, mems, config))
|
|
532
|
+
if k == "divLanes":
|
|
533
|
+
return change_element_count_div(self.llt_value(a[0], types, mems, config),
|
|
534
|
+
self.int_value(a[1], types, mems, config))
|
|
535
|
+
if k == "replace":
|
|
536
|
+
return self.value(a[0], types, mems, config)
|
|
537
|
+
raise EvalError("unknown_value", "value node %r" % (k,))
|
|
538
|
+
|
|
539
|
+
def type_value(self, v, types, mems, config):
|
|
540
|
+
"""A type-valued node: an LLT spelling, or a feature-selected pair.
|
|
541
|
+
|
|
542
|
+
The core JSON writes a subtarget-dependent type as
|
|
543
|
+
``{"kind":"type","value":{"select":{...}}}`` (emitter ``coreTypeValue``);
|
|
544
|
+
the branch is resolved here, not in the document.
|
|
545
|
+
"""
|
|
546
|
+
if isinstance(v, str):
|
|
547
|
+
return parse_core_llt(v)
|
|
548
|
+
sel = v["select"]
|
|
549
|
+
if self.bool_value(sel["cond"], types, mems, config):
|
|
550
|
+
return self.type_value(sel["then"], types, mems, config)
|
|
551
|
+
return self.type_value(sel["else"], types, mems, config)
|
|
552
|
+
|
|
553
|
+
def int_value(self, node, types, mems, config):
|
|
554
|
+
v = self.value(node, types, mems, config)
|
|
555
|
+
if isinstance(v, bool) or not isinstance(v, int):
|
|
556
|
+
raise EvalError("bad_int", "node %r is not an Int" % (node.get("kind"),))
|
|
557
|
+
return v
|
|
558
|
+
|
|
559
|
+
def llt_value(self, node, types, mems, config):
|
|
560
|
+
v = self.value(node, types, mems, config)
|
|
561
|
+
if not isinstance(v, tuple):
|
|
562
|
+
raise EvalError("bad_llt", "node %r is not an LLT" % (node.get("kind"),))
|
|
563
|
+
return v
|
|
564
|
+
|
|
565
|
+
# -- boolean nodes -----------------------------------------------------
|
|
566
|
+
def bool_value(self, node, types, mems, config):
|
|
567
|
+
k = node["kind"]
|
|
568
|
+
a = node.get("args", [])
|
|
569
|
+
if k == "leaf":
|
|
570
|
+
name = node.get("name")
|
|
571
|
+
if name == "true":
|
|
572
|
+
return True
|
|
573
|
+
if name == "hasFeature":
|
|
574
|
+
return self.features.get(config, a[0])
|
|
575
|
+
raise EvalError("unknown_leaf", "leaf %r" % (name,))
|
|
576
|
+
if k == "true":
|
|
577
|
+
return True
|
|
578
|
+
if k == "all":
|
|
579
|
+
return all(self.bool_value(c, types, mems, config) for c in a)
|
|
580
|
+
if k == "any":
|
|
581
|
+
return any(self.bool_value(c, types, mems, config) for c in a)
|
|
582
|
+
if k == "not":
|
|
583
|
+
# schema-0.4 writes `{"kind":"not","arg":...}` for a negated DAG
|
|
584
|
+
# predicate and `args` for a CoreNot node; accept both.
|
|
585
|
+
child = a[0] if a else node["arg"]
|
|
586
|
+
return not self.bool_value(child, types, mems, config)
|
|
587
|
+
if k == "nary":
|
|
588
|
+
# `{"kind":"nary","op":"all_of"|"any_of","args":[...]}`: the
|
|
589
|
+
# DAG-shaped spelling the emitter uses inside select guards.
|
|
590
|
+
op = node.get("op")
|
|
591
|
+
if op == "all_of":
|
|
592
|
+
return all(self.bool_value(c, types, mems, config) for c in a)
|
|
593
|
+
if op == "any_of":
|
|
594
|
+
return any(self.bool_value(c, types, mems, config) for c in a)
|
|
595
|
+
raise EvalError("unknown_nary", "nary op %r" % (op,))
|
|
596
|
+
if k == "isPow2":
|
|
597
|
+
return is_pow2_32(self.int_value(a[0], types, mems, config))
|
|
598
|
+
if k == "isByteSized":
|
|
599
|
+
bits, _ = size_in_bits(self.llt_value(a[0], types, mems, config))
|
|
600
|
+
return bits % 8 == 0
|
|
601
|
+
if k == "isBFloat16":
|
|
602
|
+
return is_bf16(self.llt_value(a[0], types, mems, config))
|
|
603
|
+
if k == "isFloat16":
|
|
604
|
+
t = self.llt_value(a[0], types, mems, config)
|
|
605
|
+
return t[0] == "float" and t[1] == 16
|
|
606
|
+
if k in ("in", "inPairs", "inTriples", "inMemDesc"):
|
|
607
|
+
return self.membership(node, types, mems, config)
|
|
608
|
+
if k in ("eq", "ne", "lt", "le", "gt", "ge"):
|
|
609
|
+
return self.compare(node, types, mems, config)
|
|
610
|
+
raise EvalError("unknown_bool", "bool node %r" % (k,))
|
|
611
|
+
|
|
612
|
+
# -- membership --------------------------------------------------------
|
|
613
|
+
def _slot_index(self, node):
|
|
614
|
+
if node.get("kind") == "slot":
|
|
615
|
+
return node["index"]
|
|
616
|
+
raise EvalError("bad_slot", "tuple membership needs a literal slot")
|
|
617
|
+
|
|
618
|
+
def membership(self, node, types, mems, config):
|
|
619
|
+
k = node["kind"]
|
|
620
|
+
a = node["args"]
|
|
621
|
+
if k == "inMemDesc":
|
|
622
|
+
i0 = self._slot_index(a[0])
|
|
623
|
+
i1 = self._slot_index(a[1])
|
|
624
|
+
mnode = a[2]
|
|
625
|
+
if mnode.get("kind") != "memSlot":
|
|
626
|
+
raise EvalError("bad_memslot", "inMemDesc third operand")
|
|
627
|
+
m = mems[mnode["args"][0]["value"]]
|
|
628
|
+
elems = a[3]["elems"]
|
|
629
|
+
for e in elems:
|
|
630
|
+
t0 = self.type_value(e[0], types, mems, config)
|
|
631
|
+
t1 = self.type_value(e[1], types, mems, config)
|
|
632
|
+
mt = self.type_value(e[2], types, mems, config)
|
|
633
|
+
align = e[3]
|
|
634
|
+
size_ok = (llt_eq(m.memory_ty, mt) if has_wildcard(mt)
|
|
635
|
+
else size_in_bits(m.memory_ty)[0] == size_in_bits(mt)[0])
|
|
636
|
+
if (llt_eq(types[i0], t0) and llt_eq(types[i1], t1)
|
|
637
|
+
and m.align >= align and size_ok):
|
|
638
|
+
return True
|
|
639
|
+
return False
|
|
640
|
+
|
|
641
|
+
lhs = a[0]
|
|
642
|
+
rhs = a[-1]
|
|
643
|
+
nslots = len(a) - 1
|
|
644
|
+
|
|
645
|
+
if k == "inPairs":
|
|
646
|
+
i0 = self._slot_index(a[0]); i1 = self._slot_index(a[1])
|
|
647
|
+
arity = rhs.get("arity", 2)
|
|
648
|
+
for e in rhs["elems"]:
|
|
649
|
+
if (llt_eq(types[i0], self.type_value(e[0], types, mems, config))
|
|
650
|
+
and llt_eq(types[i1], self.type_value(e[1], types, mems, config))):
|
|
651
|
+
return True
|
|
652
|
+
return False
|
|
653
|
+
if k == "inTriples":
|
|
654
|
+
i0 = self._slot_index(a[0]); i1 = self._slot_index(a[1]); i2 = self._slot_index(a[2])
|
|
655
|
+
for e in rhs["elems"]:
|
|
656
|
+
if (llt_eq(types[i0], self.type_value(e[0], types, mems, config))
|
|
657
|
+
and llt_eq(types[i1], self.type_value(e[1], types, mems, config))
|
|
658
|
+
and llt_eq(types[i2], self.type_value(e[2], types, mems, config))):
|
|
659
|
+
return True
|
|
660
|
+
return False
|
|
661
|
+
|
|
662
|
+
# one-slot `in`
|
|
663
|
+
if rhs.get("kind") == "clamp":
|
|
664
|
+
v = self.int_value(lhs, types, mems, config)
|
|
665
|
+
lo = rhs["args"][0]["value"]
|
|
666
|
+
hi = rhs["args"][1]["value"]
|
|
667
|
+
return lo <= v <= hi
|
|
668
|
+
if rhs.get("kind") == "type":
|
|
669
|
+
return llt_eq(self.llt_value(lhs, types, mems, config),
|
|
670
|
+
self.type_value(rhs["value"], types, mems, config))
|
|
671
|
+
if rhs.get("kind") == "set" and rhs.get("of") == "Type":
|
|
672
|
+
v = self.llt_value(lhs, types, mems, config)
|
|
673
|
+
for e in rhs["elems"]:
|
|
674
|
+
if llt_eq(v, self.type_value(e, types, mems, config)):
|
|
675
|
+
return True
|
|
676
|
+
return False
|
|
677
|
+
if rhs.get("kind") == "set" and rhs.get("of") == "Kind":
|
|
678
|
+
# Lhs must be SortOf; the emitter lowers it to a disjunction of
|
|
679
|
+
# kind predicates over the SortOf operand.
|
|
680
|
+
if lhs.get("kind") != "sortOf":
|
|
681
|
+
raise EvalError("bad_kind_set", "Kind set can only test SortOf")
|
|
682
|
+
t = self.llt_value(lhs["args"][0], types, mems, config)
|
|
683
|
+
for e in rhs["elems"]:
|
|
684
|
+
if _kind_predicate(e, t):
|
|
685
|
+
return True
|
|
686
|
+
return False
|
|
687
|
+
raise EvalError("unknown_set", "set %r" % (rhs.get("kind"),))
|
|
688
|
+
|
|
689
|
+
# -- comparisons -------------------------------------------------------
|
|
690
|
+
def compare(self, node, types, mems, config):
|
|
691
|
+
k = node["kind"]
|
|
692
|
+
lhs = node["args"][0]
|
|
693
|
+
rhs = node["args"][1]
|
|
694
|
+
if lhs.get("kind") == "sortOf":
|
|
695
|
+
t = self.llt_value(lhs["args"][0], types, mems, config)
|
|
696
|
+
pred = _kind_predicate(rhs.get("kind"), t)
|
|
697
|
+
return (not pred) if k == "ne" else pred
|
|
698
|
+
if lhs.get("kind") in ("memOrdering", "memFailureOrdering"):
|
|
699
|
+
v = self.value(lhs, types, mems, config)
|
|
700
|
+
o = _ORDERING_FROM_TAG.get(rhs.get("kind"))
|
|
701
|
+
if o is None:
|
|
702
|
+
raise EvalError("bad_ordering", "not an ordering tag: %r" % (rhs.get("kind"),))
|
|
703
|
+
vi = _ORDERING_INDEX[v]
|
|
704
|
+
oi = _ORDERING_INDEX[o]
|
|
705
|
+
if k == "eq":
|
|
706
|
+
return vi == oi
|
|
707
|
+
if k == "ne":
|
|
708
|
+
return vi != oi
|
|
709
|
+
if k == "ge":
|
|
710
|
+
return is_at_least_or_stronger_than(vi, oi)
|
|
711
|
+
if k == "gt":
|
|
712
|
+
return is_stronger_than(vi, oi)
|
|
713
|
+
if k == "le":
|
|
714
|
+
return not is_stronger_than(vi, oi)
|
|
715
|
+
if k == "lt":
|
|
716
|
+
return not is_at_least_or_stronger_than(vi, oi)
|
|
717
|
+
raise EvalError("bad_ordering_cmp", k)
|
|
718
|
+
lv = self.value(lhs, types, mems, config)
|
|
719
|
+
rv = self.value(rhs, types, mems, config)
|
|
720
|
+
if k == "eq":
|
|
721
|
+
return values_equal(lv, rv)
|
|
722
|
+
if k == "ne":
|
|
723
|
+
return not values_equal(lv, rv)
|
|
724
|
+
if k == "lt":
|
|
725
|
+
return lv < rv
|
|
726
|
+
if k == "le":
|
|
727
|
+
return lv <= rv
|
|
728
|
+
if k == "gt":
|
|
729
|
+
return lv > rv
|
|
730
|
+
return lv >= rv
|
|
731
|
+
|
|
732
|
+
|
|
733
|
+
def _kind_predicate(tag, t):
|
|
734
|
+
"""SortOf tag -> LLT kind predicate."""
|
|
735
|
+
if tag == "kindScalar":
|
|
736
|
+
return is_scalar(t)
|
|
737
|
+
if tag == "kindVector":
|
|
738
|
+
return is_vector(t)
|
|
739
|
+
if tag == "kindScalableVector":
|
|
740
|
+
return is_scalable(t)
|
|
741
|
+
if tag == "kindPointer":
|
|
742
|
+
return is_pointer(t)
|
|
743
|
+
if tag == "kindPointerVector":
|
|
744
|
+
return is_pointer_vector(t)
|
|
745
|
+
raise EvalError("bad_kind", "kind tag %r" % (tag,))
|
|
746
|
+
|
|
747
|
+
|
|
748
|
+
# ---------------------------------------------------------------------------
|
|
749
|
+
# Shim: implicit guard + new_type for (op, on-form, to-form)
|
|
750
|
+
# ---------------------------------------------------------------------------
|
|
751
|
+
|
|
752
|
+
def _slot_of(on_node):
|
|
753
|
+
if on_node.get("kind") == "slot":
|
|
754
|
+
return on_node["index"]
|
|
755
|
+
if on_node.get("kind") == "elemOf":
|
|
756
|
+
inner = on_node["args"][0]
|
|
757
|
+
if inner.get("kind") != "slot":
|
|
758
|
+
raise EvalError("bad_on", "nested ElemOf has no type-index projection")
|
|
759
|
+
return inner["index"]
|
|
760
|
+
raise EvalError("bad_on", "observation is not a slot/elemOf")
|
|
761
|
+
|
|
762
|
+
|
|
763
|
+
def shim_guard_and_newtype(op, on_node, to_node, types, mems, ev, config):
|
|
764
|
+
"""Return (implicit_guard, new_type) for a matched rule.
|
|
765
|
+
|
|
766
|
+
`new_type` is an LLT tuple; ("invalid",) means "not mutated". When the
|
|
767
|
+
implicit guard is false the mutation is deliberately not computed, exactly
|
|
768
|
+
as the C++ shim's `actionIf` skips the mutation lambda for a query that
|
|
769
|
+
fails its size/count test.
|
|
770
|
+
"""
|
|
771
|
+
if on_node is None:
|
|
772
|
+
# Terminal action: guard is the whole rule.
|
|
773
|
+
return True, ("invalid",)
|
|
774
|
+
idx = _slot_of(on_node)
|
|
775
|
+
on_form = "elem" if on_node.get("kind") == "elemOf" else "slot"
|
|
776
|
+
|
|
777
|
+
if to_node is None:
|
|
778
|
+
# scalarize
|
|
779
|
+
t = types[idx]
|
|
780
|
+
if not is_vector(t):
|
|
781
|
+
return False, ("invalid",)
|
|
782
|
+
return True, get_element_type(t)
|
|
783
|
+
|
|
784
|
+
if to_node.get("kind") == "int":
|
|
785
|
+
to_form = "int"
|
|
786
|
+
to_int = to_node["value"]
|
|
787
|
+
else:
|
|
788
|
+
to_form = to_node["kind"]
|
|
789
|
+
|
|
790
|
+
t = types[idx]
|
|
791
|
+
ssz = scalar_size(t)
|
|
792
|
+
size, _ = size_in_bits(t)
|
|
793
|
+
|
|
794
|
+
# Whole-type mutation: the target is the rule's own type expression.
|
|
795
|
+
if to_form == "replace":
|
|
796
|
+
return True, ev.llt_value(to_node["args"][0], types, mems, config)
|
|
797
|
+
|
|
798
|
+
if op == "widen":
|
|
799
|
+
if on_form == "slot" and to_form in ("select", "int"):
|
|
800
|
+
minsz = ev.int_value(to_node, types, mems, config)
|
|
801
|
+
guard = (ssz < minsz) or (not is_pow2_32(ssz))
|
|
802
|
+
if not guard:
|
|
803
|
+
return False, ("invalid",)
|
|
804
|
+
return True, change_element_size(t, max(pow2ceil(ssz), minsz))
|
|
805
|
+
if to_form == "pow2":
|
|
806
|
+
if on_form == "slot":
|
|
807
|
+
guard = is_scalar(t) and not is_pow2_32(size)
|
|
808
|
+
else:
|
|
809
|
+
guard = not is_pow2_32(ssz)
|
|
810
|
+
if not guard:
|
|
811
|
+
return False, ("invalid",)
|
|
812
|
+
return True, change_element_size(t, pow2ceil(ssz))
|
|
813
|
+
if to_form == "pow2Min":
|
|
814
|
+
n = to_node["args"][0]["value"]
|
|
815
|
+
if on_form == "slot":
|
|
816
|
+
guard = is_scalar(t) and not is_pow2_32(size)
|
|
817
|
+
else:
|
|
818
|
+
guard = not is_pow2_32(ssz)
|
|
819
|
+
if not guard:
|
|
820
|
+
return False, ("invalid",)
|
|
821
|
+
return True, change_element_size(t, max(pow2ceil(ssz), n))
|
|
822
|
+
if to_form == "multipleOf":
|
|
823
|
+
n = to_node["args"][0]["value"]
|
|
824
|
+
if on_form == "slot":
|
|
825
|
+
guard = is_scalar(t) and (size % n != 0)
|
|
826
|
+
else:
|
|
827
|
+
guard = ssz % n != 0
|
|
828
|
+
if not guard:
|
|
829
|
+
return False, ("invalid",)
|
|
830
|
+
return True, change_element_size(t, align_to(ssz, n))
|
|
831
|
+
if to_form == "ty":
|
|
832
|
+
target = ev.llt_value(to_node["args"][0], types, mems, config)
|
|
833
|
+
tsz = scalar_size(target)
|
|
834
|
+
if on_form == "slot":
|
|
835
|
+
guard = is_scalar(t) and size < tsz
|
|
836
|
+
else:
|
|
837
|
+
guard = ssz < tsz
|
|
838
|
+
if not guard:
|
|
839
|
+
return False, ("invalid",)
|
|
840
|
+
return True, change_element_size(t, tsz)
|
|
841
|
+
if to_form == "sameAs":
|
|
842
|
+
k = to_node["args"][0]["value"]
|
|
843
|
+
other = scalar_size(types[k])
|
|
844
|
+
guard = other > ssz
|
|
845
|
+
if not guard:
|
|
846
|
+
return False, ("invalid",)
|
|
847
|
+
return True, change_element_size(t, other)
|
|
848
|
+
if to_form == "minVectorSize":
|
|
849
|
+
n = to_node["args"][0]["value"]
|
|
850
|
+
if not (is_fixed_vector(t) and size < n):
|
|
851
|
+
return False, ("invalid",)
|
|
852
|
+
num = num_elems(t)
|
|
853
|
+
new_elt = change_element_size(get_element_type(t), n // num)
|
|
854
|
+
return True, fixed_vector(num, new_elt)
|
|
855
|
+
raise EvalError("unknown_shim", "%s %s %s" % (op, on_form, to_form))
|
|
856
|
+
|
|
857
|
+
if op == "narrow":
|
|
858
|
+
if to_form == "ty":
|
|
859
|
+
target = ev.llt_value(to_node["args"][0], types, mems, config)
|
|
860
|
+
tsz = scalar_size(target)
|
|
861
|
+
if on_form == "slot":
|
|
862
|
+
guard = is_scalar(t) and size > tsz
|
|
863
|
+
else:
|
|
864
|
+
guard = ssz > tsz
|
|
865
|
+
if not guard:
|
|
866
|
+
return False, ("invalid",)
|
|
867
|
+
return True, change_element_size(t, tsz)
|
|
868
|
+
if to_form == "sameAs":
|
|
869
|
+
k = to_node["args"][0]["value"]
|
|
870
|
+
other = scalar_size(types[k])
|
|
871
|
+
guard = other < ssz
|
|
872
|
+
if not guard:
|
|
873
|
+
return False, ("invalid",)
|
|
874
|
+
return True, change_element_size(t, other)
|
|
875
|
+
raise EvalError("unknown_shim", "%s %s %s" % (op, on_form, to_form))
|
|
876
|
+
|
|
877
|
+
if op == "more":
|
|
878
|
+
if to_form == "pow2":
|
|
879
|
+
if not (is_fixed_vector(t) and not is_pow2_32(num_elems(t))):
|
|
880
|
+
return False, ("invalid",)
|
|
881
|
+
return True, fixed_vector(pow2ceil(num_elems(t)), get_element_type(t))
|
|
882
|
+
raise EvalError("unknown_shim", "%s %s %s" % (op, on_form, to_form))
|
|
883
|
+
|
|
884
|
+
if op == "fewer":
|
|
885
|
+
if to_form == "ty":
|
|
886
|
+
target = ev.llt_value(to_node["args"][0], types, mems, config)
|
|
887
|
+
tsize, _ = size_in_bits(target)
|
|
888
|
+
if not (is_vector(t) and size > tsize):
|
|
889
|
+
return False, ("invalid",)
|
|
890
|
+
return True, change_element_type(t, target)
|
|
891
|
+
raise EvalError("unknown_shim", "%s %s %s" % (op, on_form, to_form))
|
|
892
|
+
|
|
893
|
+
if op == "bitcast":
|
|
894
|
+
if to_form == "ty":
|
|
895
|
+
target = ev.llt_value(to_node["args"][0], types, mems, config)
|
|
896
|
+
return True, change_element_type(t, target)
|
|
897
|
+
raise EvalError("unknown_shim", "%s %s %s" % (op, on_form, to_form))
|
|
898
|
+
|
|
899
|
+
raise EvalError("unknown_shim", "%s %s %s" % (op, on_form, to_form))
|
|
900
|
+
|
|
901
|
+
# ---------------------------------------------------------------------------
|
|
902
|
+
# Query evaluation
|
|
903
|
+
# ---------------------------------------------------------------------------
|
|
904
|
+
|
|
905
|
+
def evaluate(evaluator, config, opcode, types, mems):
|
|
906
|
+
idxs = evaluator.by_opcode.get(opcode)
|
|
907
|
+
if idxs is None:
|
|
908
|
+
raise EvalError("no_rules", "opcode %r has no rule sets" % (opcode,))
|
|
909
|
+
for rs_idx in idxs:
|
|
910
|
+
rs = evaluator.rulesets[rs_idx]
|
|
911
|
+
for rule in rs["rules"]:
|
|
912
|
+
if not evaluator.bool_value(rule["predicate"], types, mems, config):
|
|
913
|
+
continue
|
|
914
|
+
guard, new_type = shim_guard_and_newtype(
|
|
915
|
+
rule.get("op"), rule.get("on"), rule.get("to"),
|
|
916
|
+
types, mems, evaluator, config)
|
|
917
|
+
if not guard:
|
|
918
|
+
continue
|
|
919
|
+
action = rule["action"]
|
|
920
|
+
type_idx = rule.get("type_idx", 0)
|
|
921
|
+
return action, type_idx, new_type
|
|
922
|
+
# LegalizerInfo::getAction falls back to Unsupported when no rule fires.
|
|
923
|
+
return "Unsupported", 0, ("invalid",)
|
|
924
|
+
|
|
925
|
+
|
|
926
|
+
# ---------------------------------------------------------------------------
|
|
927
|
+
# Runner
|
|
928
|
+
# ---------------------------------------------------------------------------
|
|
929
|
+
|
|
930
|
+
def load_doc(path):
|
|
931
|
+
with open(path) as f:
|
|
932
|
+
doc = json.load(f)
|
|
933
|
+
if doc.get("version") != "0.4":
|
|
934
|
+
raise EvalError("bad_version", "expected schema 0.4, got %r" % (doc.get("version"),))
|
|
935
|
+
return doc
|
|
936
|
+
|
|
937
|
+
|
|
938
|
+
def load_features(path):
|
|
939
|
+
with open(path) as f:
|
|
940
|
+
return Features(json.load(f))
|
|
941
|
+
|
|
942
|
+
|
|
943
|
+
def run_cases(evaluator, cases_path):
|
|
944
|
+
total = 0
|
|
945
|
+
disagree = 0
|
|
946
|
+
errors = 0
|
|
947
|
+
first_failure = None
|
|
948
|
+
action_counts = {}
|
|
949
|
+
with open(cases_path) as f:
|
|
950
|
+
for line in f:
|
|
951
|
+
line = line.strip()
|
|
952
|
+
if not line:
|
|
953
|
+
continue
|
|
954
|
+
q = json.loads(line)
|
|
955
|
+
total += 1
|
|
956
|
+
types = [parse_case_llt(s) for s in q["types"]]
|
|
957
|
+
mems = []
|
|
958
|
+
if q["mem"] is not None:
|
|
959
|
+
mems.append(MemDesc(parse_case_llt(q["mem"][0]), q["mem"][1]))
|
|
960
|
+
try:
|
|
961
|
+
action, type_idx, new_type = evaluate(
|
|
962
|
+
evaluator, q["config"], q["name"], types, mems)
|
|
963
|
+
except EvalError as e:
|
|
964
|
+
errors += 1
|
|
965
|
+
if first_failure is None:
|
|
966
|
+
first_failure = "ERROR %s: %s" % (q, e)
|
|
967
|
+
continue
|
|
968
|
+
new_type_str = fmt_oracle_llt(new_type) if new_type is not None else "LLT_invalid"
|
|
969
|
+
want_new = parse_case_llt(q["new_type"])
|
|
970
|
+
action_counts[action] = action_counts.get(action, 0) + 1
|
|
971
|
+
if (action != q["action"] or type_idx != q["type_idx"]
|
|
972
|
+
or new_type != want_new):
|
|
973
|
+
disagree += 1
|
|
974
|
+
if first_failure is None:
|
|
975
|
+
first_failure = (
|
|
976
|
+
"%s %s %s mem=%s: got (%s,%d,%s) want (%s,%d,%s)"
|
|
977
|
+
% (q["config"], q["name"], q["types"], q["mem"],
|
|
978
|
+
action, type_idx, new_type_str,
|
|
979
|
+
q["action"], q["type_idx"], q["new_type"]))
|
|
980
|
+
return total, disagree, errors, first_failure, action_counts
|
|
981
|
+
|
|
982
|
+
|
|
983
|
+
def main(argv=None):
|
|
984
|
+
ap = argparse.ArgumentParser()
|
|
985
|
+
ap.add_argument("--doc", required=True)
|
|
986
|
+
ap.add_argument("--features", required=True)
|
|
987
|
+
ap.add_argument("--cases", required=True)
|
|
988
|
+
ap.add_argument("--verbose", action="store_true")
|
|
989
|
+
args = ap.parse_args(argv)
|
|
990
|
+
|
|
991
|
+
doc = load_doc(args.doc)
|
|
992
|
+
features = load_features(args.features)
|
|
993
|
+
evaluator = Evaluator(doc, features)
|
|
994
|
+
|
|
995
|
+
total, disagree, errors, first_failure, counts = run_cases(
|
|
996
|
+
evaluator, args.cases)
|
|
997
|
+
|
|
998
|
+
print("cases=%d disagreements=%d errors=%d" % (total, disagree, errors))
|
|
999
|
+
print("actions:", json.dumps(counts, sort_keys=True))
|
|
1000
|
+
if first_failure is not None:
|
|
1001
|
+
print("first failure:", first_failure)
|
|
1002
|
+
if disagree or errors:
|
|
1003
|
+
sys.exit(1)
|
|
1004
|
+
print("OK")
|
|
1005
|
+
|
|
1006
|
+
|
|
1007
|
+
if __name__ == "__main__":
|
|
1008
|
+
main()
|