llvmlite 0.42.0rc1__cp39-cp39-win_amd64.whl → 0.43.0rc1__cp39-cp39-win_amd64.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.

Files changed (46) hide show
  1. llvmlite/__init__.py +3 -3
  2. llvmlite/_version.py +2 -2
  3. llvmlite/binding/__init__.py +18 -18
  4. llvmlite/binding/analysis.py +69 -69
  5. llvmlite/binding/common.py +34 -34
  6. llvmlite/binding/context.py +29 -29
  7. llvmlite/binding/dylib.py +45 -45
  8. llvmlite/binding/executionengine.py +330 -330
  9. llvmlite/binding/ffi.py +390 -390
  10. llvmlite/binding/initfini.py +73 -73
  11. llvmlite/binding/linker.py +20 -20
  12. llvmlite/binding/llvmlite.dll +0 -0
  13. llvmlite/binding/module.py +349 -349
  14. llvmlite/binding/object_file.py +82 -82
  15. llvmlite/binding/options.py +17 -17
  16. llvmlite/binding/orcjit.py +342 -342
  17. llvmlite/binding/passmanagers.py +939 -932
  18. llvmlite/binding/targets.py +450 -450
  19. llvmlite/binding/transforms.py +151 -151
  20. llvmlite/binding/typeref.py +198 -198
  21. llvmlite/binding/value.py +618 -618
  22. llvmlite/ir/__init__.py +11 -11
  23. llvmlite/ir/_utils.py +80 -80
  24. llvmlite/ir/builder.py +1119 -1119
  25. llvmlite/ir/context.py +20 -20
  26. llvmlite/ir/instructions.py +893 -893
  27. llvmlite/ir/module.py +246 -246
  28. llvmlite/ir/transforms.py +64 -64
  29. llvmlite/ir/types.py +614 -614
  30. llvmlite/ir/values.py +1217 -1217
  31. llvmlite/tests/__init__.py +57 -57
  32. llvmlite/tests/__main__.py +3 -3
  33. llvmlite/tests/customize.py +407 -407
  34. llvmlite/tests/refprune_proto.py +329 -329
  35. llvmlite/tests/test_binding.py +2585 -2582
  36. llvmlite/tests/test_ir.py +2729 -2729
  37. llvmlite/tests/test_refprune.py +557 -526
  38. llvmlite/tests/test_valuerepr.py +60 -60
  39. llvmlite/utils.py +29 -29
  40. {llvmlite-0.42.0rc1.dist-info → llvmlite-0.43.0rc1.dist-info}/LICENSE +24 -24
  41. {llvmlite-0.42.0rc1.dist-info → llvmlite-0.43.0rc1.dist-info}/LICENSE.thirdparty +225 -225
  42. {llvmlite-0.42.0rc1.dist-info → llvmlite-0.43.0rc1.dist-info}/METADATA +1 -1
  43. llvmlite-0.43.0rc1.dist-info/RECORD +45 -0
  44. {llvmlite-0.42.0rc1.dist-info → llvmlite-0.43.0rc1.dist-info}/WHEEL +1 -1
  45. llvmlite-0.42.0rc1.dist-info/RECORD +0 -45
  46. {llvmlite-0.42.0rc1.dist-info → llvmlite-0.43.0rc1.dist-info}/top_level.txt +0 -0
