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/cli.py ADDED
@@ -0,0 +1,238 @@
1
+ """Command line for the gisel legalizer DSL."""
2
+
3
+ import argparse
4
+ import difflib
5
+ import json
6
+ import sys
7
+ from pathlib import Path
8
+
9
+ from . import dsl, td
10
+ from .backend import check_json, core_json, llvm_inc, table
11
+
12
+ BACKENDS = ("td", "inc", "check", "table", "core")
13
+
14
+
15
+ def _table_prefix(source, explicit, gs=None):
16
+ """-legalizer-table-prefix, defaulted from the source file name.
17
+
18
+ tblgen's default is "X86", which is why the AArch64 lane has to pass
19
+ -legalizer-table-prefix=AArch64. Deriving it from `X86LegalizerInfo.gisel`
20
+ removes that footgun; --prefix still overrides.
21
+ """
22
+ if explicit:
23
+ return explicit
24
+ if gs is not None:
25
+ for rs in gs.get("rulesets", []):
26
+ if "_" in rs["def"]:
27
+ return rs["def"].split("_", 1)[0]
28
+ stem = Path(source).stem
29
+ return stem[:-len("LegalizerInfo")] if stem.endswith("LegalizerInfo") else stem
30
+
31
+
32
+ def _read_text(path):
33
+ try:
34
+ return Path(path).read_text()
35
+ except OSError as e:
36
+ print(f"{path}: {e.strerror}", file=sys.stderr)
37
+ return None
38
+
39
+
40
+ def _source_error(source, exc):
41
+ """`path:line: message`, the shape every editor understands."""
42
+ msg = str(exc)
43
+ if msg.startswith("line "):
44
+ msg = msg[len("line "):]
45
+ print(f"{source}:{msg}", file=sys.stderr)
46
+ return 2
47
+
48
+
49
+ def _generate(gs, source, emit, prefix=None):
50
+ if emit == "td":
51
+ return td.emit_td(gs, source)
52
+ if emit == "inc":
53
+ return llvm_inc.emit_inc(gs, source)
54
+ if emit == "check":
55
+ return check_json.emit_check(gs)
56
+ if emit == "table":
57
+ return table.emit_table(gs, _table_prefix(source, prefix, gs))
58
+ if emit == "core":
59
+ return core_json.emit_core(gs, source, Path(source).read_text())
60
+ raise ValueError(f"unknown backend {emit!r}")
61
+
62
+
63
+ def _load_manifest(path):
64
+ """Bundled table by default; an explicit file may be bare or wrapped."""
65
+ if not path:
66
+ return dsl.load_arity_manifest()
67
+ raw = json.loads(Path(path).read_text())
68
+ return raw["opcodes"] if "opcodes" in raw else raw
69
+
70
+
71
+ def _has_slot(node):
72
+ if isinstance(node, dict):
73
+ if node.get("kind") == "slot":
74
+ return True
75
+ return any(_has_slot(v) for v in node.values())
76
+ if isinstance(node, list):
77
+ return any(_has_slot(v) for v in node)
78
+ return False
79
+
80
+
81
+ def _lint(gs, source, manifest):
82
+ """J1/J2b/J3a/J3b over every rule, with `.gisel` line numbers."""
83
+ errors, warnings = [], []
84
+ nrules = 0
85
+ for rs in gs["rulesets"]:
86
+ arities = [manifest.get(o) for o in rs["opcodes"]]
87
+ known = [a for a in arities if a is not None]
88
+ arity = min(known) if known else None
89
+ unknown = [o for o, a in zip(rs["opcodes"], arities) if a is None]
90
+ first_slot_line = None
91
+ for rule in rs["rules"]:
92
+ nrules += 1
93
+ line = rule.get("_line", 0)
94
+ errs = []
95
+ try:
96
+ dsl.check_rule(rule, arity if arity is not None else 10 ** 9, errs)
97
+ except (KeyError, IndexError, TypeError, ValueError) as e:
98
+ errs.append(f"malformed rule: {e}")
99
+ for msg in errs:
100
+ errors.append({"file": source, "line": line,
101
+ "ruleset": rs["def"], "rule": rule["op"],
102
+ "message": msg})
103
+ if unknown and first_slot_line is None and _has_slot(rule):
104
+ first_slot_line = line
105
+ if first_slot_line is not None:
106
+ warnings.append({"file": source, "line": first_slot_line,
107
+ "ruleset": rs["def"],
108
+ "message": "arity unknown for %s; slot bounds not checked"
109
+ % ", ".join(unknown)})
110
+ return errors, warnings, nrules
111
+
112
+
113
+ def main(argv=None):
114
+ argv = list(sys.argv[1:] if argv is None else argv)
115
+ ap = argparse.ArgumentParser(prog="gisel", description=__doc__)
116
+ ap.add_argument("--version", action="version", version="gisel 0.2.0")
117
+ sub = ap.add_subparsers(dest="cmd", required=True)
118
+
119
+ g = sub.add_parser("gen", help="generate a backend artifact")
120
+ g.add_argument("source")
121
+ g.add_argument("--emit", default="td", choices=BACKENDS,
122
+ help="backend: td, inc (-gen-legalizer), check (-gen-legalizer-check)")
123
+ g.add_argument("--out", required=True)
124
+ g.add_argument("--prefix", help="table prefix (default: from the file name)")
125
+
126
+ c = sub.add_parser("check", help="verify a generated artifact is up to date")
127
+ c.add_argument("source")
128
+ c.add_argument("--emit", default="td", choices=BACKENDS)
129
+ c.add_argument("--against", help="artifact to compare against")
130
+ c.add_argument("--td", help="compatibility alias for --against")
131
+ c.add_argument("--strict-origin", action="store_true",
132
+ help="core: also compare the per-rule origin line numbers")
133
+
134
+ b = sub.add_parser("bootstrap", help="build a .gisel from a hand-written .td")
135
+ b.add_argument("--td", required=True)
136
+ b.add_argument("--core", required=True)
137
+ b.add_argument("--out", required=True)
138
+ b.add_argument("--title", default="Target")
139
+
140
+ l = sub.add_parser("lint", help="static checks over a source file (no LLVM)")
141
+ l.add_argument("source")
142
+ l.add_argument("--manifest",
143
+ help="opcode arity JSON (default: the bundled table)")
144
+ l.add_argument("--json", action="store_true", help="machine-readable diagnostics")
145
+ l.add_argument("--max-errors", type=int, default=50,
146
+ help="print at most N errors (exit code is unaffected)")
147
+
148
+ e = sub.add_parser("eval", help="evaluate the normal form without LLVM")
149
+ e.add_argument("--doc", required=True)
150
+ e.add_argument("--features", required=True)
151
+ e.add_argument("--cases", required=True)
152
+ e.add_argument("--verbose", action="store_true")
153
+
154
+ args = ap.parse_args(argv)
155
+ if args.cmd == "gen":
156
+ source_text = _read_text(args.source)
157
+ if source_text is None:
158
+ return 2
159
+ try:
160
+ gs = td.parse_gisel(source_text)
161
+ text = _generate(gs, args.source, args.emit, getattr(args, "prefix", None))
162
+ except ValueError as e:
163
+ return _source_error(args.source, e)
164
+ try:
165
+ Path(args.out).write_text(text)
166
+ except OSError as e:
167
+ print(f"{args.out}: {e.strerror}", file=sys.stderr)
168
+ return 2
169
+ print(f"{args.out}: {len(gs['rulesets'])} rule sets, "
170
+ f"{sum(len(r['rules']) for r in gs['rulesets'])} rules")
171
+ return 0
172
+ if args.cmd == "check":
173
+ want_path = args.against or args.td
174
+ if not want_path:
175
+ c.error("check needs --against <artifact>")
176
+ source_text = _read_text(args.source)
177
+ if source_text is None:
178
+ return 2
179
+ try:
180
+ gs = td.parse_gisel(source_text)
181
+ got = _generate(gs, args.source, args.emit)
182
+ except ValueError as e:
183
+ return _source_error(args.source, e)
184
+ want = _read_text(want_path)
185
+ if want is None:
186
+ return 2
187
+ if args.emit == "core" and not args.strict_origin:
188
+ got = core_json.strip_origin(got)
189
+ want = core_json.strip_origin(want)
190
+ if got == want:
191
+ print(f"{want_path}: up to date")
192
+ return 0
193
+ sys.stderr.writelines(difflib.unified_diff(
194
+ want.splitlines(True), got.splitlines(True), want_path, "<generated>"))
195
+ print(f"{want_path}: STALE (re-run `gisel gen --emit={args.emit}`)",
196
+ file=sys.stderr)
197
+ return 1
198
+ if args.cmd == "lint":
199
+ text = _read_text(args.source)
200
+ if text is None:
201
+ return 2
202
+ try:
203
+ gs = td.parse_gisel(text)
204
+ except ValueError as e:
205
+ return _source_error(args.source, e)
206
+ try:
207
+ manifest = _load_manifest(args.manifest)
208
+ except (OSError, ValueError, KeyError, json.JSONDecodeError) as e:
209
+ print(f"{args.manifest}: {e}", file=sys.stderr)
210
+ return 2
211
+ errors, warnings, nrules = _lint(gs, args.source, manifest)
212
+ if args.json:
213
+ print(json.dumps({"source": args.source,
214
+ "errors": errors, "warnings": warnings,
215
+ "rules": nrules}, indent=1))
216
+ else:
217
+ for e in errors[:args.max_errors]:
218
+ print(f"{e['file']}:{e['line']}: {e['ruleset']}: "
219
+ f"{e['message']}", file=sys.stderr)
220
+ if len(errors) > args.max_errors:
221
+ print(f"... {len(errors) - args.max_errors} more error(s)",
222
+ file=sys.stderr)
223
+ for w in warnings:
224
+ print(f"{w['file']}:{w['line']}: warning: {w['message']}",
225
+ file=sys.stderr)
226
+ print(f"{args.source}: {len(errors)} error(s), {len(warnings)} "
227
+ f"warning(s), {nrules} rule(s)")
228
+ return 1 if errors else 0
229
+ if args.cmd == "bootstrap":
230
+ return td.main(["bootstrap", "--td", args.td, "--core", args.core,
231
+ "--out", args.out, "--title", args.title])
232
+ from . import eval as evaluator
233
+ # `eval` owns its own argument set; drop the subcommand word.
234
+ return evaluator.main(argv[1:])
235
+
236
+
237
+ if __name__ == "__main__":
238
+ sys.exit(main())
@@ -0,0 +1,290 @@
1
+ {
2
+ "schema": 1,
3
+ "source": "llvm/include/llvm/Target/GenericOpcodes.td",
4
+ "note": "arity = highest (p)typeN index + 1; null = variadic/unknown",
5
+ "opcodes": {
6
+ "G_ABDS": 1,
7
+ "G_ABDU": 1,
8
+ "G_ABS": 1,
9
+ "G_ADD": 1,
10
+ "G_ADDRSPACE_CAST": 2,
11
+ "G_AND": 1,
12
+ "G_ANYEXT": 2,
13
+ "G_ASHR": 2,
14
+ "G_ASSERT_ALIGN": 1,
15
+ "G_ASSERT_SEXT": 1,
16
+ "G_ASSERT_ZEXT": 1,
17
+ "G_ATOMICRMW_ADD": 2,
18
+ "G_ATOMICRMW_AND": 2,
19
+ "G_ATOMICRMW_FADD": 2,
20
+ "G_ATOMICRMW_FMAX": 2,
21
+ "G_ATOMICRMW_FMAXIMUM": 2,
22
+ "G_ATOMICRMW_FMAXIMUMNUM": 2,
23
+ "G_ATOMICRMW_FMIN": 2,
24
+ "G_ATOMICRMW_FMINIMUM": 2,
25
+ "G_ATOMICRMW_FMINIMUMNUM": 2,
26
+ "G_ATOMICRMW_FSUB": 2,
27
+ "G_ATOMICRMW_MAX": 2,
28
+ "G_ATOMICRMW_MIN": 2,
29
+ "G_ATOMICRMW_NAND": 2,
30
+ "G_ATOMICRMW_OR": 2,
31
+ "G_ATOMICRMW_SUB": 2,
32
+ "G_ATOMICRMW_UDEC_WRAP": 2,
33
+ "G_ATOMICRMW_UINC_WRAP": 2,
34
+ "G_ATOMICRMW_UMAX": 2,
35
+ "G_ATOMICRMW_UMIN": 2,
36
+ "G_ATOMICRMW_USUB_COND": 2,
37
+ "G_ATOMICRMW_USUB_SAT": 2,
38
+ "G_ATOMICRMW_XCHG": 2,
39
+ "G_ATOMICRMW_XOR": 2,
40
+ "G_ATOMIC_CMPXCHG": 2,
41
+ "G_ATOMIC_CMPXCHG_WITH_SUCCESS": 3,
42
+ "G_BITCAST": 2,
43
+ "G_BITREVERSE": 1,
44
+ "G_BLOCK_ADDR": 1,
45
+ "G_BR": 0,
46
+ "G_BRCOND": 1,
47
+ "G_BRINDIRECT": 1,
48
+ "G_BRJT": 2,
49
+ "G_BSWAP": 1,
50
+ "G_BUILD_VECTOR": null,
51
+ "G_BUILD_VECTOR_TRUNC": null,
52
+ "G_BZERO": 2,
53
+ "G_CLMUL": 1,
54
+ "G_CLMULH": 1,
55
+ "G_CLMULR": 1,
56
+ "G_CONCAT_VECTORS": null,
57
+ "G_CONSTANT": 1,
58
+ "G_CONSTANT_FOLD_BARRIER": 1,
59
+ "G_CONSTANT_POOL": 1,
60
+ "G_CTLS": 2,
61
+ "G_CTLZ": 2,
62
+ "G_CTLZ_ZERO_POISON": 2,
63
+ "G_CTPOP": 2,
64
+ "G_CTTZ": 2,
65
+ "G_CTTZ_ZERO_POISON": 2,
66
+ "G_DEBUGTRAP": 0,
67
+ "G_DYN_STACKALLOC": 2,
68
+ "G_EXTRACT": 2,
69
+ "G_EXTRACT_SUBVECTOR": 2,
70
+ "G_EXTRACT_VECTOR_ELT": 3,
71
+ "G_FABS": 1,
72
+ "G_FACOS": 1,
73
+ "G_FADD": 1,
74
+ "G_FASIN": 1,
75
+ "G_FATAN": 1,
76
+ "G_FATAN2": 1,
77
+ "G_FCANONICALIZE": 1,
78
+ "G_FCEIL": 1,
79
+ "G_FCMP": 2,
80
+ "G_FCONSTANT": 1,
81
+ "G_FCOPYSIGN": 2,
82
+ "G_FCOS": 1,
83
+ "G_FCOSH": 1,
84
+ "G_FDIV": 1,
85
+ "G_FENCE": 0,
86
+ "G_FEXP": 1,
87
+ "G_FEXP10": 1,
88
+ "G_FEXP2": 1,
89
+ "G_FFLOOR": 1,
90
+ "G_FFREXP": 2,
91
+ "G_FLDEXP": 2,
92
+ "G_FLOG": 1,
93
+ "G_FLOG10": 1,
94
+ "G_FLOG2": 1,
95
+ "G_FMA": 1,
96
+ "G_FMAD": 1,
97
+ "G_FMAXIMUM": 1,
98
+ "G_FMAXIMUMNUM": 1,
99
+ "G_FMAXNUM": 1,
100
+ "G_FMAXNUM_IEEE": 1,
101
+ "G_FMINIMUM": 1,
102
+ "G_FMINIMUMNUM": 1,
103
+ "G_FMINNUM": 1,
104
+ "G_FMINNUM_IEEE": 1,
105
+ "G_FMODF": 1,
106
+ "G_FMUL": 1,
107
+ "G_FNEARBYINT": 1,
108
+ "G_FNEG": 1,
109
+ "G_FPEXT": 2,
110
+ "G_FPEXTLOAD": 2,
111
+ "G_FPOW": 1,
112
+ "G_FPOWI": 2,
113
+ "G_FPTOSI": 2,
114
+ "G_FPTOSI_SAT": 2,
115
+ "G_FPTOUI": 2,
116
+ "G_FPTOUI_SAT": 2,
117
+ "G_FPTRUNC": 2,
118
+ "G_FPTRUNCSTORE": 2,
119
+ "G_FRAME_INDEX": 1,
120
+ "G_FREEZE": 1,
121
+ "G_FREM": 1,
122
+ "G_FRINT": 1,
123
+ "G_FSHL": 2,
124
+ "G_FSHR": 2,
125
+ "G_FSIN": 1,
126
+ "G_FSINCOS": 1,
127
+ "G_FSINH": 1,
128
+ "G_FSQRT": 1,
129
+ "G_FSUB": 1,
130
+ "G_FTAN": 1,
131
+ "G_FTANH": 1,
132
+ "G_GET_FPENV": 1,
133
+ "G_GET_FPMODE": 1,
134
+ "G_GET_ROUNDING": 1,
135
+ "G_GLOBAL_VALUE": 1,
136
+ "G_ICMP": 2,
137
+ "G_IMPLICIT_DEF": 1,
138
+ "G_INDEXED_LOAD": 3,
139
+ "G_INDEXED_SEXTLOAD": 3,
140
+ "G_INDEXED_STORE": 3,
141
+ "G_INDEXED_ZEXTLOAD": 3,
142
+ "G_INSERT": 2,
143
+ "G_INSERT_SUBVECTOR": 2,
144
+ "G_INSERT_VECTOR_ELT": 3,
145
+ "G_INTRINSIC": null,
146
+ "G_INTRINSIC_CONVERGENT": null,
147
+ "G_INTRINSIC_CONVERGENT_W_SIDE_EFFECTS": null,
148
+ "G_INTRINSIC_FPTRUNC_ROUND": 2,
149
+ "G_INTRINSIC_LLRINT": 2,
150
+ "G_INTRINSIC_LRINT": 2,
151
+ "G_INTRINSIC_ROUND": 1,
152
+ "G_INTRINSIC_ROUNDEVEN": 1,
153
+ "G_INTRINSIC_TRUNC": 1,
154
+ "G_INTRINSIC_W_SIDE_EFFECTS": null,
155
+ "G_INTTOPTR": 2,
156
+ "G_INVOKE_REGION_START": 0,
157
+ "G_IS_FPCLASS": 2,
158
+ "G_JUMP_TABLE": 1,
159
+ "G_LLROUND": 2,
160
+ "G_LOAD": 2,
161
+ "G_LROUND": 2,
162
+ "G_LSHR": 2,
163
+ "G_MEMCPY": 3,
164
+ "G_MEMCPY_INLINE": 3,
165
+ "G_MEMMOVE": 3,
166
+ "G_MEMSET": 3,
167
+ "G_MEMSET_INLINE": 3,
168
+ "G_MERGE_VALUES": null,
169
+ "G_MUL": 1,
170
+ "G_OR": 1,
171
+ "G_PHI": null,
172
+ "G_PREFETCH": 1,
173
+ "G_PTRAUTH_GLOBAL_VALUE": 2,
174
+ "G_PTRMASK": 2,
175
+ "G_PTRTOINT": 2,
176
+ "G_PTR_ADD": 2,
177
+ "G_READCYCLECOUNTER": 1,
178
+ "G_READSTEADYCOUNTER": 1,
179
+ "G_READ_REGISTER": 1,
180
+ "G_RESET_FPENV": 0,
181
+ "G_RESET_FPMODE": 0,
182
+ "G_ROTL": 2,
183
+ "G_ROTR": 2,
184
+ "G_SADDE": 2,
185
+ "G_SADDO": 2,
186
+ "G_SADDSAT": 1,
187
+ "G_SAVGCEIL": 1,
188
+ "G_SAVGFLOOR": 1,
189
+ "G_SBFX": 2,
190
+ "G_SCMP": 2,
191
+ "G_SDIV": 1,
192
+ "G_SDIVFIX": 1,
193
+ "G_SDIVFIXSAT": 1,
194
+ "G_SDIVREM": 1,
195
+ "G_SELECT": 2,
196
+ "G_SET_FPENV": 1,
197
+ "G_SET_FPMODE": 1,
198
+ "G_SET_ROUNDING": 1,
199
+ "G_SEXT": 2,
200
+ "G_SEXTLOAD": 2,
201
+ "G_SEXT_INREG": 1,
202
+ "G_SHL": 2,
203
+ "G_SHUFFLE_VECTOR": 2,
204
+ "G_SITOFP": 2,
205
+ "G_SMAX": 1,
206
+ "G_SMIN": 1,
207
+ "G_SMULFIX": 1,
208
+ "G_SMULFIXSAT": 1,
209
+ "G_SMULH": 1,
210
+ "G_SMULO": 2,
211
+ "G_SPLAT_VECTOR": 2,
212
+ "G_SREM": 1,
213
+ "G_SSHLSAT": 2,
214
+ "G_SSUBE": 2,
215
+ "G_SSUBO": 2,
216
+ "G_SSUBSAT": 1,
217
+ "G_STACKRESTORE": 1,
218
+ "G_STACKSAVE": 1,
219
+ "G_STEP_VECTOR": 1,
220
+ "G_STORE": 2,
221
+ "G_STRICT_FADD": 0,
222
+ "G_STRICT_FCMP": 0,
223
+ "G_STRICT_FCMPS": 0,
224
+ "G_STRICT_FDIV": 0,
225
+ "G_STRICT_FLDEXP": 0,
226
+ "G_STRICT_FMA": 0,
227
+ "G_STRICT_FMUL": 0,
228
+ "G_STRICT_FREM": 0,
229
+ "G_STRICT_FSQRT": 0,
230
+ "G_STRICT_FSUB": 0,
231
+ "G_SUB": 1,
232
+ "G_TRAP": 0,
233
+ "G_TRUNC": 2,
234
+ "G_TRUNC_SSAT_S": 2,
235
+ "G_TRUNC_SSAT_U": 2,
236
+ "G_TRUNC_USAT_U": 2,
237
+ "G_UADDE": 2,
238
+ "G_UADDO": 2,
239
+ "G_UADDSAT": 1,
240
+ "G_UAVGCEIL": 1,
241
+ "G_UAVGFLOOR": 1,
242
+ "G_UBFX": 2,
243
+ "G_UBSANTRAP": 0,
244
+ "G_UCMP": 2,
245
+ "G_UDIV": 1,
246
+ "G_UDIVFIX": 1,
247
+ "G_UDIVFIXSAT": 1,
248
+ "G_UDIVREM": 1,
249
+ "G_UITOFP": 2,
250
+ "G_UMAX": 1,
251
+ "G_UMIN": 1,
252
+ "G_UMULFIX": 1,
253
+ "G_UMULFIXSAT": 1,
254
+ "G_UMULH": 1,
255
+ "G_UMULO": 2,
256
+ "G_UNMERGE_VALUES": null,
257
+ "G_UREM": 1,
258
+ "G_USHLSAT": 2,
259
+ "G_USUBE": 2,
260
+ "G_USUBO": 2,
261
+ "G_USUBSAT": 1,
262
+ "G_VAARG": 2,
263
+ "G_VASTART": 1,
264
+ "G_VECREDUCE_ADD": 2,
265
+ "G_VECREDUCE_AND": 2,
266
+ "G_VECREDUCE_FADD": 2,
267
+ "G_VECREDUCE_FMAX": 2,
268
+ "G_VECREDUCE_FMAXIMUM": 2,
269
+ "G_VECREDUCE_FMAXIMUMNUM": 2,
270
+ "G_VECREDUCE_FMIN": 2,
271
+ "G_VECREDUCE_FMINIMUM": 2,
272
+ "G_VECREDUCE_FMINIMUMNUM": 2,
273
+ "G_VECREDUCE_FMUL": 2,
274
+ "G_VECREDUCE_MUL": 2,
275
+ "G_VECREDUCE_OR": 2,
276
+ "G_VECREDUCE_SEQ_FADD": 3,
277
+ "G_VECREDUCE_SEQ_FMUL": 3,
278
+ "G_VECREDUCE_SMAX": 2,
279
+ "G_VECREDUCE_SMIN": 2,
280
+ "G_VECREDUCE_UMAX": 2,
281
+ "G_VECREDUCE_UMIN": 2,
282
+ "G_VECREDUCE_XOR": 2,
283
+ "G_VECTOR_COMPRESS": 2,
284
+ "G_VSCALE": 1,
285
+ "G_WRITE_REGISTER": 1,
286
+ "G_XOR": 1,
287
+ "G_ZEXT": 2,
288
+ "G_ZEXTLOAD": 2
289
+ }
290
+ }