llvmlite 0.43.0__cp311-cp311-win_amd64.whl → 0.44.0__cp311-cp311-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 (47) hide show
  1. llvmlite/__init__.py +10 -3
  2. llvmlite/_version.py +2 -2
  3. llvmlite/binding/__init__.py +19 -18
  4. llvmlite/binding/analysis.py +69 -69
  5. llvmlite/binding/common.py +34 -34
  6. llvmlite/binding/context.py +39 -29
  7. llvmlite/binding/dylib.py +45 -45
  8. llvmlite/binding/executionengine.py +330 -330
  9. llvmlite/binding/ffi.py +395 -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/newpassmanagers.py +357 -0
  15. llvmlite/binding/object_file.py +82 -82
  16. llvmlite/binding/options.py +17 -17
  17. llvmlite/binding/orcjit.py +342 -342
  18. llvmlite/binding/passmanagers.py +946 -939
  19. llvmlite/binding/targets.py +520 -450
  20. llvmlite/binding/transforms.py +151 -151
  21. llvmlite/binding/typeref.py +285 -198
  22. llvmlite/binding/value.py +632 -618
  23. llvmlite/ir/__init__.py +11 -11
  24. llvmlite/ir/_utils.py +80 -80
  25. llvmlite/ir/builder.py +1120 -1119
  26. llvmlite/ir/context.py +20 -20
  27. llvmlite/ir/instructions.py +920 -893
  28. llvmlite/ir/module.py +246 -246
  29. llvmlite/ir/transforms.py +64 -64
  30. llvmlite/ir/types.py +734 -614
  31. llvmlite/ir/values.py +1217 -1217
  32. llvmlite/tests/__init__.py +57 -57
  33. llvmlite/tests/__main__.py +3 -3
  34. llvmlite/tests/customize.py +407 -407
  35. llvmlite/tests/refprune_proto.py +329 -329
  36. llvmlite/tests/test_binding.py +3208 -2585
  37. llvmlite/tests/test_ir.py +2994 -2729
  38. llvmlite/tests/test_refprune.py +730 -557
  39. llvmlite/tests/test_valuerepr.py +60 -60
  40. llvmlite/utils.py +29 -29
  41. {llvmlite-0.43.0.dist-info → llvmlite-0.44.0.dist-info}/LICENSE +24 -24
  42. {llvmlite-0.43.0.dist-info → llvmlite-0.44.0.dist-info}/LICENSE.thirdparty +225 -225
  43. {llvmlite-0.43.0.dist-info → llvmlite-0.44.0.dist-info}/METADATA +7 -6
  44. llvmlite-0.44.0.dist-info/RECORD +46 -0
  45. {llvmlite-0.43.0.dist-info → llvmlite-0.44.0.dist-info}/WHEEL +1 -1
  46. llvmlite-0.43.0.dist-info/RECORD +0 -45
  47. {llvmlite-0.43.0.dist-info → llvmlite-0.44.0.dist-info}/top_level.txt +0 -0
