llvmlite 0.46.0b1__cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.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.
Potentially problematic release.
This version of llvmlite might be problematic. Click here for more details.
- llvmlite/__init__.py +11 -0
- llvmlite/_version.py +11 -0
- llvmlite/binding/__init__.py +18 -0
- llvmlite/binding/analysis.py +69 -0
- llvmlite/binding/common.py +34 -0
- llvmlite/binding/config.py +143 -0
- llvmlite/binding/context.py +31 -0
- llvmlite/binding/dylib.py +45 -0
- llvmlite/binding/executionengine.py +330 -0
- llvmlite/binding/ffi.py +395 -0
- llvmlite/binding/initfini.py +85 -0
- llvmlite/binding/libllvmlite.so +0 -0
- llvmlite/binding/linker.py +20 -0
- llvmlite/binding/module.py +349 -0
- llvmlite/binding/newpassmanagers.py +1049 -0
- llvmlite/binding/object_file.py +82 -0
- llvmlite/binding/options.py +17 -0
- llvmlite/binding/orcjit.py +342 -0
- llvmlite/binding/targets.py +462 -0
- llvmlite/binding/typeref.py +267 -0
- llvmlite/binding/value.py +632 -0
- llvmlite/ir/__init__.py +11 -0
- llvmlite/ir/_utils.py +80 -0
- llvmlite/ir/builder.py +1120 -0
- llvmlite/ir/context.py +20 -0
- llvmlite/ir/instructions.py +920 -0
- llvmlite/ir/module.py +256 -0
- llvmlite/ir/transforms.py +64 -0
- llvmlite/ir/types.py +730 -0
- llvmlite/ir/values.py +1217 -0
- llvmlite/tests/__init__.py +57 -0
- llvmlite/tests/__main__.py +3 -0
- llvmlite/tests/customize.py +407 -0
- llvmlite/tests/refprune_proto.py +330 -0
- llvmlite/tests/test_binding.py +3155 -0
- llvmlite/tests/test_ir.py +3095 -0
- llvmlite/tests/test_refprune.py +574 -0
- llvmlite/tests/test_valuerepr.py +60 -0
- llvmlite/utils.py +29 -0
- llvmlite-0.46.0b1.dist-info/METADATA +145 -0
- llvmlite-0.46.0b1.dist-info/RECORD +45 -0
- llvmlite-0.46.0b1.dist-info/WHEEL +6 -0
- llvmlite-0.46.0b1.dist-info/licenses/LICENSE +24 -0
- llvmlite-0.46.0b1.dist-info/licenses/LICENSE.thirdparty +225 -0
- llvmlite-0.46.0b1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Contains tests and a prototype implementation for the fanout algorithm in
|
|
3
|
+
the LLVM refprune pass.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
try:
|
|
7
|
+
from graphviz import Digraph
|
|
8
|
+
except ImportError:
|
|
9
|
+
pass
|
|
10
|
+
from collections import defaultdict
|
|
11
|
+
|
|
12
|
+
# The entry block. It's always the same.
|
|
13
|
+
ENTRY = "A"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# The following caseNN() functions returns a 3-tuple of
|
|
17
|
+
# (nodes, edges, expected).
|
|
18
|
+
# `nodes` maps BB nodes to incref/decref inside the block.
|
|
19
|
+
# `edges` maps BB nodes to their successor BB.
|
|
20
|
+
# `expected` maps BB-node with incref to a set of BB-nodes with the decrefs, or
|
|
21
|
+
# the value can be None, indicating invalid prune.
|
|
22
|
+
|
|
23
|
+
def case1():
|
|
24
|
+
edges = {
|
|
25
|
+
"A": ["B"],
|
|
26
|
+
"B": ["C", "D"],
|
|
27
|
+
"C": [],
|
|
28
|
+
"D": ["E", "F"],
|
|
29
|
+
"E": ["G"],
|
|
30
|
+
"F": [],
|
|
31
|
+
"G": ["H", "I"],
|
|
32
|
+
"I": ["G", "F"],
|
|
33
|
+
"H": ["J", "K"],
|
|
34
|
+
"J": ["L", "M"],
|
|
35
|
+
"K": [],
|
|
36
|
+
"L": ["Z"],
|
|
37
|
+
"M": ["Z", "O", "P"],
|
|
38
|
+
"O": ["Z"],
|
|
39
|
+
"P": ["Z"],
|
|
40
|
+
"Z": [],
|
|
41
|
+
}
|
|
42
|
+
nodes = defaultdict(list)
|
|
43
|
+
nodes["D"] = ["incref"]
|
|
44
|
+
nodes["H"] = ["decref"]
|
|
45
|
+
nodes["F"] = ["decref", "decref"]
|
|
46
|
+
expected = {"D": {"H", "F"}}
|
|
47
|
+
return nodes, edges, expected
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def case2():
|
|
51
|
+
edges = {
|
|
52
|
+
"A": ["B", "C"],
|
|
53
|
+
"B": ["C"],
|
|
54
|
+
"C": [],
|
|
55
|
+
}
|
|
56
|
+
nodes = defaultdict(list)
|
|
57
|
+
nodes["A"] = ["incref"]
|
|
58
|
+
nodes["B"] = ["decref"]
|
|
59
|
+
nodes["C"] = ["decref"]
|
|
60
|
+
expected = {"A": None}
|
|
61
|
+
return nodes, edges, expected
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def case3():
|
|
65
|
+
nodes, edges, _ = case1()
|
|
66
|
+
# adds an invalid edge
|
|
67
|
+
edges["H"].append("F")
|
|
68
|
+
expected = {"D": None}
|
|
69
|
+
return nodes, edges, expected
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def case4():
|
|
73
|
+
nodes, edges, _ = case1()
|
|
74
|
+
# adds an invalid edge
|
|
75
|
+
edges["H"].append("E")
|
|
76
|
+
expected = {"D": None}
|
|
77
|
+
return nodes, edges, expected
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def case5():
|
|
81
|
+
nodes, edges, _ = case1()
|
|
82
|
+
# adds backedge to go before incref
|
|
83
|
+
edges["B"].append("I")
|
|
84
|
+
expected = {"D": None}
|
|
85
|
+
return nodes, edges, expected
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def case6():
|
|
89
|
+
nodes, edges, _ = case1()
|
|
90
|
+
# adds backedge to go before incref
|
|
91
|
+
edges["I"].append("B")
|
|
92
|
+
expected = {"D": None}
|
|
93
|
+
return nodes, edges, expected
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def case7():
|
|
97
|
+
nodes, edges, _ = case1()
|
|
98
|
+
# adds forward jump outside
|
|
99
|
+
edges["I"].append("M")
|
|
100
|
+
expected = {"D": None}
|
|
101
|
+
return nodes, edges, expected
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def case8():
|
|
105
|
+
edges = {
|
|
106
|
+
"entry:": ["A"],
|
|
107
|
+
"A": ["B", "C"],
|
|
108
|
+
"B": ["C"],
|
|
109
|
+
"C": [],
|
|
110
|
+
}
|
|
111
|
+
nodes = defaultdict(list)
|
|
112
|
+
nodes["A"] = ["incref"]
|
|
113
|
+
nodes["C"] = ["decref"]
|
|
114
|
+
expected = {"A": {"C"}}
|
|
115
|
+
return nodes, edges, expected
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def case9():
|
|
119
|
+
nodes, edges, _ = case8()
|
|
120
|
+
# adds back edge
|
|
121
|
+
edges["C"].append("B")
|
|
122
|
+
expected = {"A": None}
|
|
123
|
+
return nodes, edges, expected
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def case10():
|
|
127
|
+
nodes, edges, _ = case8()
|
|
128
|
+
# adds back edge to A
|
|
129
|
+
edges["C"].append("A")
|
|
130
|
+
expected = {"A": {"C"}}
|
|
131
|
+
return nodes, edges, expected
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def case11():
|
|
135
|
+
nodes, edges, _ = case8()
|
|
136
|
+
edges["C"].append("D")
|
|
137
|
+
edges["D"] = []
|
|
138
|
+
expected = {"A": {"C"}}
|
|
139
|
+
return nodes, edges, expected
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def case12():
|
|
143
|
+
nodes, edges, _ = case8()
|
|
144
|
+
edges["C"].append("D")
|
|
145
|
+
edges["D"] = ["A"]
|
|
146
|
+
expected = {"A": {"C"}}
|
|
147
|
+
return nodes, edges, expected
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def case13():
|
|
151
|
+
nodes, edges, _ = case8()
|
|
152
|
+
edges["C"].append("D")
|
|
153
|
+
edges["D"] = ["B"]
|
|
154
|
+
expected = {"A": None}
|
|
155
|
+
return nodes, edges, expected
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def make_predecessor_map(edges):
|
|
159
|
+
d = defaultdict(set)
|
|
160
|
+
for src, outgoings in edges.items():
|
|
161
|
+
for dst in outgoings:
|
|
162
|
+
d[dst].add(src)
|
|
163
|
+
return d
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
class FanoutAlgorithm:
|
|
167
|
+
def __init__(self, nodes, edges, verbose=False):
|
|
168
|
+
self.nodes = nodes
|
|
169
|
+
self.edges = edges
|
|
170
|
+
self.rev_edges = make_predecessor_map(edges)
|
|
171
|
+
self.print = print if verbose else self._null_print
|
|
172
|
+
|
|
173
|
+
def run(self):
|
|
174
|
+
return self.find_fanout_in_function()
|
|
175
|
+
|
|
176
|
+
def _null_print(self, *args, **kwargs):
|
|
177
|
+
pass
|
|
178
|
+
|
|
179
|
+
def find_fanout_in_function(self):
|
|
180
|
+
got = {}
|
|
181
|
+
for cur_node in self.edges:
|
|
182
|
+
for incref in (x for x in self.nodes[cur_node] if x == "incref"):
|
|
183
|
+
decref_blocks = self.find_fanout(cur_node)
|
|
184
|
+
self.print(">>", cur_node, "===", decref_blocks)
|
|
185
|
+
got[cur_node] = decref_blocks
|
|
186
|
+
return got
|
|
187
|
+
|
|
188
|
+
def find_fanout(self, head_node):
|
|
189
|
+
decref_blocks = self.find_decref_candidates(head_node)
|
|
190
|
+
self.print("candidates", decref_blocks)
|
|
191
|
+
if not decref_blocks:
|
|
192
|
+
return None
|
|
193
|
+
if not self.verify_non_overlapping(
|
|
194
|
+
head_node, decref_blocks, entry=ENTRY
|
|
195
|
+
):
|
|
196
|
+
return None
|
|
197
|
+
return set(decref_blocks)
|
|
198
|
+
|
|
199
|
+
def verify_non_overlapping(self, head_node, decref_blocks, entry):
|
|
200
|
+
self.print("verify_non_overlapping".center(80, "-"))
|
|
201
|
+
# reverse walk for each decref_blocks
|
|
202
|
+
# they should end at head_node
|
|
203
|
+
todo = list(decref_blocks)
|
|
204
|
+
while todo:
|
|
205
|
+
cur_node = todo.pop()
|
|
206
|
+
visited = set()
|
|
207
|
+
|
|
208
|
+
workstack = [cur_node]
|
|
209
|
+
del cur_node
|
|
210
|
+
while workstack:
|
|
211
|
+
cur_node = workstack.pop()
|
|
212
|
+
self.print("cur_node", cur_node, "|", workstack)
|
|
213
|
+
if cur_node in visited:
|
|
214
|
+
continue # skip
|
|
215
|
+
if cur_node == entry:
|
|
216
|
+
# Entry node
|
|
217
|
+
self.print(
|
|
218
|
+
"!! failed because we arrived at entry", cur_node
|
|
219
|
+
)
|
|
220
|
+
return False
|
|
221
|
+
visited.add(cur_node)
|
|
222
|
+
# check all predecessors
|
|
223
|
+
self.print(
|
|
224
|
+
f" {cur_node} preds {self.get_predecessors(cur_node)}"
|
|
225
|
+
)
|
|
226
|
+
for pred in self.get_predecessors(cur_node):
|
|
227
|
+
if pred in decref_blocks:
|
|
228
|
+
# reject because there's a predecessor in decref_blocks
|
|
229
|
+
self.print(
|
|
230
|
+
"!! reject because predecessor in decref_blocks"
|
|
231
|
+
)
|
|
232
|
+
return False
|
|
233
|
+
if pred != head_node:
|
|
234
|
+
|
|
235
|
+
workstack.append(pred)
|
|
236
|
+
|
|
237
|
+
return True
|
|
238
|
+
|
|
239
|
+
def get_successors(self, node):
|
|
240
|
+
return tuple(self.edges[node])
|
|
241
|
+
|
|
242
|
+
def get_predecessors(self, node):
|
|
243
|
+
return tuple(self.rev_edges[node])
|
|
244
|
+
|
|
245
|
+
def has_decref(self, node):
|
|
246
|
+
return "decref" in self.nodes[node]
|
|
247
|
+
|
|
248
|
+
def walk_child_for_decref(
|
|
249
|
+
self, cur_node, path_stack, decref_blocks, depth=10
|
|
250
|
+
):
|
|
251
|
+
indent = " " * len(path_stack)
|
|
252
|
+
self.print(indent, "walk", path_stack, cur_node)
|
|
253
|
+
if depth <= 0:
|
|
254
|
+
return False # missing
|
|
255
|
+
if cur_node in path_stack:
|
|
256
|
+
if cur_node == path_stack[0]:
|
|
257
|
+
return False # reject interior node backedge
|
|
258
|
+
return True # skip
|
|
259
|
+
if self.has_decref(cur_node):
|
|
260
|
+
decref_blocks.add(cur_node)
|
|
261
|
+
self.print(indent, "found decref")
|
|
262
|
+
return True
|
|
263
|
+
|
|
264
|
+
depth -= 1
|
|
265
|
+
path_stack += (cur_node,)
|
|
266
|
+
found = False
|
|
267
|
+
for child in self.get_successors(cur_node):
|
|
268
|
+
if not self.walk_child_for_decref(
|
|
269
|
+
child, path_stack, decref_blocks
|
|
270
|
+
):
|
|
271
|
+
found = False
|
|
272
|
+
break
|
|
273
|
+
else:
|
|
274
|
+
found = True
|
|
275
|
+
|
|
276
|
+
self.print(indent, f"ret {found}")
|
|
277
|
+
return found
|
|
278
|
+
|
|
279
|
+
def find_decref_candidates(self, cur_node):
|
|
280
|
+
# Forward pass
|
|
281
|
+
self.print("find_decref_candidates".center(80, "-"))
|
|
282
|
+
path_stack = (cur_node,)
|
|
283
|
+
found = False
|
|
284
|
+
decref_blocks = set()
|
|
285
|
+
for child in self.get_successors(cur_node):
|
|
286
|
+
if not self.walk_child_for_decref(
|
|
287
|
+
child, path_stack, decref_blocks
|
|
288
|
+
):
|
|
289
|
+
found = False
|
|
290
|
+
break
|
|
291
|
+
else:
|
|
292
|
+
found = True
|
|
293
|
+
if not found:
|
|
294
|
+
return set()
|
|
295
|
+
else:
|
|
296
|
+
return decref_blocks
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def check_once():
|
|
300
|
+
nodes, edges, expected = case13()
|
|
301
|
+
|
|
302
|
+
# Render graph
|
|
303
|
+
G = Digraph()
|
|
304
|
+
for node in edges:
|
|
305
|
+
G.node(node, shape="rect", label=f"{node}\n" + r"\l".join(nodes[node]))
|
|
306
|
+
for node, children in edges.items():
|
|
307
|
+
for child in children:
|
|
308
|
+
G.edge(node, child)
|
|
309
|
+
|
|
310
|
+
G.view()
|
|
311
|
+
|
|
312
|
+
algo = FanoutAlgorithm(nodes, edges, verbose=True)
|
|
313
|
+
got = algo.run()
|
|
314
|
+
assert expected == got
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def check_all():
|
|
318
|
+
for k, fn in list(globals().items()):
|
|
319
|
+
if k.startswith("case"):
|
|
320
|
+
print(f"{fn}".center(80, "-"))
|
|
321
|
+
nodes, edges, expected = fn()
|
|
322
|
+
algo = FanoutAlgorithm(nodes, edges)
|
|
323
|
+
got = algo.run()
|
|
324
|
+
assert expected == got
|
|
325
|
+
print("ALL PASSED")
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
if __name__ == "__main__":
|
|
329
|
+
# check_once()
|
|
330
|
+
check_all()
|