@@ -1,526 +1,557 @@
1
- import unittest
2
- from llvmlite import ir
3
- from llvmlite import binding as llvm
4
- from llvmlite.tests import TestCase
5
-
6
- from . import refprune_proto as proto
7
-
8
-
9
- def _iterate_cases(generate_test):
10
- def wrap(fn):
11
- def wrapped(self):
12
- return generate_test(self, fn)
13
- wrapped.__doc__ = f"generated test for {fn.__module__}.{fn.__name__}"
14
- return wrapped
15
-
16
- for k, case_fn in proto.__dict__.items():
17
- if k.startswith('case'):
18
- yield f'test_{k}', wrap(case_fn)
19
-
20
-
21
- class TestRefPrunePrototype(TestCase):
22
- """
23
- Test that the prototype is working.
24
- """
25
- def generate_test(self, case_gen):
26
- nodes, edges, expected = case_gen()
27
- got = proto.FanoutAlgorithm(nodes, edges).run()
28
- self.assertEqual(expected, got)
29
-
30
- # Generate tests
31
- for name, case in _iterate_cases(generate_test):
32
- locals()[name] = case
33
-
34
-
35
- ptr_ty = ir.IntType(8).as_pointer()
36
-
37
-
38
- class TestRefPrunePass(TestCase):
39
- """
40
- Test that the C++ implementation matches the expected behavior as for
41
- the prototype.
42
-
43
- This generates a LLVM module for each test case, runs the pruner and checks
44
- that the expected results are achieved.
45
- """
46
-
47
- def make_incref(self, m):
48
- fnty = ir.FunctionType(ir.VoidType(), [ptr_ty])
49
- return ir.Function(m, fnty, name='NRT_incref')
50
-
51
- def make_decref(self, m):
52
- fnty = ir.FunctionType(ir.VoidType(), [ptr_ty])
53
- return ir.Function(m, fnty, name='NRT_decref')
54
-
55
- def make_switcher(self, m):
56
- fnty = ir.FunctionType(ir.IntType(32), ())
57
- return ir.Function(m, fnty, name='switcher')
58
-
59
- def make_brancher(self, m):
60
- fnty = ir.FunctionType(ir.IntType(1), ())
61
- return ir.Function(m, fnty, name='brancher')
62
-
63
- def generate_ir(self, nodes, edges):
64
- # Build LLVM module for the CFG
65
- m = ir.Module()
66
-
67
- incref_fn = self.make_incref(m)
68
- decref_fn = self.make_decref(m)
69
- switcher_fn = self.make_switcher(m)
70
- brancher_fn = self.make_brancher(m)
71
-
72
- fnty = ir.FunctionType(ir.VoidType(), [ptr_ty])
73
- fn = ir.Function(m, fnty, name='main')
74
- [ptr] = fn.args
75
- ptr.name = 'mem'
76
- # populate the BB nodes
77
- bbmap = {}
78
- for bb in edges:
79
- bbmap[bb] = fn.append_basic_block(bb)
80
- # populate the BB
81
- builder = ir.IRBuilder()
82
- for bb, jump_targets in edges.items():
83
- builder.position_at_end(bbmap[bb])
84
- # Insert increfs and decrefs
85
- for action in nodes[bb]:
86
- if action == 'incref':
87
- builder.call(incref_fn, [ptr])
88
- elif action == 'decref':
89
- builder.call(decref_fn, [ptr])
90
- else:
91
- raise AssertionError('unreachable')
92
-
93
- # Insert the terminator.
94
- # Switch base on the number of jump targets.
95
- n_targets = len(jump_targets)
96
- if n_targets == 0:
97
- builder.ret_void()
98
- elif n_targets == 1:
99
- [dst] = jump_targets
100
- builder.branch(bbmap[dst])
101
- elif n_targets == 2:
102
- [left, right] = jump_targets
103
- sel = builder.call(brancher_fn, ())
104
- builder.cbranch(sel, bbmap[left], bbmap[right])
105
- elif n_targets > 2:
106
- sel = builder.call(switcher_fn, ())
107
- [head, *tail] = jump_targets
108
-
109
- sw = builder.switch(sel, default=bbmap[head])
110
- for i, dst in enumerate(tail):
111
- sw.add_case(sel.type(i), bbmap[dst])
112
- else:
113
- raise AssertionError('unreachable')
114
-
115
- return m
116
-
117
- def apply_refprune(self, irmod):
118
- mod = llvm.parse_assembly(str(irmod))
119
- pm = llvm.ModulePassManager()
120
- pm.add_refprune_pass()
121
- pm.run(mod)
122
- return mod
123
-
124
- def check(self, mod, expected, nodes):
125
- # preprocess incref/decref locations
126
- d = {}
127
- for k, vs in nodes.items():
128
- n_incref = vs.count('incref')
129
- n_decref = vs.count('decref')
130
- d[k] = {'incref': n_incref, 'decref': n_decref}
131
- for k, stats in d.items():
132
- if expected.get(k):
133
- stats['incref'] -= 1
134
- for dec_bb in expected[k]:
135
- d[dec_bb]['decref'] -= 1
136
-
137
- # find the main function
138
- for f in mod.functions:
139
- if f.name == 'main':
140
- break
141
- # check each BB
142
- for bb in f.blocks:
143
- stats = d[bb.name]
144
- text = str(bb)
145
- n_incref = text.count('NRT_incref')
146
- n_decref = text.count('NRT_decref')
147
- self.assertEqual(stats['incref'], n_incref, msg=f'BB {bb}')
148
- self.assertEqual(stats['decref'], n_decref, msg=f'BB {bb}')
149
-
150
- def generate_test(self, case_gen):
151
- nodes, edges, expected = case_gen()
152
- irmod = self.generate_ir(nodes, edges)
153
- outmod = self.apply_refprune(irmod)
154
- self.check(outmod, expected, nodes)
155
-
156
- # Generate tests
157
- for name, case in _iterate_cases(generate_test):
158
- locals()[name] = case
159
-
160
-
161
- class BaseTestByIR(TestCase):
162
- refprune_bitmask = 0
163
-
164
- prologue = r"""
165
- declare void @NRT_incref(i8* %ptr)
166
- declare void @NRT_decref(i8* %ptr)
167
- """
168
-
169
- def check(self, irmod, subgraph_limit=None):
170
- mod = llvm.parse_assembly(f"{self.prologue}\n{irmod}")
171
- pm = llvm.ModulePassManager()
172
- if subgraph_limit is None:
173
- pm.add_refprune_pass(self.refprune_bitmask)
174
- else:
175
- pm.add_refprune_pass(self.refprune_bitmask,
176
- subgraph_limit=subgraph_limit)
177
- before = llvm.dump_refprune_stats()
178
- pm.run(mod)
179
- after = llvm.dump_refprune_stats()
180
- return mod, after - before
181
-
182
-
183
- class TestPerBB(BaseTestByIR):
184
- refprune_bitmask = llvm.RefPruneSubpasses.PER_BB
185
-
186
- per_bb_ir_1 = r"""
187
- define void @main(i8* %ptr) {
188
- call void @NRT_incref(i8* %ptr)
189
- call void @NRT_decref(i8* %ptr)
190
- ret void
191
- }
192
- """
193
-
194
- def test_per_bb_1(self):
195
- mod, stats = self.check(self.per_bb_ir_1)
196
- self.assertEqual(stats.basicblock, 2)
197
-
198
- per_bb_ir_2 = r"""
199
- define void @main(i8* %ptr) {
200
- call void @NRT_incref(i8* %ptr)
201
- call void @NRT_incref(i8* %ptr)
202
- call void @NRT_incref(i8* %ptr)
203
- call void @NRT_decref(i8* %ptr)
204
- call void @NRT_decref(i8* %ptr)
205
- ret void
206
- }
207
- """
208
-
209
- def test_per_bb_2(self):
210
- mod, stats = self.check(self.per_bb_ir_2)
211
- self.assertEqual(stats.basicblock, 4)
212
- # not pruned
213
- self.assertIn("call void @NRT_incref(i8* %ptr)", str(mod))
214
-
215
- per_bb_ir_3 = r"""
216
- define void @main(i8* %ptr, i8* %other) {
217
- call void @NRT_incref(i8* %ptr)
218
- call void @NRT_incref(i8* %ptr)
219
- call void @NRT_decref(i8* %ptr)
220
- call void @NRT_decref(i8* %other)
221
- ret void
222
- }
223
- """
224
-
225
- def test_per_bb_3(self):
226
- mod, stats = self.check(self.per_bb_ir_3)
227
- self.assertEqual(stats.basicblock, 2)
228
- # not pruned
229
- self.assertIn("call void @NRT_decref(i8* %other)", str(mod))
230
-
231
- per_bb_ir_4 = r"""
232
- ; reordered
233
- define void @main(i8* %ptr, i8* %other) {
234
- call void @NRT_incref(i8* %ptr)
235
- call void @NRT_decref(i8* %ptr)
236
- call void @NRT_decref(i8* %ptr)
237
- call void @NRT_decref(i8* %other)
238
- call void @NRT_incref(i8* %ptr)
239
- ret void
240
- }
241
- """
242
-
243
- def test_per_bb_4(self):
244
- mod, stats = self.check(self.per_bb_ir_4)
245
- self.assertEqual(stats.basicblock, 4)
246
- # not pruned
247
- self.assertIn("call void @NRT_decref(i8* %other)", str(mod))
248
-
249
-
250
- class TestDiamond(BaseTestByIR):
251
- refprune_bitmask = llvm.RefPruneSubpasses.DIAMOND
252
-
253
- per_diamond_1 = r"""
254
- define void @main(i8* %ptr) {
255
- bb_A:
256
- call void @NRT_incref(i8* %ptr)
257
- br label %bb_B
258
- bb_B:
259
- call void @NRT_decref(i8* %ptr)
260
- ret void
261
- }
262
- """
263
-
264
- def test_per_diamond_1(self):
265
- mod, stats = self.check(self.per_diamond_1)
266
- self.assertEqual(stats.diamond, 2)
267
-
268
- per_diamond_2 = r"""
269
- define void @main(i8* %ptr, i1 %cond) {
270
- bb_A:
271
- call void @NRT_incref(i8* %ptr)
272
- br i1 %cond, label %bb_B, label %bb_C
273
- bb_B:
274
- br label %bb_D
275
- bb_C:
276
- br label %bb_D
277
- bb_D:
278
- call void @NRT_decref(i8* %ptr)
279
- ret void
280
- }
281
- """
282
-
283
- def test_per_diamond_2(self):
284
- mod, stats = self.check(self.per_diamond_2)
285
- self.assertEqual(stats.diamond, 2)
286
-
287
- per_diamond_3 = r"""
288
- define void @main(i8* %ptr, i1 %cond) {
289
- bb_A:
290
- call void @NRT_incref(i8* %ptr)
291
- br i1 %cond, label %bb_B, label %bb_C
292
- bb_B:
293
- br label %bb_D
294
- bb_C:
295
- call void @NRT_decref(i8* %ptr) ; reject because of decref in diamond
296
- br label %bb_D
297
- bb_D:
298
- call void @NRT_decref(i8* %ptr)
299
- ret void
300
- }
301
- """
302
-
303
- def test_per_diamond_3(self):
304
- mod, stats = self.check(self.per_diamond_3)
305
- self.assertEqual(stats.diamond, 0)
306
-
307
- per_diamond_4 = r"""
308
- define void @main(i8* %ptr, i1 %cond) {
309
- bb_A:
310
- call void @NRT_incref(i8* %ptr)
311
- br i1 %cond, label %bb_B, label %bb_C
312
- bb_B:
313
- call void @NRT_incref(i8* %ptr) ; extra incref will not affect prune
314
- br label %bb_D
315
- bb_C:
316
- br label %bb_D
317
- bb_D:
318
- call void @NRT_decref(i8* %ptr)
319
- ret void
320
- }
321
- """
322
-
323
- def test_per_diamond_4(self):
324
- mod, stats = self.check(self.per_diamond_4)
325
- self.assertEqual(stats.diamond, 2)
326
-
327
- per_diamond_5 = r"""
328
- define void @main(i8* %ptr, i1 %cond) {
329
- bb_A:
330
- call void @NRT_incref(i8* %ptr)
331
- call void @NRT_incref(i8* %ptr)
332
- br i1 %cond, label %bb_B, label %bb_C
333
- bb_B:
334
- br label %bb_D
335
- bb_C:
336
- br label %bb_D
337
- bb_D:
338
- call void @NRT_decref(i8* %ptr)
339
- call void @NRT_decref(i8* %ptr)
340
- ret void
341
- }
342
- """
343
-
344
- def test_per_diamond_5(self):
345
- mod, stats = self.check(self.per_diamond_5)
346
- self.assertEqual(stats.diamond, 4)
347
-
348
-
349
- class TestFanout(BaseTestByIR):
350
- """More complex cases are tested in TestRefPrunePass
351
- """
352
-
353
- refprune_bitmask = llvm.RefPruneSubpasses.FANOUT
354
-
355
- fanout_1 = r"""
356
- define void @main(i8* %ptr, i1 %cond) {
357
- bb_A:
358
- call void @NRT_incref(i8* %ptr)
359
- br i1 %cond, label %bb_B, label %bb_C
360
- bb_B:
361
- call void @NRT_decref(i8* %ptr)
362
- ret void
363
- bb_C:
364
- call void @NRT_decref(i8* %ptr)
365
- ret void
366
- }
367
- """
368
-
369
- def test_fanout_1(self):
370
- mod, stats = self.check(self.fanout_1)
371
- self.assertEqual(stats.fanout, 3)
372
-
373
- fanout_2 = r"""
374
- define void @main(i8* %ptr, i1 %cond, i8** %excinfo) {
375
- bb_A:
376
- call void @NRT_incref(i8* %ptr)
377
- br i1 %cond, label %bb_B, label %bb_C
378
- bb_B:
379
- call void @NRT_decref(i8* %ptr)
380
- ret void
381
- bb_C:
382
- call void @NRT_decref(i8* %ptr)
383
- br label %bb_B ; illegal jump to other decref
384
- }
385
- """
386
-
387
- def test_fanout_2(self):
388
- mod, stats = self.check(self.fanout_2)
389
- self.assertEqual(stats.fanout, 0)
390
-
391
- fanout_3 = r"""
392
- define void @main(i8* %ptr, i1 %cond) {
393
- bb_A:
394
- call void @NRT_incref(i8* %ptr)
395
- call void @NRT_incref(i8* %ptr)
396
- br i1 %cond, label %bb_B, label %bb_C
397
- bb_B:
398
- call void @NRT_decref(i8* %ptr)
399
- call void @NRT_decref(i8* %ptr)
400
- call void @NRT_decref(i8* %ptr)
401
- ret void
402
- bb_C:
403
- call void @NRT_decref(i8* %ptr)
404
- call void @NRT_decref(i8* %ptr)
405
- ret void
406
- }
407
- """
408
-
409
- def test_fanout_3(self):
410
- mod, stats = self.check(self.fanout_3)
411
- self.assertEqual(stats.fanout, 6)
412
-
413
- def test_fanout_3_limited(self):
414
- # With subgraph limit at 1, it is essentially turning off the fanout
415
- # pruner.
416
- mod, stats = self.check(self.fanout_3, subgraph_limit=1)
417
- self.assertEqual(stats.fanout, 0)
418
-
419
-
420
- class TestFanoutRaise(BaseTestByIR):
421
- refprune_bitmask = llvm.RefPruneSubpasses.FANOUT_RAISE
422
-
423
- fanout_raise_1 = r"""
424
- define i32 @main(i8* %ptr, i1 %cond, i8** %excinfo) {
425
- bb_A:
426
- call void @NRT_incref(i8* %ptr)
427
- br i1 %cond, label %bb_B, label %bb_C
428
- bb_B:
429
- call void @NRT_decref(i8* %ptr)
430
- ret i32 0
431
- bb_C:
432
- store i8* null, i8** %excinfo, !numba_exception_output !0
433
- ret i32 1
434
- }
435
- !0 = !{i1 true}
436
- """
437
-
438
- def test_fanout_raise_1(self):
439
- mod, stats = self.check(self.fanout_raise_1)
440
- self.assertEqual(stats.fanout_raise, 2)
441
-
442
- fanout_raise_2 = r"""
443
- define i32 @main(i8* %ptr, i1 %cond, i8** %excinfo) {
444
- bb_A:
445
- call void @NRT_incref(i8* %ptr)
446
- br i1 %cond, label %bb_B, label %bb_C
447
- bb_B:
448
- call void @NRT_decref(i8* %ptr)
449
- ret i32 0
450
- bb_C:
451
- store i8* null, i8** %excinfo, !numba_exception_typo !0 ; bad metadata
452
- ret i32 1
453
- }
454
-
455
- !0 = !{i1 true}
456
- """
457
-
458
- def test_fanout_raise_2(self):
459
- # This is ensuring that fanout_raise is not pruning when the metadata
460
- # is incorrectly named.
461
- mod, stats = self.check(self.fanout_raise_2)
462
- self.assertEqual(stats.fanout_raise, 0)
463
-
464
- fanout_raise_3 = r"""
465
- define i32 @main(i8* %ptr, i1 %cond, i8** %excinfo) {
466
- bb_A:
467
- call void @NRT_incref(i8* %ptr)
468
- br i1 %cond, label %bb_B, label %bb_C
469
- bb_B:
470
- call void @NRT_decref(i8* %ptr)
471
- ret i32 0
472
- bb_C:
473
- store i8* null, i8** %excinfo, !numba_exception_output !0
474
- ret i32 1
475
- }
476
-
477
- !0 = !{i32 1} ; ok; use i32
478
- """
479
-
480
- def test_fanout_raise_3(self):
481
- mod, stats = self.check(self.fanout_raise_3)
482
- self.assertEqual(stats.fanout_raise, 2)
483
-
484
- fanout_raise_4 = r"""
485
- define i32 @main(i8* %ptr, i1 %cond, i8** %excinfo) {
486
- bb_A:
487
- call void @NRT_incref(i8* %ptr)
488
- br i1 %cond, label %bb_B, label %bb_C
489
- bb_B:
490
- ret i32 1 ; BAD; all tails are raising without decref
491
- bb_C:
492
- ret i32 1 ; BAD; all tails are raising without decref
493
- }
494
-
495
- !0 = !{i1 1}
496
- """
497
-
498
- def test_fanout_raise_4(self):
499
- mod, stats = self.check(self.fanout_raise_4)
500
- self.assertEqual(stats.fanout_raise, 0)
501
-
502
- fanout_raise_5 = r"""
503
- define i32 @main(i8* %ptr, i1 %cond, i8** %excinfo) {
504
- bb_A:
505
- call void @NRT_incref(i8* %ptr)
506
- br i1 %cond, label %bb_B, label %bb_C
507
- bb_B:
508
- call void @NRT_decref(i8* %ptr)
509
- br label %common.ret
510
- bb_C:
511
- store i8* null, i8** %excinfo, !numba_exception_output !0
512
- br label %common.ret
513
- common.ret:
514
- %common.ret.op = phi i32 [ 0, %bb_B ], [ 1, %bb_C ]
515
- ret i32 %common.ret.op
516
- }
517
- !0 = !{i1 1}
518
- """
519
-
520
- def test_fanout_raise_5(self):
521
- mod, stats = self.check(self.fanout_raise_5)
522
- self.assertEqual(stats.fanout_raise, 2)
523
-
524
-
525
- if __name__ == '__main__':
526
- unittest.main()
1
+ import unittest
2
+ from llvmlite import ir
3
+ from llvmlite import binding as llvm
4
+ from llvmlite.tests import TestCase
5
+
6
+ from . import refprune_proto as proto
7
+
8
+
9
+ def _iterate_cases(generate_test):
10
+ def wrap(fn):
11
+ def wrapped(self):
12
+ return generate_test(self, fn)
13
+ wrapped.__doc__ = f"generated test for {fn.__module__}.{fn.__name__}"
14
+ return wrapped
15
+
16
+ for k, case_fn in proto.__dict__.items():
17
+ if k.startswith('case'):
18
+ yield f'test_{k}', wrap(case_fn)
19
+
20
+
21
+ class TestRefPrunePrototype(TestCase):
22
+ """
23
+ Test that the prototype is working.
24
+ """
25
+ def generate_test(self, case_gen):
26
+ nodes, edges, expected = case_gen()
27
+ got = proto.FanoutAlgorithm(nodes, edges).run()
28
+ self.assertEqual(expected, got)
29
+
30
+ # Generate tests
31
+ for name, case in _iterate_cases(generate_test):
32
+ locals()[name] = case
33
+
34
+
35
+ ptr_ty = ir.IntType(8).as_pointer()
36
+
37
+
38
+ class TestRefPrunePass(TestCase):
39
+ """
40
+ Test that the C++ implementation matches the expected behavior as for
41
+ the prototype.
42
+
43
+ This generates a LLVM module for each test case, runs the pruner and checks
44
+ that the expected results are achieved.
45
+ """
46
+
47
+ def make_incref(self, m):
48
+ fnty = ir.FunctionType(ir.VoidType(), [ptr_ty])
49
+ return ir.Function(m, fnty, name='NRT_incref')
50
+
51
+ def make_decref(self, m):
52
+ fnty = ir.FunctionType(ir.VoidType(), [ptr_ty])
53
+ return ir.Function(m, fnty, name='NRT_decref')
54
+
55
+ def make_switcher(self, m):
56
+ fnty = ir.FunctionType(ir.IntType(32), ())
57
+ return ir.Function(m, fnty, name='switcher')
58
+
59
+ def make_brancher(self, m):
60
+ fnty = ir.FunctionType(ir.IntType(1), ())
61
+ return ir.Function(m, fnty, name='brancher')
62
+
63
+ def generate_ir(self, nodes, edges):
64
+ # Build LLVM module for the CFG
65
+ m = ir.Module()
66
+
67
+ incref_fn = self.make_incref(m)
68
+ decref_fn = self.make_decref(m)
69
+ switcher_fn = self.make_switcher(m)
70
+ brancher_fn = self.make_brancher(m)
71
+
72
+ fnty = ir.FunctionType(ir.VoidType(), [ptr_ty])
73
+ fn = ir.Function(m, fnty, name='main')
74
+ [ptr] = fn.args
75
+ ptr.name = 'mem'
76
+ # populate the BB nodes
77
+ bbmap = {}
78
+ for bb in edges:
79
+ bbmap[bb] = fn.append_basic_block(bb)
80
+ # populate the BB
81
+ builder = ir.IRBuilder()
82
+ for bb, jump_targets in edges.items():
83
+ builder.position_at_end(bbmap[bb])
84
+ # Insert increfs and decrefs
85
+ for action in nodes[bb]:
86
+ if action == 'incref':
87
+ builder.call(incref_fn, [ptr])
88
+ elif action == 'decref':
89
+ builder.call(decref_fn, [ptr])
90
+ else:
91
+ raise AssertionError('unreachable')
92
+
93
+ # Insert the terminator.
94
+ # Switch base on the number of jump targets.
95
+ n_targets = len(jump_targets)
96
+ if n_targets == 0:
97
+ builder.ret_void()
98
+ elif n_targets == 1:
99
+ [dst] = jump_targets
100
+ builder.branch(bbmap[dst])
101
+ elif n_targets == 2:
102
+ [left, right] = jump_targets
103
+ sel = builder.call(brancher_fn, ())
104
+ builder.cbranch(sel, bbmap[left], bbmap[right])
105
+ elif n_targets > 2:
106
+ sel = builder.call(switcher_fn, ())
107
+ [head, *tail] = jump_targets
108
+
109
+ sw = builder.switch(sel, default=bbmap[head])
110
+ for i, dst in enumerate(tail):
111
+ sw.add_case(sel.type(i), bbmap[dst])
112
+ else:
113
+ raise AssertionError('unreachable')
114
+
115
+ return m
116
+
117
+ def apply_refprune(self, irmod):
118
+ mod = llvm.parse_assembly(str(irmod))
119
+ pm = llvm.ModulePassManager()
120
+ pm.add_refprune_pass()
121
+ pm.run(mod)
122
+ return mod
123
+
124
+ def check(self, mod, expected, nodes):
125
+ # preprocess incref/decref locations
126
+ d = {}
127
+ for k, vs in nodes.items():
128
+ n_incref = vs.count('incref')
129
+ n_decref = vs.count('decref')
130
+ d[k] = {'incref': n_incref, 'decref': n_decref}
131
+ for k, stats in d.items():
132
+ if expected.get(k):
133
+ stats['incref'] -= 1
134
+ for dec_bb in expected[k]:
135
+ d[dec_bb]['decref'] -= 1
136
+
137
+ # find the main function
138
+ for f in mod.functions:
139
+ if f.name == 'main':
140
+ break
141
+ # check each BB
142
+ for bb in f.blocks:
143
+ stats = d[bb.name]
144
+ text = str(bb)
145
+ n_incref = text.count('NRT_incref')
146
+ n_decref = text.count('NRT_decref')
147
+ self.assertEqual(stats['incref'], n_incref, msg=f'BB {bb}')
148
+ self.assertEqual(stats['decref'], n_decref, msg=f'BB {bb}')
149
+
150
+ def generate_test(self, case_gen):
151
+ nodes, edges, expected = case_gen()
152
+ irmod = self.generate_ir(nodes, edges)
153
+ outmod = self.apply_refprune(irmod)
154
+ self.check(outmod, expected, nodes)
155
+
156
+ # Generate tests
157
+ for name, case in _iterate_cases(generate_test):
158
+ locals()[name] = case
159
+
160
+
161
+ class BaseTestByIR(TestCase):
162
+ refprune_bitmask = 0
163
+
164
+ prologue = r"""
165
+ declare void @NRT_incref(i8* %ptr)
166
+ declare void @NRT_decref(i8* %ptr)
167
+ """
168
+
169
+ def check(self, irmod, subgraph_limit=None):
170
+ mod = llvm.parse_assembly(f"{self.prologue}\n{irmod}")
171
+ pm = llvm.ModulePassManager()
172
+ if subgraph_limit is None:
173
+ pm.add_refprune_pass(self.refprune_bitmask)
174
+ else:
175
+ pm.add_refprune_pass(self.refprune_bitmask,
176
+ subgraph_limit=subgraph_limit)
177
+ before = llvm.dump_refprune_stats()
178
+ pm.run(mod)
179
+ after = llvm.dump_refprune_stats()
180
+ return mod, after - before
181
+
182
+
183
+ class TestPerBB(BaseTestByIR):
184
+ refprune_bitmask = llvm.RefPruneSubpasses.PER_BB
185
+
186
+ per_bb_ir_1 = r"""
187
+ define void @main(i8* %ptr) {
188
+ call void @NRT_incref(i8* %ptr)
189
+ call void @NRT_decref(i8* %ptr)
190
+ ret void
191
+ }
192
+ """
193
+
194
+ def test_per_bb_1(self):
195
+ mod, stats = self.check(self.per_bb_ir_1)
196
+ self.assertEqual(stats.basicblock, 2)
197
+
198
+ per_bb_ir_2 = r"""
199
+ define void @main(i8* %ptr) {
200
+ call void @NRT_incref(i8* %ptr)
201
+ call void @NRT_incref(i8* %ptr)
202
+ call void @NRT_incref(i8* %ptr)
203
+ call void @NRT_decref(i8* %ptr)
204
+ call void @NRT_decref(i8* %ptr)
205
+ ret void
206
+ }
207
+ """
208
+
209
+ def test_per_bb_2(self):
210
+ mod, stats = self.check(self.per_bb_ir_2)
211
+ self.assertEqual(stats.basicblock, 4)
212
+ # not pruned
213
+ self.assertIn("call void @NRT_incref(i8* %ptr)", str(mod))
214
+
215
+ per_bb_ir_3 = r"""
216
+ define void @main(i8* %ptr, i8* %other) {
217
+ call void @NRT_incref(i8* %ptr)
218
+ call void @NRT_incref(i8* %ptr)
219
+ call void @NRT_decref(i8* %ptr)
220
+ call void @NRT_decref(i8* %other)
221
+ ret void
222
+ }
223
+ """
224
+
225
+ def test_per_bb_3(self):
226
+ mod, stats = self.check(self.per_bb_ir_3)
227
+ self.assertEqual(stats.basicblock, 2)
228
+ # not pruned
229
+ self.assertIn("call void @NRT_decref(i8* %other)", str(mod))
230
+
231
+ per_bb_ir_4 = r"""
232
+ ; reordered
233
+ define void @main(i8* %ptr, i8* %other) {
234
+ call void @NRT_incref(i8* %ptr)
235
+ call void @NRT_decref(i8* %ptr)
236
+ call void @NRT_decref(i8* %ptr)
237
+ call void @NRT_decref(i8* %other)
238
+ call void @NRT_incref(i8* %ptr)
239
+ ret void
240
+ }
241
+ """
242
+
243
+ def test_per_bb_4(self):
244
+ mod, stats = self.check(self.per_bb_ir_4)
245
+ self.assertEqual(stats.basicblock, 4)
246
+ # not pruned
247
+ self.assertIn("call void @NRT_decref(i8* %other)", str(mod))
248
+
249
+
250
+ class TestDiamond(BaseTestByIR):
251
+ refprune_bitmask = llvm.RefPruneSubpasses.DIAMOND
252
+
253
+ per_diamond_1 = r"""
254
+ define void @main(i8* %ptr) {
255
+ bb_A:
256
+ call void @NRT_incref(i8* %ptr)
257
+ br label %bb_B
258
+ bb_B:
259
+ call void @NRT_decref(i8* %ptr)
260
+ ret void
261
+ }
262
+ """
263
+
264
+ def test_per_diamond_1(self):
265
+ mod, stats = self.check(self.per_diamond_1)
266
+ self.assertEqual(stats.diamond, 2)
267
+
268
+ per_diamond_2 = r"""
269
+ define void @main(i8* %ptr, i1 %cond) {
270
+ bb_A:
271
+ call void @NRT_incref(i8* %ptr)
272
+ br i1 %cond, label %bb_B, label %bb_C
273
+ bb_B:
274
+ br label %bb_D
275
+ bb_C:
276
+ br label %bb_D
277
+ bb_D:
278
+ call void @NRT_decref(i8* %ptr)
279
+ ret void
280
+ }
281
+ """
282
+
283
+ def test_per_diamond_2(self):
284
+ mod, stats = self.check(self.per_diamond_2)
285
+ self.assertEqual(stats.diamond, 2)
286
+
287
+ per_diamond_3 = r"""
288
+ define void @main(i8* %ptr, i1 %cond) {
289
+ bb_A:
290
+ call void @NRT_incref(i8* %ptr)
291
+ br i1 %cond, label %bb_B, label %bb_C
292
+ bb_B:
293
+ br label %bb_D
294
+ bb_C:
295
+ call void @NRT_decref(i8* %ptr) ; reject because of decref in diamond
296
+ br label %bb_D
297
+ bb_D:
298
+ call void @NRT_decref(i8* %ptr)
299
+ ret void
300
+ }
301
+ """
302
+
303
+ def test_per_diamond_3(self):
304
+ mod, stats = self.check(self.per_diamond_3)
305
+ self.assertEqual(stats.diamond, 0)
306
+
307
+ per_diamond_4 = r"""
308
+ define void @main(i8* %ptr, i1 %cond) {
309
+ bb_A:
310
+ call void @NRT_incref(i8* %ptr)
311
+ br i1 %cond, label %bb_B, label %bb_C
312
+ bb_B:
313
+ call void @NRT_incref(i8* %ptr) ; extra incref will not affect prune
314
+ br label %bb_D
315
+ bb_C:
316
+ br label %bb_D
317
+ bb_D:
318
+ call void @NRT_decref(i8* %ptr)
319
+ ret void
320
+ }
321
+ """
322
+
323
+ def test_per_diamond_4(self):
324
+ mod, stats = self.check(self.per_diamond_4)
325
+ self.assertEqual(stats.diamond, 2)
326
+
327
+ per_diamond_5 = r"""
328
+ define void @main(i8* %ptr, i1 %cond) {
329
+ bb_A:
330
+ call void @NRT_incref(i8* %ptr)
331
+ call void @NRT_incref(i8* %ptr)
332
+ br i1 %cond, label %bb_B, label %bb_C
333
+ bb_B:
334
+ br label %bb_D
335
+ bb_C:
336
+ br label %bb_D
337
+ bb_D:
338
+ call void @NRT_decref(i8* %ptr)
339
+ call void @NRT_decref(i8* %ptr)
340
+ ret void
341
+ }
342
+ """
343
+
344
+ def test_per_diamond_5(self):
345
+ mod, stats = self.check(self.per_diamond_5)
346
+ self.assertEqual(stats.diamond, 4)
347
+
348
+
349
+ class TestFanout(BaseTestByIR):
350
+ """More complex cases are tested in TestRefPrunePass
351
+ """
352
+
353
+ refprune_bitmask = llvm.RefPruneSubpasses.FANOUT
354
+
355
+ fanout_1 = r"""
356
+ define void @main(i8* %ptr, i1 %cond) {
357
+ bb_A:
358
+ call void @NRT_incref(i8* %ptr)
359
+ br i1 %cond, label %bb_B, label %bb_C
360
+ bb_B:
361
+ call void @NRT_decref(i8* %ptr)
362
+ ret void
363
+ bb_C:
364
+ call void @NRT_decref(i8* %ptr)
365
+ ret void
366
+ }
367
+ """
368
+
369
+ def test_fanout_1(self):
370
+ mod, stats = self.check(self.fanout_1)
371
+ self.assertEqual(stats.fanout, 3)
372
+
373
+ fanout_2 = r"""
374
+ define void @main(i8* %ptr, i1 %cond, i8** %excinfo) {
375
+ bb_A:
376
+ call void @NRT_incref(i8* %ptr)
377
+ br i1 %cond, label %bb_B, label %bb_C
378
+ bb_B:
379
+ call void @NRT_decref(i8* %ptr)
380
+ ret void
381
+ bb_C:
382
+ call void @NRT_decref(i8* %ptr)
383
+ br label %bb_B ; illegal jump to other decref
384
+ }
385
+ """
386
+
387
+ def test_fanout_2(self):
388
+ mod, stats = self.check(self.fanout_2)
389
+ self.assertEqual(stats.fanout, 0)
390
+
391
+ fanout_3 = r"""
392
+ define void @main(i8* %ptr, i1 %cond) {
393
+ bb_A:
394
+ call void @NRT_incref(i8* %ptr)
395
+ call void @NRT_incref(i8* %ptr)
396
+ br i1 %cond, label %bb_B, label %bb_C
397
+ bb_B:
398
+ call void @NRT_decref(i8* %ptr)
399
+ call void @NRT_decref(i8* %ptr)
400
+ call void @NRT_decref(i8* %ptr)
401
+ ret void
402
+ bb_C:
403
+ call void @NRT_decref(i8* %ptr)
404
+ call void @NRT_decref(i8* %ptr)
405
+ ret void
406
+ }
407
+ """
408
+
409
+ def test_fanout_3(self):
410
+ mod, stats = self.check(self.fanout_3)
411
+ self.assertEqual(stats.fanout, 6)
412
+
413
+ def test_fanout_3_limited(self):
414
+ # With subgraph limit at 1, it is essentially turning off the fanout
415
+ # pruner.
416
+ mod, stats = self.check(self.fanout_3, subgraph_limit=1)
417
+ self.assertEqual(stats.fanout, 0)
418
+
419
+
420
+ class TestFanoutRaise(BaseTestByIR):
421
+ refprune_bitmask = llvm.RefPruneSubpasses.FANOUT_RAISE
422
+
423
+ fanout_raise_1 = r"""
424
+ define i32 @main(i8* %ptr, i1 %cond, i8** %excinfo) {
425
+ bb_A:
426
+ call void @NRT_incref(i8* %ptr)
427
+ br i1 %cond, label %bb_B, label %bb_C
428
+ bb_B:
429
+ call void @NRT_decref(i8* %ptr)
430
+ ret i32 0
431
+ bb_C:
432
+ store i8* null, i8** %excinfo, !numba_exception_output !0
433
+ ret i32 1
434
+ }
435
+ !0 = !{i1 true}
436
+ """
437
+
438
+ def test_fanout_raise_1(self):
439
+ mod, stats = self.check(self.fanout_raise_1)
440
+ self.assertEqual(stats.fanout_raise, 2)
441
+
442
+ fanout_raise_2 = r"""
443
+ define i32 @main(i8* %ptr, i1 %cond, i8** %excinfo) {
444
+ bb_A:
445
+ call void @NRT_incref(i8* %ptr)
446
+ br i1 %cond, label %bb_B, label %bb_C
447
+ bb_B:
448
+ call void @NRT_decref(i8* %ptr)
449
+ ret i32 0
450
+ bb_C:
451
+ store i8* null, i8** %excinfo, !numba_exception_typo !0 ; bad metadata
452
+ ret i32 1
453
+ }
454
+
455
+ !0 = !{i1 true}
456
+ """
457
+
458
+ def test_fanout_raise_2(self):
459
+ # This is ensuring that fanout_raise is not pruning when the metadata
460
+ # is incorrectly named.
461
+ mod, stats = self.check(self.fanout_raise_2)
462
+ self.assertEqual(stats.fanout_raise, 0)
463
+
464
+ fanout_raise_3 = r"""
465
+ define i32 @main(i8* %ptr, i1 %cond, i8** %excinfo) {
466
+ bb_A:
467
+ call void @NRT_incref(i8* %ptr)
468
+ br i1 %cond, label %bb_B, label %bb_C
469
+ bb_B:
470
+ call void @NRT_decref(i8* %ptr)
471
+ ret i32 0
472
+ bb_C:
473
+ store i8* null, i8** %excinfo, !numba_exception_output !0
474
+ ret i32 1
475
+ }
476
+
477
+ !0 = !{i32 1} ; ok; use i32
478
+ """
479
+
480
+ def test_fanout_raise_3(self):
481
+ mod, stats = self.check(self.fanout_raise_3)
482
+ self.assertEqual(stats.fanout_raise, 2)
483
+
484
+ fanout_raise_4 = r"""
485
+ define i32 @main(i8* %ptr, i1 %cond, i8** %excinfo) {
486
+ bb_A:
487
+ call void @NRT_incref(i8* %ptr)
488
+ br i1 %cond, label %bb_B, label %bb_C
489
+ bb_B:
490
+ ret i32 1 ; BAD; all tails are raising without decref
491
+ bb_C:
492
+ ret i32 1 ; BAD; all tails are raising without decref
493
+ }
494
+
495
+ !0 = !{i1 1}
496
+ """
497
+
498
+ def test_fanout_raise_4(self):
499
+ mod, stats = self.check(self.fanout_raise_4)
500
+ self.assertEqual(stats.fanout_raise, 0)
501
+
502
+ fanout_raise_5 = r"""
503
+ define i32 @main(i8* %ptr, i1 %cond, i8** %excinfo) {
504
+ bb_A:
505
+ call void @NRT_incref(i8* %ptr)
506
+ br i1 %cond, label %bb_B, label %bb_C
507
+ bb_B:
508
+ call void @NRT_decref(i8* %ptr)
509
+ br label %common.ret
510
+ bb_C:
511
+ store i8* null, i8** %excinfo, !numba_exception_output !0
512
+ br label %common.ret
513
+ common.ret:
514
+ %common.ret.op = phi i32 [ 0, %bb_B ], [ 1, %bb_C ]
515
+ ret i32 %common.ret.op
516
+ }
517
+ !0 = !{i1 1}
518
+ """
519
+
520
+ def test_fanout_raise_5(self):
521
+ mod, stats = self.check(self.fanout_raise_5)
522
+ self.assertEqual(stats.fanout_raise, 2)
523
+
524
+ # test case 6 is from https://github.com/numba/llvmlite/issues/1023
525
+ fanout_raise_6 = r"""
526
+ define i32 @main(i8* %ptr, i1 %cond1, i1 %cond2, i1 %cond3, i8** %excinfo) {
527
+ bb_A:
528
+ call void @NRT_incref(i8* %ptr)
529
+ call void @NRT_incref(i8* %ptr)
530
+ br i1 %cond1, label %bb_B, label %bb_C
531
+ bb_B:
532
+ call void @NRT_decref(i8* %ptr)
533
+ br i1 %cond2, label %bb_D, label %bb_E
534
+ bb_C:
535
+ store i8* null, i8** %excinfo, !numba_exception_output !0
536
+ ret i32 1
537
+ bb_D:
538
+ call void @NRT_decref(i8* %ptr)
539
+ ret i32 0
540
+ bb_E:
541
+ call void @NRT_incref(i8* %ptr)
542
+ br i1 %cond3, label %bb_F, label %bb_C
543
+ bb_F:
544
+ call void @NRT_decref(i8* %ptr)
545
+ call void @NRT_decref(i8* %ptr)
546
+ ret i32 0
547
+ }
548
+ !0 = !{i1 1}
549
+ """
550
+
551
+ def test_fanout_raise_6(self):
552
+ mod, stats = self.check(self.fanout_raise_6)
553
+ self.assertEqual(stats.fanout_raise, 7)
554
+
555
+
556
+ if __name__ == '__main__':
557
+ unittest.main()