@@ -1,557 +1,730 @@
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()
1
+ import unittest
2
+ from llvmlite import ir
3
+ from llvmlite import binding as llvm
4
+ from llvmlite.tests import TestCase
5
+
6
+ import llvmlite.tests.refprune_proto as proto
7
+
8
+ # TODO:: Get rid of Legacy tests once completely transitioned to NewPassManager
9
+
10
+ # FIXME: Remove me once typed pointers are no longer supported.
11
+ from llvmlite import opaque_pointers_enabled
12
+
13
+
14
+ def _iterate_cases(generate_test):
15
+ def wrap(fn):
16
+ def wrapped(self):
17
+ return generate_test(self, fn)
18
+ wrapped.__doc__ = f"generated test for {fn.__module__}.{fn.__name__}"
19
+ return wrapped
20
+
21
+ for k, case_fn in proto.__dict__.items():
22
+ if k.startswith('case'):
23
+ yield f'test_{k}', wrap(case_fn)
24
+
25
+
26
+ class PassManagerMixin():
27
+
28
+ def pb(self):
29
+ llvm.initialize_native_target()
30
+ tm = llvm.Target.from_default_triple().create_target_machine()
31
+ pto = llvm.create_pipeline_tuning_options(speed_level=0, size_level=0)
32
+ return llvm.create_pass_builder(tm, pto)
33
+
34
+
35
+ class TestRefPrunePrototype(TestCase):
36
+ """
37
+ Test that the prototype is working.
38
+ """
39
+ def generate_test(self, case_gen):
40
+ nodes, edges, expected = case_gen()
41
+ got = proto.FanoutAlgorithm(nodes, edges).run()
42
+ self.assertEqual(expected, got)
43
+
44
+ # Generate tests
45
+ for name, case in _iterate_cases(generate_test):
46
+ locals()[name] = case
47
+
48
+
49
+ ptr_ty = ir.IntType(8).as_pointer()
50
+
51
+
52
+ class TestRefPrunePass(TestCase, PassManagerMixin):
53
+ """
54
+ Test that the C++ implementation matches the expected behavior as for
55
+ the prototype.
56
+
57
+ This generates a LLVM module for each test case, runs the pruner and checks
58
+ that the expected results are achieved.
59
+ """
60
+
61
+ def make_incref(self, m):
62
+ fnty = ir.FunctionType(ir.VoidType(), [ptr_ty])
63
+ return ir.Function(m, fnty, name='NRT_incref')
64
+
65
+ def make_decref(self, m):
66
+ fnty = ir.FunctionType(ir.VoidType(), [ptr_ty])
67
+ return ir.Function(m, fnty, name='NRT_decref')
68
+
69
+ def make_switcher(self, m):
70
+ fnty = ir.FunctionType(ir.IntType(32), ())
71
+ return ir.Function(m, fnty, name='switcher')
72
+
73
+ def make_brancher(self, m):
74
+ fnty = ir.FunctionType(ir.IntType(1), ())
75
+ return ir.Function(m, fnty, name='brancher')
76
+
77
+ def generate_ir(self, nodes, edges):
78
+ # Build LLVM module for the CFG
79
+ m = ir.Module()
80
+
81
+ incref_fn = self.make_incref(m)
82
+ decref_fn = self.make_decref(m)
83
+ switcher_fn = self.make_switcher(m)
84
+ brancher_fn = self.make_brancher(m)
85
+
86
+ fnty = ir.FunctionType(ir.VoidType(), [ptr_ty])
87
+ fn = ir.Function(m, fnty, name='main')
88
+ [ptr] = fn.args
89
+ ptr.name = 'mem'
90
+ # populate the BB nodes
91
+ bbmap = {}
92
+ for bb in edges:
93
+ bbmap[bb] = fn.append_basic_block(bb)
94
+ # populate the BB
95
+ builder = ir.IRBuilder()
96
+ for bb, jump_targets in edges.items():
97
+ builder.position_at_end(bbmap[bb])
98
+ # Insert increfs and decrefs
99
+ for action in nodes[bb]:
100
+ if action == 'incref':
101
+ builder.call(incref_fn, [ptr])
102
+ elif action == 'decref':
103
+ builder.call(decref_fn, [ptr])
104
+ else:
105
+ raise AssertionError('unreachable')
106
+
107
+ # Insert the terminator.
108
+ # Switch base on the number of jump targets.
109
+ n_targets = len(jump_targets)
110
+ if n_targets == 0:
111
+ builder.ret_void()
112
+ elif n_targets == 1:
113
+ [dst] = jump_targets
114
+ builder.branch(bbmap[dst])
115
+ elif n_targets == 2:
116
+ [left, right] = jump_targets
117
+ sel = builder.call(brancher_fn, ())
118
+ builder.cbranch(sel, bbmap[left], bbmap[right])
119
+ elif n_targets > 2:
120
+ sel = builder.call(switcher_fn, ())
121
+ [head, *tail] = jump_targets
122
+
123
+ sw = builder.switch(sel, default=bbmap[head])
124
+ for i, dst in enumerate(tail):
125
+ sw.add_case(sel.type(i), bbmap[dst])
126
+ else:
127
+ raise AssertionError('unreachable')
128
+
129
+ return m
130
+
131
+ def apply_refprune(self, irmod):
132
+ mod = llvm.parse_assembly(str(irmod))
133
+ pb = self.pb()
134
+ pm = pb.getModulePassManager()
135
+ pm.add_refprune_pass()
136
+ pm.run(mod, pb)
137
+ return mod
138
+
139
+ def apply_refprune_legacy(self, irmod):
140
+ mod = llvm.parse_assembly(str(irmod))
141
+ pm = llvm.ModulePassManager()
142
+ pm.add_refprune_pass()
143
+ pm.run(mod)
144
+ return mod
145
+
146
+ def check(self, mod, expected, nodes):
147
+ # preprocess incref/decref locations
148
+ d = {}
149
+ for k, vs in nodes.items():
150
+ n_incref = vs.count('incref')
151
+ n_decref = vs.count('decref')
152
+ d[k] = {'incref': n_incref, 'decref': n_decref}
153
+ for k, stats in d.items():
154
+ if expected.get(k):
155
+ stats['incref'] -= 1
156
+ for dec_bb in expected[k]:
157
+ d[dec_bb]['decref'] -= 1
158
+
159
+ # find the main function
160
+ for f in mod.functions:
161
+ if f.name == 'main':
162
+ break
163
+ # check each BB
164
+ for bb in f.blocks:
165
+ stats = d[bb.name]
166
+ text = str(bb)
167
+ n_incref = text.count('NRT_incref')
168
+ n_decref = text.count('NRT_decref')
169
+ self.assertEqual(stats['incref'], n_incref, msg=f'BB {bb}')
170
+ self.assertEqual(stats['decref'], n_decref, msg=f'BB {bb}')
171
+
172
+ def generate_test(self, case_gen):
173
+ nodes, edges, expected = case_gen()
174
+ irmod = self.generate_ir(nodes, edges)
175
+ outmod = self.apply_refprune(irmod)
176
+ self.check(outmod, expected, nodes)
177
+
178
+ def generate_test_legacy(self, case_gen):
179
+ nodes, edges, expected = case_gen()
180
+ irmod = self.generate_ir(nodes, edges)
181
+ outmod = self.apply_refprune_legacy(irmod)
182
+ self.check(outmod, expected, nodes)
183
+
184
+ # Generate tests
185
+ for name, case in _iterate_cases(generate_test):
186
+ locals()[name] = case
187
+
188
+ for name, case in _iterate_cases(generate_test_legacy):
189
+ locals()[name + "_legacy"] = case
190
+
191
+
192
+ class BaseTestByIR(TestCase, PassManagerMixin):
193
+ refprune_bitmask = 0
194
+
195
+ prologue = r"""
196
+ declare void @NRT_incref(i8* %ptr)
197
+ declare void @NRT_decref(i8* %ptr)
198
+ """
199
+
200
+ def check(self, irmod, subgraph_limit=None):
201
+ mod = llvm.parse_assembly(f"{self.prologue}\n{irmod}")
202
+ pb = self.pb()
203
+ pm = pb.getModulePassManager()
204
+ if subgraph_limit is None:
205
+ pm.add_refprune_pass(self.refprune_bitmask)
206
+ else:
207
+ pm.add_refprune_pass(self.refprune_bitmask,
208
+ subgraph_limit=subgraph_limit)
209
+ before = llvm.dump_refprune_stats()
210
+ pm.run(mod, pb)
211
+ after = llvm.dump_refprune_stats()
212
+ return mod, after - before
213
+
214
+ def check_legacy(self, irmod, subgraph_limit=None):
215
+ mod = llvm.parse_assembly(f"{self.prologue}\n{irmod}")
216
+ pm = llvm.ModulePassManager()
217
+ if subgraph_limit is None:
218
+ pm.add_refprune_pass(self.refprune_bitmask)
219
+ else:
220
+ pm.add_refprune_pass(self.refprune_bitmask,
221
+ subgraph_limit=subgraph_limit)
222
+ before = llvm.dump_refprune_stats()
223
+ pm.run(mod)
224
+ after = llvm.dump_refprune_stats()
225
+ return mod, after - before
226
+
227
+
228
+ class TestPerBB(BaseTestByIR):
229
+ refprune_bitmask = llvm.RefPruneSubpasses.PER_BB
230
+
231
+ per_bb_ir_1 = r"""
232
+ define void @main(i8* %ptr) {
233
+ call void @NRT_incref(i8* %ptr)
234
+ call void @NRT_decref(i8* %ptr)
235
+ ret void
236
+ }
237
+ """
238
+
239
+ def test_per_bb_1(self):
240
+ mod, stats = self.check(self.per_bb_ir_1)
241
+ self.assertEqual(stats.basicblock, 2)
242
+
243
+ def test_per_bb_1_legacy(self):
244
+ mod, stats = self.check_legacy(self.per_bb_ir_1)
245
+ self.assertEqual(stats.basicblock, 2)
246
+
247
+ per_bb_ir_2 = r"""
248
+ define void @main(i8* %ptr) {
249
+ call void @NRT_incref(i8* %ptr)
250
+ call void @NRT_incref(i8* %ptr)
251
+ call void @NRT_incref(i8* %ptr)
252
+ call void @NRT_decref(i8* %ptr)
253
+ call void @NRT_decref(i8* %ptr)
254
+ ret void
255
+ }
256
+ """
257
+
258
+ def test_per_bb_2(self):
259
+ mod, stats = self.check(self.per_bb_ir_2)
260
+ self.assertEqual(stats.basicblock, 4)
261
+ # not pruned
262
+ # FIXME: Remove `else' once TP are no longer supported.
263
+ if opaque_pointers_enabled:
264
+ self.assertIn("call void @NRT_incref(ptr %ptr)", str(mod))
265
+ else:
266
+ self.assertIn("call void @NRT_incref(i8* %ptr)", str(mod))
267
+
268
+ def test_per_bb_2_legacy(self):
269
+ mod, stats = self.check_legacy(self.per_bb_ir_2)
270
+ self.assertEqual(stats.basicblock, 4)
271
+ # not pruned
272
+ # FIXME: Remove `else' once TP are no longer supported.
273
+ if opaque_pointers_enabled:
274
+ self.assertIn("call void @NRT_incref(ptr %ptr)", str(mod))
275
+ else:
276
+ self.assertIn("call void @NRT_incref(i8* %ptr)", str(mod))
277
+
278
+ per_bb_ir_3 = r"""
279
+ define void @main(ptr %ptr, ptr %other) {
280
+ call void @NRT_incref(ptr %ptr)
281
+ call void @NRT_incref(ptr %ptr)
282
+ call void @NRT_decref(ptr %ptr)
283
+ call void @NRT_decref(ptr %other)
284
+ ret void
285
+ }
286
+ """ if opaque_pointers_enabled else r"""
287
+ define void @main(i8* %ptr, i8* %other) {
288
+ call void @NRT_incref(i8* %ptr)
289
+ call void @NRT_incref(i8* %ptr)
290
+ call void @NRT_decref(i8* %ptr)
291
+ call void @NRT_decref(i8* %other)
292
+ ret void
293
+ }
294
+ """
295
+
296
+ def test_per_bb_3(self):
297
+ mod, stats = self.check(self.per_bb_ir_3)
298
+ self.assertEqual(stats.basicblock, 2)
299
+ # not pruned
300
+ # FIXME: Remove `else' once TP are no longer supported.
301
+ if opaque_pointers_enabled:
302
+ self.assertIn("call void @NRT_decref(ptr %other)", str(mod))
303
+ else:
304
+ self.assertIn("call void @NRT_decref(i8* %other)", str(mod))
305
+
306
+ def test_per_bb_3_legacy(self):
307
+ mod, stats = self.check_legacy(self.per_bb_ir_3)
308
+ self.assertEqual(stats.basicblock, 2)
309
+ # not pruned
310
+ # FIXME: Remove `else' once TP are no longer supported.
311
+ if opaque_pointers_enabled:
312
+ self.assertIn("call void @NRT_decref(ptr %other)", str(mod))
313
+ else:
314
+ self.assertIn("call void @NRT_decref(i8* %other)", str(mod))
315
+
316
+ per_bb_ir_4 = r"""
317
+ ; reordered
318
+ define void @main(ptr %ptr, ptr %other) {
319
+ call void @NRT_incref(ptr %ptr)
320
+ call void @NRT_decref(ptr %ptr)
321
+ call void @NRT_decref(ptr %ptr)
322
+ call void @NRT_decref(ptr %other)
323
+ call void @NRT_incref(ptr %ptr)
324
+ ret void
325
+ }
326
+ """ if opaque_pointers_enabled else r"""
327
+ ; reordered
328
+ define void @main(i8* %ptr, i8* %other) {
329
+ call void @NRT_incref(i8* %ptr)
330
+ call void @NRT_decref(i8* %ptr)
331
+ call void @NRT_decref(i8* %ptr)
332
+ call void @NRT_decref(i8* %other)
333
+ call void @NRT_incref(i8* %ptr)
334
+ ret void
335
+ }
336
+ """
337
+
338
+ def test_per_bb_4(self):
339
+ mod, stats = self.check(self.per_bb_ir_4)
340
+ self.assertEqual(stats.basicblock, 4)
341
+ # not pruned
342
+ # FIXME: Remove `else' once TP are no longer supported.
343
+ if opaque_pointers_enabled:
344
+ self.assertIn("call void @NRT_decref(ptr %other)", str(mod))
345
+ else:
346
+ self.assertIn("call void @NRT_decref(i8* %other)", str(mod))
347
+
348
+ def test_per_bb_4_legacy(self):
349
+ mod, stats = self.check_legacy(self.per_bb_ir_4)
350
+ self.assertEqual(stats.basicblock, 4)
351
+ # not pruned
352
+ # FIXME: Remove `else' once TP are no longer supported.
353
+ if opaque_pointers_enabled:
354
+ self.assertIn("call void @NRT_decref(ptr %other)", str(mod))
355
+ else:
356
+ self.assertIn("call void @NRT_decref(i8* %other)", str(mod))
357
+
358
+
359
+ class TestDiamond(BaseTestByIR):
360
+ refprune_bitmask = llvm.RefPruneSubpasses.DIAMOND
361
+
362
+ per_diamond_1 = r"""
363
+ define void @main(i8* %ptr) {
364
+ bb_A:
365
+ call void @NRT_incref(i8* %ptr)
366
+ br label %bb_B
367
+ bb_B:
368
+ call void @NRT_decref(i8* %ptr)
369
+ ret void
370
+ }
371
+ """
372
+
373
+ def test_per_diamond_1(self):
374
+ mod, stats = self.check(self.per_diamond_1)
375
+ self.assertEqual(stats.diamond, 2)
376
+
377
+ def test_per_diamond_1_legacy(self):
378
+ mod, stats = self.check_legacy(self.per_diamond_1)
379
+ self.assertEqual(stats.diamond, 2)
380
+
381
+ per_diamond_2 = r"""
382
+ define void @main(i8* %ptr, i1 %cond) {
383
+ bb_A:
384
+ call void @NRT_incref(i8* %ptr)
385
+ br i1 %cond, label %bb_B, label %bb_C
386
+ bb_B:
387
+ br label %bb_D
388
+ bb_C:
389
+ br label %bb_D
390
+ bb_D:
391
+ call void @NRT_decref(i8* %ptr)
392
+ ret void
393
+ }
394
+ """
395
+
396
+ def test_per_diamond_2(self):
397
+ mod, stats = self.check(self.per_diamond_2)
398
+ self.assertEqual(stats.diamond, 2)
399
+
400
+ def test_per_diamond_2_legacy(self):
401
+ mod, stats = self.check_legacy(self.per_diamond_2)
402
+ self.assertEqual(stats.diamond, 2)
403
+
404
+ per_diamond_3 = r"""
405
+ define void @main(i8* %ptr, i1 %cond) {
406
+ bb_A:
407
+ call void @NRT_incref(i8* %ptr)
408
+ br i1 %cond, label %bb_B, label %bb_C
409
+ bb_B:
410
+ br label %bb_D
411
+ bb_C:
412
+ call void @NRT_decref(i8* %ptr) ; reject because of decref in diamond
413
+ br label %bb_D
414
+ bb_D:
415
+ call void @NRT_decref(i8* %ptr)
416
+ ret void
417
+ }
418
+ """
419
+
420
+ def test_per_diamond_3(self):
421
+ mod, stats = self.check(self.per_diamond_3)
422
+ self.assertEqual(stats.diamond, 0)
423
+
424
+ def test_per_diamond_3_legacy(self):
425
+ mod, stats = self.check_legacy(self.per_diamond_3)
426
+ self.assertEqual(stats.diamond, 0)
427
+
428
+ per_diamond_4 = r"""
429
+ define void @main(i8* %ptr, i1 %cond) {
430
+ bb_A:
431
+ call void @NRT_incref(i8* %ptr)
432
+ br i1 %cond, label %bb_B, label %bb_C
433
+ bb_B:
434
+ call void @NRT_incref(i8* %ptr) ; extra incref will not affect prune
435
+ br label %bb_D
436
+ bb_C:
437
+ br label %bb_D
438
+ bb_D:
439
+ call void @NRT_decref(i8* %ptr)
440
+ ret void
441
+ }
442
+ """
443
+
444
+ def test_per_diamond_4(self):
445
+ mod, stats = self.check(self.per_diamond_4)
446
+ self.assertEqual(stats.diamond, 2)
447
+
448
+ def test_per_diamond_4_legacy(self):
449
+ mod, stats = self.check_legacy(self.per_diamond_4)
450
+ self.assertEqual(stats.diamond, 2)
451
+
452
+ per_diamond_5 = r"""
453
+ define void @main(i8* %ptr, i1 %cond) {
454
+ bb_A:
455
+ call void @NRT_incref(i8* %ptr)
456
+ call void @NRT_incref(i8* %ptr)
457
+ br i1 %cond, label %bb_B, label %bb_C
458
+ bb_B:
459
+ br label %bb_D
460
+ bb_C:
461
+ br label %bb_D
462
+ bb_D:
463
+ call void @NRT_decref(i8* %ptr)
464
+ call void @NRT_decref(i8* %ptr)
465
+ ret void
466
+ }
467
+ """
468
+
469
+ def test_per_diamond_5(self):
470
+ mod, stats = self.check(self.per_diamond_5)
471
+ self.assertEqual(stats.diamond, 4)
472
+
473
+ def test_per_diamond_5_legacy(self):
474
+ mod, stats = self.check_legacy(self.per_diamond_5)
475
+ self.assertEqual(stats.diamond, 4)
476
+
477
+
478
+ class TestFanout(BaseTestByIR):
479
+ """More complex cases are tested in TestRefPrunePass
480
+ """
481
+
482
+ refprune_bitmask = llvm.RefPruneSubpasses.FANOUT
483
+
484
+ fanout_1 = r"""
485
+ define void @main(i8* %ptr, i1 %cond) {
486
+ bb_A:
487
+ call void @NRT_incref(i8* %ptr)
488
+ br i1 %cond, label %bb_B, label %bb_C
489
+ bb_B:
490
+ call void @NRT_decref(i8* %ptr)
491
+ ret void
492
+ bb_C:
493
+ call void @NRT_decref(i8* %ptr)
494
+ ret void
495
+ }
496
+ """
497
+
498
+ def test_fanout_1(self):
499
+ mod, stats = self.check(self.fanout_1)
500
+ self.assertEqual(stats.fanout, 3)
501
+
502
+ def test_fanout_1_legacy(self):
503
+ mod, stats = self.check_legacy(self.fanout_1)
504
+ self.assertEqual(stats.fanout, 3)
505
+
506
+ fanout_2 = r"""
507
+ define void @main(i8* %ptr, i1 %cond, i8** %excinfo) {
508
+ bb_A:
509
+ call void @NRT_incref(i8* %ptr)
510
+ br i1 %cond, label %bb_B, label %bb_C
511
+ bb_B:
512
+ call void @NRT_decref(i8* %ptr)
513
+ ret void
514
+ bb_C:
515
+ call void @NRT_decref(i8* %ptr)
516
+ br label %bb_B ; illegal jump to other decref
517
+ }
518
+ """
519
+
520
+ def test_fanout_2(self):
521
+ mod, stats = self.check(self.fanout_2)
522
+ self.assertEqual(stats.fanout, 0)
523
+
524
+ def test_fanout_2_legacy(self):
525
+ mod, stats = self.check_legacy(self.fanout_2)
526
+ self.assertEqual(stats.fanout, 0)
527
+
528
+ fanout_3 = r"""
529
+ define void @main(i8* %ptr, i1 %cond) {
530
+ bb_A:
531
+ call void @NRT_incref(i8* %ptr)
532
+ call void @NRT_incref(i8* %ptr)
533
+ br i1 %cond, label %bb_B, label %bb_C
534
+ bb_B:
535
+ call void @NRT_decref(i8* %ptr)
536
+ call void @NRT_decref(i8* %ptr)
537
+ call void @NRT_decref(i8* %ptr)
538
+ ret void
539
+ bb_C:
540
+ call void @NRT_decref(i8* %ptr)
541
+ call void @NRT_decref(i8* %ptr)
542
+ ret void
543
+ }
544
+ """
545
+
546
+ def test_fanout_3(self):
547
+ mod, stats = self.check(self.fanout_3)
548
+ self.assertEqual(stats.fanout, 6)
549
+
550
+ def test_fanout_3_limited(self):
551
+ # With subgraph limit at 1, it is essentially turning off the fanout
552
+ # pruner.
553
+ mod, stats = self.check(self.fanout_3, subgraph_limit=1)
554
+ self.assertEqual(stats.fanout, 0)
555
+
556
+ def test_fanout_3_legacy(self):
557
+ mod, stats = self.check_legacy(self.fanout_3)
558
+ self.assertEqual(stats.fanout, 6)
559
+
560
+ def test_fanout_3_limited_legacy(self):
561
+ # With subgraph limit at 1, it is essentially turning off the fanout
562
+ # pruner.
563
+ mod, stats = self.check_legacy(self.fanout_3, subgraph_limit=1)
564
+ self.assertEqual(stats.fanout, 0)
565
+
566
+
567
+ class TestFanoutRaise(BaseTestByIR):
568
+ refprune_bitmask = llvm.RefPruneSubpasses.FANOUT_RAISE
569
+
570
+ fanout_raise_1 = r"""
571
+ define i32 @main(i8* %ptr, i1 %cond, i8** %excinfo) {
572
+ bb_A:
573
+ call void @NRT_incref(i8* %ptr)
574
+ br i1 %cond, label %bb_B, label %bb_C
575
+ bb_B:
576
+ call void @NRT_decref(i8* %ptr)
577
+ ret i32 0
578
+ bb_C:
579
+ store i8* null, i8** %excinfo, !numba_exception_output !0
580
+ ret i32 1
581
+ }
582
+ !0 = !{i1 true}
583
+ """
584
+
585
+ def test_fanout_raise_1(self):
586
+ mod, stats = self.check(self.fanout_raise_1)
587
+ self.assertEqual(stats.fanout_raise, 2)
588
+
589
+ def test_fanout_raise_1_legacy(self):
590
+ mod, stats = self.check_legacy(self.fanout_raise_1)
591
+ self.assertEqual(stats.fanout_raise, 2)
592
+
593
+ fanout_raise_2 = r"""
594
+ define i32 @main(i8* %ptr, i1 %cond, i8** %excinfo) {
595
+ bb_A:
596
+ call void @NRT_incref(i8* %ptr)
597
+ br i1 %cond, label %bb_B, label %bb_C
598
+ bb_B:
599
+ call void @NRT_decref(i8* %ptr)
600
+ ret i32 0
601
+ bb_C:
602
+ store i8* null, i8** %excinfo, !numba_exception_typo !0 ; bad metadata
603
+ ret i32 1
604
+ }
605
+
606
+ !0 = !{i1 true}
607
+ """
608
+
609
+ def test_fanout_raise_2(self):
610
+ # This is ensuring that fanout_raise is not pruning when the metadata
611
+ # is incorrectly named.
612
+ mod, stats = self.check(self.fanout_raise_2)
613
+ self.assertEqual(stats.fanout_raise, 0)
614
+
615
+ def test_fanout_raise_2_legacy(self):
616
+ # This is ensuring that fanout_raise is not pruning when the metadata
617
+ # is incorrectly named.
618
+ mod, stats = self.check_legacy(self.fanout_raise_2)
619
+ self.assertEqual(stats.fanout_raise, 0)
620
+
621
+ fanout_raise_3 = r"""
622
+ define i32 @main(i8* %ptr, i1 %cond, i8** %excinfo) {
623
+ bb_A:
624
+ call void @NRT_incref(i8* %ptr)
625
+ br i1 %cond, label %bb_B, label %bb_C
626
+ bb_B:
627
+ call void @NRT_decref(i8* %ptr)
628
+ ret i32 0
629
+ bb_C:
630
+ store i8* null, i8** %excinfo, !numba_exception_output !0
631
+ ret i32 1
632
+ }
633
+
634
+ !0 = !{i32 1} ; ok; use i32
635
+ """
636
+
637
+ def test_fanout_raise_3(self):
638
+ mod, stats = self.check(self.fanout_raise_3)
639
+ self.assertEqual(stats.fanout_raise, 2)
640
+
641
+ def test_fanout_raise_3_legacy(self):
642
+ mod, stats = self.check_legacy(self.fanout_raise_3)
643
+ self.assertEqual(stats.fanout_raise, 2)
644
+
645
+ fanout_raise_4 = r"""
646
+ define i32 @main(i8* %ptr, i1 %cond, i8** %excinfo) {
647
+ bb_A:
648
+ call void @NRT_incref(i8* %ptr)
649
+ br i1 %cond, label %bb_B, label %bb_C
650
+ bb_B:
651
+ ret i32 1 ; BAD; all tails are raising without decref
652
+ bb_C:
653
+ ret i32 1 ; BAD; all tails are raising without decref
654
+ }
655
+
656
+ !0 = !{i1 1}
657
+ """
658
+
659
+ def test_fanout_raise_4(self):
660
+ mod, stats = self.check(self.fanout_raise_4)
661
+ self.assertEqual(stats.fanout_raise, 0)
662
+
663
+ def test_fanout_raise_4_legacy(self):
664
+ mod, stats = self.check_legacy(self.fanout_raise_4)
665
+ self.assertEqual(stats.fanout_raise, 0)
666
+
667
+ fanout_raise_5 = r"""
668
+ define i32 @main(i8* %ptr, i1 %cond, i8** %excinfo) {
669
+ bb_A:
670
+ call void @NRT_incref(i8* %ptr)
671
+ br i1 %cond, label %bb_B, label %bb_C
672
+ bb_B:
673
+ call void @NRT_decref(i8* %ptr)
674
+ br label %common.ret
675
+ bb_C:
676
+ store i8* null, i8** %excinfo, !numba_exception_output !0
677
+ br label %common.ret
678
+ common.ret:
679
+ %common.ret.op = phi i32 [ 0, %bb_B ], [ 1, %bb_C ]
680
+ ret i32 %common.ret.op
681
+ }
682
+ !0 = !{i1 1}
683
+ """
684
+
685
+ def test_fanout_raise_5(self):
686
+ mod, stats = self.check(self.fanout_raise_5)
687
+ self.assertEqual(stats.fanout_raise, 2)
688
+
689
+ def test_fanout_raise_5_legacy(self):
690
+ mod, stats = self.check_legacy(self.fanout_raise_5)
691
+ self.assertEqual(stats.fanout_raise, 2)
692
+
693
+ # test case 6 is from https://github.com/numba/llvmlite/issues/1023
694
+ fanout_raise_6 = r"""
695
+ define i32 @main(i8* %ptr, i1 %cond1, i1 %cond2, i1 %cond3, i8** %excinfo) {
696
+ bb_A:
697
+ call void @NRT_incref(i8* %ptr)
698
+ call void @NRT_incref(i8* %ptr)
699
+ br i1 %cond1, label %bb_B, label %bb_C
700
+ bb_B:
701
+ call void @NRT_decref(i8* %ptr)
702
+ br i1 %cond2, label %bb_D, label %bb_E
703
+ bb_C:
704
+ store i8* null, i8** %excinfo, !numba_exception_output !0
705
+ ret i32 1
706
+ bb_D:
707
+ call void @NRT_decref(i8* %ptr)
708
+ ret i32 0
709
+ bb_E:
710
+ call void @NRT_incref(i8* %ptr)
711
+ br i1 %cond3, label %bb_F, label %bb_C
712
+ bb_F:
713
+ call void @NRT_decref(i8* %ptr)
714
+ call void @NRT_decref(i8* %ptr)
715
+ ret i32 0
716
+ }
717
+ !0 = !{i1 1}
718
+ """
719
+
720
+ def test_fanout_raise_6(self):
721
+ mod, stats = self.check(self.fanout_raise_6)
722
+ self.assertEqual(stats.fanout_raise, 7)
723
+
724
+ def test_fanout_raise_6_legacy(self):
725
+ mod, stats = self.check_legacy(self.fanout_raise_6)
726
+ self.assertEqual(stats.fanout_raise, 7)
727
+
728
+
729
+ if __name__ == '__main__':
730
+ unittest.main()