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 ADDED
@@ -0,0 +1,3 @@
1
+ """gisel: a free-form language for GlobalISel legalizer rules."""
2
+
3
+ __version__ = "0.1.0"
gisel/__main__.py ADDED
@@ -0,0 +1,6 @@
1
+ import sys
2
+
3
+ from .cli import main
4
+
5
+ if __name__ == "__main__":
6
+ sys.exit(main())
File without changes
@@ -0,0 +1,57 @@
1
+ """Emit the ``-gen-legalizer-check`` JSON from the schema-0.4 normal form.
2
+
3
+ Mirrors ``LegalizerCheckEmitter::run`` (LegalizerInfoEmitter.cpp:8004) and the
4
+ counters it prints from ``verifyLoweringRegistry`` (:4568).
5
+
6
+ Two kinds of number appear in that JSON, and they must not be confused:
7
+
8
+ * per-target counts (``rule_sets``, ``rules``, ``core_rules``, ``coverage``)
9
+ are derived from the corpus. ``Rules`` counts every entry in a rule set's
10
+ ``Rules`` list, including ``ImmIdx``; ``Coverage`` counts the ``immIdx``
11
+ markers, so ``rules == core_rules + legacy_rules + coverage``.
12
+ * vocabulary counts (``builders``, ``overloads``, ``entries``) describe the
13
+ TableGen compatibility layer itself -- 50 ``LegalizeBuilder`` records and 64
14
+ ``LegalizeLowering`` entries, the same for every target. They are carried
15
+ as provenance-stamped constants in ``gisel/vocab.py`` because they are not a
16
+ property of the DSL; a target cannot change them.
17
+ """
18
+
19
+ from .. import td as T
20
+ from ..vocab import BUILDER_VOCABULARY, CORE_OPERATORS
21
+
22
+
23
+ def counts(gs):
24
+ """Per-target counters, in the shape ``LoweringStats`` uses."""
25
+ rule_sets = len(gs["rulesets"])
26
+ core = sum(len(rs["rules"]) for rs in gs["rulesets"])
27
+ imm = sum(len(rs.get("legacy", [])) for rs in gs["rulesets"])
28
+ return {
29
+ "rule_sets": rule_sets,
30
+ "rules": core + imm,
31
+ "core_rules": core,
32
+ "legacy_rules": 0,
33
+ "coverage": imm,
34
+ }
35
+
36
+
37
+ def emit_check(gs):
38
+ c = counts(gs)
39
+ v = BUILDER_VOCABULARY
40
+ out = [
41
+ "{",
42
+ ' "builders": %d,' % v["builders"],
43
+ ' "overloads": %d,' % v["overloads"],
44
+ ' "entries": %d,' % v["entries"],
45
+ ' "rule_sets": %d,' % c["rule_sets"],
46
+ ' "rules": %d,' % c["rules"],
47
+ ' "escapes": 0,',
48
+ ' "entries_without_table_translation": 0,',
49
+ ' "rule_sets_without_table_translation": 0,',
50
+ ' "core_rules": %d,' % c["core_rules"],
51
+ ' "legacy_rules": %d,' % c["legacy_rules"],
52
+ ' "coverage": %d,' % c["coverage"],
53
+ ' "core_operators": [' +
54
+ ", ".join('"%s"' % op for op in CORE_OPERATORS) + "]",
55
+ "}",
56
+ ]
57
+ return "\n".join(out) + "\n"
@@ -0,0 +1,192 @@
1
+ """Emit the ``-gen-legalizer-core`` JSON from the schema-0.4 normal form.
2
+
3
+ Mirrors ``LegalizerInfoEmitter::runCore`` (LegalizerInfoEmitter.cpp:4816),
4
+ ``coreTypeValue`` (:1361) and ``coreSetJson`` (:2820).
5
+
6
+ The rule body is already the normal form; three things are re-spelled on the
7
+ way out:
8
+
9
+ * a type is named by its LLT, not by its record (``X86_s8`` -> ``s8``), and a
10
+ pointer whose width the configuration decides is written as a wildcard
11
+ (``p0:*``) inside a set but with its width (``p0:64``) in the domain;
12
+ * a select type becomes a nested ``{"select": {...}}`` value, because the
13
+ branch is a property of the subtarget, not of the document;
14
+ * the domain is the set of normalised named types with the wildcards dropped,
15
+ plus ``p0:<width>``, sorted and deduplicated (:4856).
16
+
17
+ ``origin`` points at the ``.gisel`` source, not at the generated ``.td``, so
18
+ the gate compares documents after dropping it (plan AC5).
19
+ """
20
+
21
+ import json
22
+ import re
23
+
24
+ from .. import td as T
25
+
26
+ SCHEMA = "legalizer-spec-core"
27
+ VERSION = "0.4"
28
+ POINTER_WIDTH = 64
29
+
30
+
31
+ _ORIGIN_RE = re.compile(r'"origin": \{[^{}]*\}')
32
+
33
+
34
+ def strip_origin(text):
35
+ """Drop the per-rule ``origin`` values, keeping the key.
36
+
37
+ ``origin`` names the source file and line; ours point at ``.gisel`` and the
38
+ baseline at the generated ``.td``, so it is provenance, not semantics
39
+ (plan AC5). Both the golden test and ``gisel check --emit=core`` compare
40
+ documents with the values erased.
41
+ """
42
+ return _ORIGIN_RE.sub('"origin": {}', text)
43
+
44
+
45
+ def _compact(obj):
46
+ return json.dumps(obj, separators=(",", ":"), ensure_ascii=False)
47
+
48
+
49
+ def _string(s):
50
+ return json.dumps(s, ensure_ascii=False)
51
+
52
+
53
+ class CoreJson:
54
+ def __init__(self, gs):
55
+ self.gs = gs
56
+ self.exprs = {n: p for k, n, p in gs["decls"] if k == "type"}
57
+ self.selects = {n: p for k, n, p in gs["decls"] if k == "select"}
58
+ self.sets = T.collect_named_sets(gs["decls"])
59
+
60
+ # ---- types -----------------------------------------------------------
61
+
62
+ def type_name(self, name):
63
+ """A record name -> its canonical LLT spelling (``s8``, ``p0:*``)."""
64
+ expr = self.exprs.get(name)
65
+ if expr is None:
66
+ raise KeyError("not a named type: %r" % name)
67
+ norm = T.normalize_llt(expr, self.exprs)
68
+ if not norm:
69
+ raise ValueError("cannot normalise type %r" % name)
70
+ return norm
71
+
72
+ def type_value(self, name):
73
+ """A value in a rule: a string, or a nested select object."""
74
+ if name in self.selects:
75
+ return self.select_json(name)
76
+ return self.type_name(name)
77
+
78
+ def select_json(self, name):
79
+ conds, types, otherwise = self.selects[name]
80
+ value = self.type_value(otherwise)
81
+ for i in range(len(conds) - 1, -1, -1):
82
+ cond = self.pred_name(conds[i])
83
+ value = {"select": {"cond": cond, "then": self.type_value(types[i]),
84
+ "else": value}}
85
+ return value
86
+
87
+ def pred_name(self, name):
88
+ """A select guard: a feature leaf, or an inline predicate dag."""
89
+ feats = {n: p for k, n, p in self.gs["decls"] if k == "feature"}
90
+ if name in feats:
91
+ return {"kind": "leaf", "name": "hasFeature", "args": [feats[name]]}
92
+ preds = {n: p for k, n, p in self.gs["decls"] if k == "pred"}
93
+ if name in preds:
94
+ return T._bool_node(T.parse_bool(preds[name]), feats)
95
+ raise KeyError("unknown select guard %r" % name)
96
+
97
+ # ---- nodes -----------------------------------------------------------
98
+
99
+ def node(self, n):
100
+ if isinstance(n, list):
101
+ return [self.node(v) for v in n]
102
+ if not isinstance(n, dict):
103
+ return n
104
+ out = {}
105
+ for k, v in n.items():
106
+ if k == "value" and n.get("kind") == "type" and isinstance(v, str) \
107
+ and v in self.sets:
108
+ # A named set stays one object, expanded: one spelling, one
109
+ # normal form.
110
+ return {"kind": "set", "of": "Type",
111
+ "elems": [self.type_value(m) for m in self.sets[v]]}
112
+ if k == "value" and n.get("kind") == "type":
113
+ out[k] = self.type_value(v) if isinstance(v, str) else self.node(v)
114
+ elif k == "elems" and n.get("kind") == "set":
115
+ out[k] = [self.type_value(row) if isinstance(row, str)
116
+ else [self.type_value(x) if isinstance(x, str) else x
117
+ for x in row]
118
+ for row in v]
119
+ else:
120
+ out[k] = self.node(v)
121
+ return out
122
+
123
+ # ---- document --------------------------------------------------------
124
+
125
+ def domain(self):
126
+ types = []
127
+ for k, name, expr in self.gs["decls"]:
128
+ if k != "type":
129
+ continue
130
+ norm = T.normalize_llt(expr, self.exprs)
131
+ if norm and ":*" not in norm:
132
+ types.append(norm)
133
+ types.append("p0:%d" % POINTER_WIDTH)
134
+ return sorted(set(types))
135
+
136
+ def rule(self, rule, ruleset, index, origin):
137
+ parts = ['"action":%s' % _string(rule["action"]),
138
+ '"type_idx":%d' % rule["type_idx"],
139
+ '"predicate":%s' % _compact(self.node(rule["predicate"])),
140
+ '"schema": "0.4"',
141
+ '"op": %s' % _string(rule["op"])]
142
+ if rule.get("on") is not None:
143
+ parts.append('"on": %s' % _compact(self.node(rule["on"])))
144
+ if rule.get("to") is not None:
145
+ parts.append('"to": %s' % _compact(self.node(rule["to"])))
146
+ parts += ['"ruleset": %s' % _string(ruleset),
147
+ '"rule_index": %d' % index,
148
+ '"origin": %s' % _compact(origin)]
149
+ return "{" + ", ".join(parts) + "}"
150
+
151
+ def emit(self, source_name, lines=None):
152
+ lines = lines or {}
153
+ out = ["{",
154
+ ' "schema": %s,' % _string(SCHEMA),
155
+ ' "version": %s,' % _string(VERSION),
156
+ ' "config": {"triple": "unspecified", "cpu": "unspecified", '
157
+ '"features": [], "pointer_width": %d},' % POINTER_WIDTH,
158
+ ' "domain": {"types": [%s], "imms": [], "mems": [], '
159
+ '"orderings": [], "q0": []},'
160
+ % ", ".join(_string(t) for t in self.domain()),
161
+ ' "rulesets": [']
162
+ for si, rs in enumerate(self.gs["rulesets"]):
163
+ origin = {"source": source_name,
164
+ "line": lines.get(rs["def"], 0)}
165
+ out.append(' {"def": %s, "origin": %s,'
166
+ % (_string(rs["def"]), _compact(origin)))
167
+ out.append(' "opcodes": [%s],'
168
+ % ", ".join(_string(o) for o in rs["opcodes"]))
169
+ out.append(' "rules": [')
170
+ for ri, rule in enumerate(rs["rules"]):
171
+ text = self.rule(rule, rs["def"], ri, origin)
172
+ out.append(" " + text + ("," if ri + 1 < len(rs["rules"]) else ""))
173
+ out.append(" ]}" + ("," if si + 1 < len(self.gs["rulesets"]) else ""))
174
+ out += [" ],", ' "escapes": []', "}"]
175
+ return "\n".join(out) + "\n"
176
+
177
+
178
+ def stage_lines(text):
179
+ """`ruleset Name:` -> line number, for origin. Cheap and side-effect free."""
180
+ out = {}
181
+ for i, line in enumerate(text.splitlines(), 1):
182
+ s = line.strip()
183
+ if s.startswith("ruleset "):
184
+ rest = s[len("ruleset "):]
185
+ name = rest.split(":", 1)[0].strip()
186
+ out.setdefault(name, i)
187
+ return out
188
+
189
+
190
+ def emit_core(gs, source_name, source_text=None):
191
+ lines = stage_lines(source_text) if source_text else {}
192
+ return CoreJson(gs).emit(source_name, lines)