Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
numba
GitHub Repository: numba/llvmlite
Path: blob/main/ffi/custom_passes.cpp
1154 views
1
2
#include "core.h"
3
4
#include "llvm/IR/BasicBlock.h"
5
#include "llvm/IR/Function.h"
6
#include "llvm/IR/Instructions.h"
7
#include "llvm/Pass.h"
8
9
#include "llvm/ADT/SmallSet.h"
10
11
#include "llvm/Analysis/Passes.h"
12
#include "llvm/Analysis/PostDominators.h"
13
#include "llvm/Support/raw_ostream.h"
14
15
#include "llvm/InitializePasses.h"
16
#include "llvm/LinkAllPasses.h"
17
18
#include <iostream>
19
#include <vector>
20
21
// #define DEBUG_PRINT 1
22
#define DEBUG_PRINT 0
23
24
using namespace llvm;
25
26
namespace llvm {
27
struct OpaqueModulePassManager;
28
typedef OpaqueModulePassManager *LLVMModulePassManagerRef;
29
DEFINE_SIMPLE_CONVERSION_FUNCTIONS(ModulePassManager, LLVMModulePassManagerRef)
30
31
struct OpaqueFunctionPassManager;
32
typedef OpaqueFunctionPassManager *LLVMFunctionPassManagerRef;
33
DEFINE_SIMPLE_CONVERSION_FUNCTIONS(FunctionPassManager,
34
LLVMFunctionPassManagerRef)
35
} // namespace llvm
36
37
namespace {
38
/**
39
* Checks if a call instruction is an incref
40
*
41
* Parameters:
42
* - call_inst, a call instruction
43
*
44
* Returns:
45
* - true if call_inst is an incref, false otherwise
46
*/
47
bool IsIncRef(CallInst *call_inst) {
48
Value *callee = call_inst->getCalledOperand();
49
return callee->getName() == "NRT_incref";
50
}
51
52
/**
53
* Checks if a call instruction is an decref
54
*
55
* Parameters:
56
* - call_inst, a call instruction
57
*
58
* Returns:
59
* - true if call_inst is an decref, false otherwise
60
*/
61
bool IsDecRef(CallInst *call_inst) {
62
Value *callee = call_inst->getCalledOperand();
63
return callee->getName() == "NRT_decref";
64
}
65
66
/**
67
* Checks if an instruction is a "refop" (either an incref or a decref).
68
*
69
* Parameters:
70
* - ii, the instruction to check
71
*
72
* Returns:
73
* - the instruction ii, if it is a "refop", NULL otherwise
74
*/
75
CallInst *GetRefOpCall(Instruction *ii) {
76
if (ii->getOpcode() == Instruction::Call) {
77
CallInst *call_inst = dyn_cast<CallInst>(ii);
78
if (IsIncRef(call_inst) || IsDecRef(call_inst)) {
79
return call_inst;
80
}
81
}
82
return NULL;
83
}
84
85
/**
86
* RAII push-pop of elements onto a stack.
87
*
88
* Template parameter <Tstack>:
89
* - the type of the stack
90
*/
91
template <class Tstack> struct raiiStack {
92
Tstack &stack;
93
94
typedef typename Tstack::value_type value_type;
95
96
/**
97
* ctor pushes `elem` onto `stack`
98
*/
99
raiiStack(Tstack &stack, value_type &elem) : stack(stack) {
100
stack.push_back(elem);
101
}
102
/**
103
* dtor pops back of `stack`
104
*/
105
~raiiStack() { stack.pop_back(); }
106
};
107
108
/**
109
* A FunctionPass to reorder incref/decref instructions such that decrefs occur
110
* logically after increfs. This is a pre-requisite pass to the pruner passes.
111
*/
112
struct RefNormalize {
113
114
bool runOnFunction(Function &F) {
115
bool mutated = false;
116
// For each basic block in F
117
for (BasicBlock &bb : F) {
118
// This find a incref in the basic block
119
120
bool has_incref = false;
121
// check the instructions in the basic block
122
for (Instruction &ii : bb) {
123
// see if it is a refop
124
CallInst *refop = GetRefOpCall(&ii);
125
// if it is a refop and it is an incref, set flag and break
126
if (refop != NULL && IsIncRef(refop)) {
127
has_incref = true;
128
break;
129
}
130
}
131
132
// block has an incref
133
if (has_incref) {
134
// This moves decrefs to the back just before the terminator.
135
136
SmallVector<CallInst *, 10> to_be_moved;
137
// walk the instructions in the block
138
for (Instruction &ii : bb) {
139
// query the instruction, if its a refop store to refop
140
// if not store NULL to refop
141
CallInst *refop = GetRefOpCall(&ii);
142
// if the refop is not NULL and it is also a decref then
143
// shove it into the to_be_moved vector
144
if (refop != NULL && IsDecRef(refop)) {
145
to_be_moved.push_back(refop);
146
}
147
}
148
// Walk the to_be_moved vector of instructions, these are all
149
// decrefs by construction.
150
for (CallInst *decref : to_be_moved) {
151
// move the decref to a location prior to the block
152
// terminator and set mutated.
153
auto term = bb.getTerminator();
154
decref->moveBefore(term->getIterator());
155
mutated |= true;
156
}
157
}
158
}
159
return mutated;
160
}
161
};
162
163
typedef enum {
164
None = 0b0000,
165
PerBasicBlock = 0b0001,
166
Diamond = 0b0010,
167
Fanout = 0b0100,
168
FanoutRaise = 0b1000,
169
All = PerBasicBlock | Diamond | Fanout | FanoutRaise
170
} Subpasses;
171
172
struct RefPrune {
173
static char ID;
174
static size_t stats_per_bb;
175
static size_t stats_diamond;
176
static size_t stats_fanout;
177
static size_t stats_fanout_raise;
178
179
// Fixed size for how deep to recurse in the fanout case prior to giving up.
180
static const size_t FANOUT_RECURSE_DEPTH = 15;
181
typedef SmallSet<BasicBlock *, FANOUT_RECURSE_DEPTH> SmallBBSet;
182
183
DominatorTree &DT;
184
PostDominatorTree &PDT;
185
/**
186
* Enum for setting which subpasses to run, there is no interdependence.
187
*/
188
Subpasses flags;
189
190
// The maximum number of nodes that the fanout pruners will look at.
191
size_t subgraph_limit;
192
193
RefPrune(DominatorTree &DT, PostDominatorTree &PDT,
194
Subpasses flags = Subpasses::All, size_t subgraph_limit = -1)
195
: DT(DT), PDT(PDT), flags(flags), subgraph_limit(subgraph_limit) {}
196
197
bool isSubpassEnabledFor(Subpasses expected) {
198
return (flags & expected) == expected;
199
}
200
201
bool runOnFunction(Function &F) {
202
// state for LLVM function pass mutated IR
203
bool mutated = false;
204
205
// local state for capturing mutation by any selected pass, any mutation
206
// at all propagates into mutated for return.
207
bool local_mutated;
208
do {
209
local_mutated = false;
210
if (isSubpassEnabledFor(Subpasses::PerBasicBlock))
211
local_mutated |= runPerBasicBlockPrune(F);
212
if (isSubpassEnabledFor(Subpasses::Diamond))
213
local_mutated |= runDiamondPrune(F);
214
if (isSubpassEnabledFor(Subpasses::Fanout))
215
local_mutated |= runFanoutPrune(F, /*prune_raise*/ false);
216
if (isSubpassEnabledFor(Subpasses::FanoutRaise))
217
local_mutated |= runFanoutPrune(F, /*prune_raise*/ true);
218
mutated |= local_mutated;
219
} while (local_mutated);
220
221
return mutated;
222
}
223
224
/**
225
* Per BasicBlock pruning pass.
226
*
227
* Assumes all increfs are before all decrefs.
228
* Cleans up all refcount operations on NULL pointers.
229
* Cleans up all redundant incref/decref pairs.
230
*
231
* This pass works on a block at a time and does not change the CFG.
232
* Incref/Decref removal is restricted to the basic block.
233
*
234
* General idea is to be able to prune within a block as follows:
235
*
236
* ┌─────────────┐
237
* │ Block entry │
238
* | Instruction │
239
* | Incref(A) │ ──> No match, cannot remove.
240
* | Incref(B) │ ──┐
241
* | Instruction │ │ Matching pair, can be removed.
242
* | Instruction │ │
243
* | Decref(B) │ <─┘
244
* | Instruction │
245
* | Decref(NULL)│ ──> Decref on NULL, can be removed.
246
* | Terminator │
247
* └─────────────┘
248
* Parameters:
249
* - F a Function
250
*
251
* Returns:
252
* - true if pruning took place, false otherwise
253
*
254
*/
255
bool runPerBasicBlockPrune(Function &F) {
256
bool mutated = false;
257
258
// walk the basic blocks in Function F.
259
for (BasicBlock &bb : F) {
260
// allocate some buffers
261
SmallVector<CallInst *, 10> incref_list, decref_list, null_list;
262
263
// This is a scanning phase looking to classify instructions into
264
// inrefs, decrefs and operations on already NULL pointers.
265
// walk the instructions in the current basic block
266
for (Instruction &ii : bb) {
267
// If the instruction is a refop
268
CallInst *ci;
269
if ((ci = GetRefOpCall(&ii))) {
270
if (!isNonNullFirstArg(ci)) {
271
// Drop refops on NULL pointers
272
null_list.push_back(ci);
273
} else if (IsIncRef(ci)) {
274
incref_list.push_back(ci);
275
} else if (IsDecRef(ci)) {
276
decref_list.push_back(ci);
277
}
278
}
279
}
280
281
// First: Remove refops on NULL
282
for (CallInst *ci : null_list) {
283
ci->eraseFromParent();
284
mutated = true;
285
286
// Do we care about differentiating between prunes of NULL
287
// and prunes of pairs?
288
stats_per_bb += 1;
289
}
290
291
// Second: Find matching pairs of incref decref
292
while (incref_list.size() > 0) {
293
// get an incref
294
CallInst *incref = incref_list.pop_back_val();
295
// walk decrefs
296
for (size_t i = 0; i < decref_list.size(); ++i) {
297
CallInst *decref = decref_list[i];
298
// is this instruction a decref thats non-NULL and
299
// the decref related to the incref?
300
if (decref && isRelatedDecref(incref, decref)) {
301
if (DEBUG_PRINT) {
302
errs() << "Prune: matching pair in BB:\n";
303
incref->dump();
304
decref->dump();
305
incref->getParent()->dump();
306
}
307
// strip incref and decref from blck
308
incref->eraseFromParent();
309
decref->eraseFromParent();
310
311
// set stripped decref to null
312
decref_list[i] = NULL;
313
// set mutated bit and update prune stats
314
mutated = true;
315
stats_per_bb += 2;
316
break;
317
}
318
}
319
}
320
}
321
return mutated;
322
}
323
324
/**
325
* "Diamond" pruning pass.
326
*
327
* Looks to prune across basic blocks in cases where incref/decref pairs
328
* appear in a "diamond" shape CFG structure. For example:
329
*
330
* ┌────────────┐
331
* │ incref (A) │
332
* └────────────┘
333
* / \
334
* / \
335
* / \
336
* . .
337
* . .
338
* ┌────────────┐ ┌────────────┐
339
* │ MORE CFG │ │ MORE CFG │
340
* └────────────┘ └────────────┘
341
* . .
342
* . .
343
* \ /
344
* \ /
345
* \ /
346
* \ /
347
* ┌────────────┐
348
* │ decref (A) │
349
* └────────────┘
350
*
351
* Condition for prune is that, in an incref/decref pair:
352
* - the incref dominates the decref.
353
* - the decref postdominates the incref.
354
* - in the blocks in the CFG between the basic blocks containing the
355
* incref/decref pair there's no other decref present (this is
356
* conservative and is to handle aliasing of references).
357
* - that the decref is not in a cycle dominated by the incref (i.e. decref
358
* in a loop).
359
*
360
* Parameters:
361
* - F a Function
362
*
363
* Returns:
364
* - true if pruning took place, false otherwise
365
*
366
*/
367
bool runDiamondPrune(Function &F) {
368
bool mutated = false;
369
370
// Find all increfs and decrefs in the Function and store them in
371
// incref_list and decref_list respectively.
372
std::vector<CallInst *> incref_list, decref_list;
373
listRefOps(F, IsIncRef, incref_list);
374
listRefOps(F, IsDecRef, decref_list);
375
376
// Walk the incref list
377
for (CallInst *&incref : incref_list) {
378
// NULL is the token for already erased, skip on it
379
if (incref == NULL)
380
continue;
381
382
// Walk the decref_list
383
for (CallInst *&decref : decref_list) {
384
// NULL is the token for already erased, skip on it
385
if (decref == NULL)
386
continue;
387
388
// Diamond prune is for refops not in the same BB
389
if (incref->getParent() == decref->getParent())
390
continue;
391
392
// If the refops are unrelated, skip
393
if (!isRelatedDecref(incref, decref))
394
continue;
395
396
// incref DOM decref && decref POSTDOM incref
397
if (DT.dominates(incref, decref) &&
398
PDT.dominates(decref, incref)) {
399
// check that the decref cannot be executed multiple times
400
SmallBBSet tail_nodes;
401
tail_nodes.insert(decref->getParent());
402
if (!verifyFanoutBackward(incref, incref->getParent(),
403
&tail_nodes, false))
404
405
continue;
406
407
// scan the CFG between the incref and decref BBs, if
408
// there's a decref present then skip, this is conservative.
409
if (hasDecrefBetweenGraph(incref->getParent(),
410
decref->getParent())) {
411
continue;
412
} else {
413
414
if (DEBUG_PRINT) {
415
errs() << F.getName() << "-------------\n";
416
errs() << incref->getParent()->getName() << "\n";
417
incref->dump();
418
errs() << decref->getParent()->getName() << "\n";
419
decref->dump();
420
}
421
422
// erase instruction from block and set NULL marker for
423
// bookkeeping purposes
424
incref->eraseFromParent();
425
decref->eraseFromParent();
426
incref = NULL;
427
decref = NULL;
428
429
stats_diamond += 2;
430
}
431
// mark mutated
432
mutated = true;
433
break;
434
}
435
}
436
}
437
return mutated;
438
}
439
440
/**
441
* "Fan-out" pruning passes.
442
*
443
* Prunes "fan-out"s, this is where control flow from a block containing an
444
* incref "fans out" into blocks that contain corresponding decrefs.
445
*
446
* There are two supported fan-out shape CFG structures.
447
*
448
* Supported case 1, simple "fan-out" with no raise, prune occurs when the
449
* incref dominates the predecessor blocks containing associated decrefs.
450
*
451
* ┌────────────┐
452
* │ incref (A) │
453
* └────────────┘
454
* / \
455
* / \
456
* / \
457
* . .
458
* . .
459
* ┌────────────┐ ┌────────────┐
460
* │ MORE CFG │ │ MORE CFG │
461
* └────────────┘ └────────────┘
462
* . .
463
* . .
464
* ┌────────────┐ ┌────────────┐
465
* │ decref (A) │ │ decref (A) │
466
* └────────────┘ └────────────┘
467
* . .
468
* . .
469
* . .
470
*
471
*
472
* Supported case 2, simple "fan-out" with raise, prune occurs when the
473
* incref dominates the predecessor blocks containing associated decrefs
474
* with the exception of the raise block (this is to "forgive" the
475
* occasional missing decref in a raise block).
476
*
477
* ┌────────────┐
478
* │ incref (A) │
479
* └────────────┘
480
* / \
481
* / \
482
* / \
483
* . .
484
* . .
485
* ┌────────────┐ ┌────────────┐
486
* │ MORE CFG │ │ MORE CFG │
487
* └────────────┘ └────────────┘
488
* . .
489
* . .
490
* ┌────────────┐ ┌────────────┐
491
* │ decref (A) │ │ raise │
492
* └────────────┘ └────────────┘
493
* .
494
* .
495
* ┌────────────┐
496
* │ MORE CFG │
497
* └────────────┘
498
*
499
* a complex pattern about fanout-raise
500
* https://github.com/numba/llvmlite/issues/1023
501
* ┌────────────┐
502
* │ incref │
503
* │ incref │
504
* └────────────┘
505
* / \
506
* / \
507
* ┌────────────┐ \
508
* │ decref | \
509
* └────────────┘ \
510
* / \ \
511
* / \ \
512
* ┌────────────┐ ┌────────────┐ \
513
* │ decref | │ incref | \
514
* └────────────┘ └────────────┘ \
515
* / \ \
516
* / \ \
517
* ┌────────────┐ ┌────────────┐
518
* │ decref | │ raise |
519
* │ decref | └────────────┘
520
* └────────────┘
521
*
522
* Parameters:
523
* - F a Function
524
* - prune_raise_exit, if false case 1 is considered, if true case 2 is
525
* considered.
526
*
527
* Returns:
528
* - true if pruning took place, false otherwise
529
*/
530
bool runFanoutPrune(Function &F, bool prune_raise_exit) {
531
bool mutated = false;
532
533
// Find all Increfs and store them in incref_list
534
std::vector<CallInst *> incref_list;
535
listRefOps(F, IsIncRef, incref_list);
536
537
// Remember incref-blocks that will always fail.
538
SmallBBSet bad_blocks;
539
// walk the incref_list
540
for (CallInst *incref : incref_list) {
541
// Skip blocks that will always fail.
542
if (bad_blocks.count(incref->getParent())) {
543
continue; // skip
544
}
545
546
// Is there *any* decref in the parent node of the incref?
547
// If so skip this incref (considering that aliases may exist).
548
if (hasAnyDecrefInNode(incref->getParent())) {
549
// be careful of potential alias
550
continue; // skip
551
}
552
553
SmallBBSet decref_blocks;
554
// Check for the chosen "fan out" condition
555
if (findFanout(incref, bad_blocks, &decref_blocks,
556
prune_raise_exit)) {
557
if (DEBUG_PRINT) {
558
F.viewCFG();
559
errs() << "------------\n";
560
errs() << "incref " << incref->getParent()->getName()
561
<< "\n";
562
errs() << " decref_blocks.size()" << decref_blocks.size()
563
<< "\n";
564
incref->dump();
565
}
566
// Remove first related decref in each block
567
// for each block
568
for (BasicBlock *each : decref_blocks) {
569
// for each instruction
570
for (Instruction &ii : *each) {
571
CallInst *decref;
572
// walrus:
573
// is the current instruction the decref associated with
574
// the incref under consideration, if so assign to
575
// decref and continue.
576
if ((decref = isRelatedDecref(incref, &ii))) {
577
if (DEBUG_PRINT) {
578
errs()
579
<< decref->getParent()->getName() << "\n";
580
decref->dump();
581
}
582
// Remove this decref from its block
583
decref->eraseFromParent();
584
585
// update counters based on decref removal
586
if (prune_raise_exit)
587
stats_fanout_raise += 1;
588
else
589
stats_fanout += 1;
590
break;
591
}
592
}
593
}
594
// remove the incref from its block
595
incref->eraseFromParent();
596
597
// update counters based on incref removal
598
if (prune_raise_exit)
599
stats_fanout_raise += 1;
600
else
601
stats_fanout += 1;
602
mutated = true;
603
}
604
}
605
return mutated;
606
}
607
608
/**
609
* This searches for the "fan-out" condition and returns true if it is
610
* found.
611
*
612
* Parameters:
613
* - incref: the incref from which fan-out should be checked.
614
* - bad_blocks: a set of blocks that are known to not satisfy the
615
* the fanout condition. Mutated by this function.
616
* - decref_blocks: pointer to a set of basic blocks, this is mutated by
617
* this function, on return it contains the basic blocks containing
618
* decrefs related to the incref
619
* - prune_raise_exit: this is a bool to signal whether to just look for
620
* the fan-out case or also look for the fan-out with raise condition,
621
* if true the fan-out with raise condition is considered else it is
622
* not.
623
*
624
* Returns:
625
* - true if the fan-out condition specified by `prune_raise_exit` was
626
* found, false otherwise.
627
*/
628
bool findFanout(CallInst *incref, SmallBBSet &bad_blocks,
629
SmallBBSet *decref_blocks, bool prune_raise_exit) {
630
631
// get the basic block of the incref instruction
632
BasicBlock *head_node = incref->getParent();
633
634
// work space, a set of basic blocks to hold the block which contain
635
// raises, only used in the case of prune_raise_exit
636
SmallBBSet raising_blocks, *p_raising_blocks = NULL;
637
// Set up pointer to raising_blocks
638
if (prune_raise_exit)
639
p_raising_blocks = &raising_blocks;
640
641
if (findFanoutDecrefCandidates(incref, head_node, bad_blocks,
642
decref_blocks, p_raising_blocks)) {
643
if (DEBUG_PRINT) {
644
errs() << "forward pass candids.size() = "
645
<< decref_blocks->size() << "\n";
646
errs() << " " << head_node->getName() << "\n";
647
incref->dump();
648
}
649
if (decref_blocks->size() == 0) {
650
// no decref blocks
651
if (DEBUG_PRINT) {
652
errs() << "missing decref blocks = "
653
<< raising_blocks.size() << "\n";
654
}
655
return false;
656
}
657
if (prune_raise_exit) {
658
if (raising_blocks.size() == 0) {
659
// no raising blocks
660
if (DEBUG_PRINT) {
661
errs() << "missing raising blocks = "
662
<< raising_blocks.size() << "\n";
663
for (auto bb : *decref_blocks) {
664
errs() << " " << bb->getName() << "\n";
665
}
666
}
667
return false;
668
}
669
670
// combine decref_blocks into raising blocks for checking the
671
// exit node condition
672
for (BasicBlock *bb : *decref_blocks) {
673
raising_blocks.insert(bb);
674
}
675
if (verifyFanoutBackward(incref, head_node, p_raising_blocks,
676
prune_raise_exit))
677
return true;
678
679
} else if (verifyFanoutBackward(incref, head_node, decref_blocks,
680
prune_raise_exit)) {
681
return true;
682
}
683
}
684
return false;
685
}
686
687
/**
688
* Forward pass.
689
*
690
* Walk the successors of the incref node recursively until a decref
691
* or an exit node is found.
692
*
693
* In the case of a decref node, the node is added to decref_blocks only if
694
* it contains a decref associated with the incref and there is no interior
695
* back-edge to a predecessor in the current sub-graph.
696
*
697
* In the case of an exit, it must be a raise for it to be added to
698
* raising_blocks.
699
*
700
* Parameters:
701
* - incref: The incref under consideration.
702
* - cur_node: The basic block in which incref is found.
703
* - bad_blocks: a set of blocks that are known to not satisfy the
704
* the fanout condition. Mutated by this function.
705
* - decref_blocks: pointer to a set of basic blocks, it is mutated by this
706
* function and on successful return contains the basic blocks which have
707
* a decref related to the supplied incref in them.
708
* - raising_blocks: point to a set of basic blocks OR NULL. If not-NULL
709
* it is mutated by this function and on successful return contains the
710
* basic blocks which have a raise in them that is reachable from the
711
* incref.
712
*
713
* Return condition:
714
* depends on the value of raising_blocks:
715
* == NULL -> return true iff all paths from the incref have led to a
716
* decref.
717
* != NULL -> return true iff all paths from the incref have led to
718
* either a decref or a raising exit.
719
*/
720
bool findFanoutDecrefCandidates(CallInst *incref, BasicBlock *cur_node,
721
SmallBBSet &bad_blocks,
722
SmallBBSet *decref_blocks,
723
SmallBBSet *raising_blocks) {
724
// stack of basic blocks for the walked path(s)
725
SmallVector<BasicBlock *, FANOUT_RECURSE_DEPTH> path_stack;
726
bool found = false;
727
// Get the terminator of the basic block containing the incref, the
728
// search starts from here.
729
auto term = cur_node->getTerminator();
730
731
// RAII push cur_node onto the work stack
732
raiiStack<SmallVectorImpl<BasicBlock *>> raii_path_stack(path_stack,
733
cur_node);
734
735
// This is a pass-by-ref accumulator.
736
unsigned subgraph_size = 0;
737
738
// Walk the successors of the terminator.
739
for (unsigned i = 0; i < term->getNumSuccessors(); ++i) {
740
// Get the successor
741
BasicBlock *child = term->getSuccessor(i);
742
// Walk the successor looking for decrefs
743
found =
744
walkChildForDecref(incref, child, path_stack, subgraph_size,
745
bad_blocks, decref_blocks, raising_blocks);
746
// if not found, return false
747
if (!found)
748
return found; // found must be false
749
}
750
return found;
751
}
752
753
/**
754
* "Walk" a child node looking for blocks containing decrefs or raises that
755
* meet the conditions described in findFanoutDecrefCandidates.
756
*
757
* Parameters:
758
* - incref: The incref under consideration
759
* - cur_node: The current basic block being assessed
760
* - path_stack: A stack of basic blocks representing unsearched paths
761
* - bad_blocks: a set of blocks that are known to not satisfy the
762
* the fanout condition. Mutated by this function.
763
* - subgraph_size: accumulator to count the subgraph size (node count).
764
* - decref_blocks: a set that stores references to accepted blocks that
765
* contain decrefs associated with the incref.
766
* - raising_blocks: a set that stores references to accepted blocks that
767
* contain raises.
768
*
769
* Returns:
770
* - true if the conditions above hold, false otherwise.
771
*/
772
bool walkChildForDecref(CallInst *incref, BasicBlock *cur_node,
773
SmallVectorImpl<BasicBlock *> &path_stack,
774
unsigned &subgraph_size, SmallBBSet &bad_blocks,
775
SmallBBSet *decref_blocks,
776
SmallBBSet *raising_blocks) {
777
// If the current path stack exceeds the recursion depth, stop, return
778
// false.
779
if (path_stack.size() >= FANOUT_RECURSE_DEPTH)
780
return false;
781
782
// Reject subgraph that is bigger than the subgraph_limit
783
if (++subgraph_size > subgraph_limit) {
784
// mark head-node as always fail because that subgraph is too big
785
// to analyze.
786
bad_blocks.insert(incref->getParent());
787
return false;
788
}
789
790
// Check for the back-edge condition...
791
// If the current block is in the path stack
792
if (basicBlockInList(cur_node, path_stack)) {
793
// If the current node is TOS
794
if (cur_node == path_stack[0]) {
795
// Reject interior node back-edge to start of sub-graph.
796
// This means that the incref can be executed multiple times
797
// before reaching the decref.
798
799
// mark head-node as always fail.
800
bad_blocks.insert(incref->getParent());
801
return false;
802
}
803
// it is a legal backedge; skip
804
return true;
805
}
806
807
// Does the current block have a related decref?
808
if (hasDecrefInNode(incref, cur_node)) {
809
// Add to the list of decref_blocks
810
decref_blocks->insert(cur_node);
811
return true; // done for this path
812
}
813
814
// Are there any decrefs in the current node?
815
if (hasAnyDecrefInNode(cur_node)) {
816
// Because we don't know about aliasing
817
818
// mark head-node as always fail.
819
bad_blocks.insert(incref->getParent());
820
return false;
821
}
822
823
// If raising_blocks is non-NULL, see if the current node is a block
824
// which raises, if so add to the raising_blocks list, this path is now
825
// finished.
826
if (raising_blocks && isRaising(cur_node)) {
827
raising_blocks->insert(cur_node);
828
return true; // done for this path
829
}
830
831
// Continue searching by recursing into successors of the current
832
// block.
833
834
// First RAII push cur_node as TOS
835
raiiStack<SmallVectorImpl<BasicBlock *>> raii_push_pop(path_stack,
836
cur_node);
837
bool found = false;
838
// get cur_node terminator
839
auto term = cur_node->getTerminator();
840
// walk successors of the current node
841
for (unsigned i = 0; i < term->getNumSuccessors(); ++i) {
842
// get a successor
843
BasicBlock *child = term->getSuccessor(i);
844
// recurse
845
found =
846
walkChildForDecref(incref, child, path_stack, subgraph_size,
847
bad_blocks, decref_blocks, raising_blocks);
848
if (!found)
849
return false;
850
}
851
// If this is a leaf node, returns false.
852
return found;
853
}
854
855
/**
856
* Backward pass.
857
* Check the tail-node condition for the fanout subgraph:
858
* The reverse walks from all exit-nodes must end with the head-node
859
* and the tail-nodes cannot be executed multiple times.
860
*
861
* Parameters:
862
* - incref: the incref instruction
863
* - head_node: the basic block containing the arg incref
864
* - tail_nodes: a set containing the basic block(s) in which decrefs
865
* corresponding to the arg incref instruction exist.
866
*
867
* Returns:
868
* - true if it could be verified that there's no loop structure
869
* surrounding the use of the decrefs, false else.
870
*
871
*/
872
bool verifyFanoutBackward(CallInst *incref, BasicBlock *head_node,
873
const SmallBBSet *tail_nodes,
874
bool prune_raise_exit) {
875
// push the tail nodes into a work list
876
SmallVector<BasicBlock *, 10> todo;
877
for (BasicBlock *bb : *tail_nodes) {
878
todo.push_back(bb);
879
}
880
881
// visited is for bookkeeping to hold reference to those nodes which
882
// have already been visited.
883
SmallBBSet visited;
884
// while there is work...
885
while (todo.size() > 0) {
886
SmallVector<BasicBlock *, FANOUT_RECURSE_DEPTH> workstack;
887
// pop an element from the work list into the work stack
888
workstack.push_back(todo.pop_back_val());
889
890
// while there's work on the local workstack
891
while (workstack.size() > 0) {
892
// Get a basic block
893
BasicBlock *cur_node = workstack.pop_back_val();
894
// If cur_node is a raising block, then skip it
895
if (prune_raise_exit && isRaising(cur_node)) {
896
continue;
897
}
898
// if the block has been seen before then skip
899
if (visited.count(cur_node)) {
900
// Already visited
901
continue; // skip
902
}
903
904
if (cur_node == &head_node->getParent()->getEntryBlock()) {
905
// Arrived at the entry node of the function.
906
// This means the reverse walk from a tail-node can
907
// bypass the head-node (incref node) of this fanout
908
// subgraph.
909
return false;
910
}
911
912
// remember that we have visited this node already
913
visited.insert(cur_node);
914
915
// Walk into all predecessors
916
// pred_begin and pred_end are defined under Functions in:
917
// http://llvm.org/doxygen/IR_2CFG_8h.html
918
auto it = pred_begin(cur_node), end = pred_end(cur_node);
919
for (; it != end; ++it) {
920
auto pred = *it;
921
if (tail_nodes->count(pred)) {
922
// reject because a predecessor is a block containing
923
// a decref matching the incref
924
return false;
925
}
926
if (pred != head_node) {
927
// If the predecessor is the head-node,
928
// this path is ok; otherwise, continue to walk up.
929
workstack.push_back(pred);
930
}
931
}
932
}
933
}
934
// analysis didn't exit, all conditions must be ok, return true
935
return true;
936
}
937
938
/**
939
* Check if a basic block is a block which raises, based on writing to the
940
* `%excinfo`.
941
*
942
* Parameters:
943
* - bb a basic block
944
*
945
* Returns:
946
* - true if basic block bb contains a raise with the appropriate metadata,
947
* false otherwise
948
*/
949
bool isRaising(const BasicBlock *bb) {
950
for (auto &instruction : *bb) {
951
if (instruction.getOpcode() == Instruction::Store) {
952
auto name = dyn_cast<StoreInst>(&instruction)
953
->getPointerOperand()
954
->getName();
955
if (name.compare("excinfo") == 0 &&
956
instruction.hasMetadata("numba_exception_output")) {
957
return true;
958
}
959
}
960
}
961
return false;
962
}
963
964
/**
965
* Does "Is a basic block in a given list"
966
*
967
* Template parameter <T>:
968
* - the type of list
969
*
970
* Parameters:
971
* - bb a basic block
972
* - list a list-like container in which to search
973
*
974
* Returns:
975
* - true if bb is in list, false else.
976
*/
977
template <class T>
978
bool basicBlockInList(const BasicBlock *bb, const T &list) {
979
for (BasicBlock *each : list) {
980
if (bb == each)
981
return true;
982
}
983
return false;
984
}
985
986
/**
987
* Check to see if a basic block contains a decref related to a given incref
988
*
989
* Parameters:
990
* - incref an incref
991
* - bb a basic block
992
*
993
* Returns:
994
* - true if basic block bb contains a decref related to incref, false
995
* otherwise.
996
*/
997
bool hasDecrefInNode(CallInst *incref, BasicBlock *bb) {
998
for (Instruction &ii : *bb) {
999
if (isRelatedDecref(incref, &ii) != NULL) {
1000
return true;
1001
}
1002
}
1003
return false;
1004
}
1005
1006
/**
1007
* Checks if an instruction is a decref that is related to the given incref.
1008
*
1009
* Parameters:
1010
* - incref an incref
1011
* - bb a basic block
1012
*
1013
* Returns:
1014
* - returns input ii as a CallInst* if it is a decref related to incref,
1015
* NULL otherwise.
1016
*/
1017
CallInst *isRelatedDecref(CallInst *incref, Instruction *ii) {
1018
CallInst *suspect;
1019
if ((suspect = GetRefOpCall(ii))) {
1020
if (!IsDecRef(suspect)) {
1021
return NULL;
1022
}
1023
if (incref->getArgOperand(0) != suspect->getArgOperand(0)) {
1024
return NULL;
1025
}
1026
return suspect;
1027
}
1028
return NULL;
1029
}
1030
1031
/**
1032
* Checks if the first argument to the supplied call_inst is NULL and
1033
* returns true if so, false otherwise.
1034
*
1035
* Parameters:
1036
* - call_inst, a call instruction to check.
1037
*
1038
* Returns:
1039
* - true is the first argument to call_inst is not NULL, false otherwise
1040
*/
1041
bool isNonNullFirstArg(CallInst *call_inst) {
1042
auto val = call_inst->getArgOperand(0);
1043
auto ptr = dyn_cast<ConstantPointerNull>(val);
1044
return ptr == NULL;
1045
}
1046
1047
/**
1048
* Scans a basic block for decrefs and returns true if one is found,
1049
* false otherwise
1050
*
1051
* Parameters:
1052
* - bb, a basic block
1053
*
1054
* Returns:
1055
* - true if there is a decref in the basic block, false otherwise.
1056
*/
1057
bool hasAnyDecrefInNode(BasicBlock *bb) {
1058
for (Instruction &ii : *bb) {
1059
CallInst *refop = GetRefOpCall(&ii);
1060
if (refop != NULL && IsDecRef(refop)) {
1061
return true;
1062
}
1063
}
1064
return false;
1065
}
1066
1067
/**
1068
* Determines if there is a decref between two nodes in a graph.
1069
*
1070
* NOTE: Required condition: head_node dominates tail_node
1071
*
1072
* Parameters:
1073
* - head_node, a basic block which is the head of the graph
1074
* - tail_node, a basic block which is the tail of the graph
1075
*
1076
* Returns:
1077
* - true if there is a decref, false else
1078
*
1079
*/
1080
bool hasDecrefBetweenGraph(BasicBlock *head_node, BasicBlock *tail_node) {
1081
// This function implements a depth-first search.
1082
1083
// visited keeps track of the visited blocks
1084
SmallBBSet visited;
1085
// stack keeps track of blocks to be checked.
1086
SmallVector<BasicBlock *, 20> stack;
1087
// start with the head_node;
1088
stack.push_back(head_node);
1089
do {
1090
BasicBlock *cur_node = stack.pop_back_val();
1091
// First, is the current BB already visited, if so return false,
1092
// its already been checked.
1093
if (visited.count(cur_node)) {
1094
continue; // skip
1095
}
1096
// remember that it is visited
1097
visited.insert(cur_node);
1098
if (DEBUG_PRINT) {
1099
errs() << "Check..." << cur_node->getName() << "\n";
1100
}
1101
1102
// scan the current BB for decrefs, if any are present return true
1103
if (hasAnyDecrefInNode(cur_node))
1104
return true;
1105
1106
// get the terminator of the current node
1107
Instruction *term = cur_node->getTerminator();
1108
// walk the successor blocks
1109
for (unsigned i = 0; i < term->getNumSuccessors(); ++i) {
1110
BasicBlock *child = term->getSuccessor(i);
1111
// if the successor is the tail node, skip
1112
if (child == tail_node)
1113
continue;
1114
// else check the subgraph between the current successor and the
1115
// tail
1116
stack.push_back(child);
1117
}
1118
} while (stack.size() > 0);
1119
return false;
1120
}
1121
1122
typedef bool (*test_refops_function)(CallInst *);
1123
1124
/**
1125
* Walks the basic blocks of a function F, scans each instruction, if the
1126
* instruction is a refop and calling `test_refops_function` on it evaluates
1127
* to true then add it to list.
1128
*
1129
* Templates Parameter <T>
1130
* - the type of list
1131
*
1132
* Parameters:
1133
* - F a LLVM function
1134
* - test_refops_function a function that takes an Instruction instance and
1135
* returns true if the instance is a refop, false otherwise
1136
* - list, a list-like container to hold reference to instruction instances
1137
* which are identified by test_refops_function.
1138
*/
1139
template <class T>
1140
void listRefOps(Function &F, test_refops_function fn, T &list) {
1141
// For each basic block in the function
1142
for (BasicBlock &bb : F) {
1143
// For each instruction the basic block
1144
for (Instruction &ii : bb) {
1145
CallInst *ci;
1146
// if the instruction is a refop
1147
if ((ci = GetRefOpCall(&ii))) {
1148
// and the test_refops_function returns true when called
1149
// on the instruction
1150
if (fn(ci)) {
1151
// add to the list
1152
list.push_back(ci);
1153
}
1154
}
1155
}
1156
}
1157
}
1158
}; // end of struct RefPrune
1159
1160
} // namespace
1161
1162
class RefPrunePass : public PassInfoMixin<RefPrunePass> {
1163
1164
public:
1165
Subpasses flags;
1166
size_t subgraph_limit;
1167
RefPrunePass(Subpasses flags = Subpasses::All, size_t subgraph_limit = -1)
1168
: flags(flags), subgraph_limit(subgraph_limit) {}
1169
1170
PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM) {
1171
auto &DT = AM.getResult<DominatorTreeAnalysis>(F);
1172
auto &PDT = AM.getResult<PostDominatorTreeAnalysis>(F);
1173
if (RefPrune(DT, PDT, flags, subgraph_limit).runOnFunction(F)) {
1174
return PreservedAnalyses::none();
1175
}
1176
1177
return PreservedAnalyses::all();
1178
}
1179
};
1180
1181
class RefNormalizePass : public PassInfoMixin<RefNormalizePass> {
1182
1183
public:
1184
RefNormalizePass() = default;
1185
1186
PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM) {
1187
if (RefNormalize().runOnFunction(F)) {
1188
return PreservedAnalyses::none();
1189
}
1190
1191
return PreservedAnalyses::all();
1192
}
1193
};
1194
1195
size_t RefPrune::stats_per_bb = 0;
1196
size_t RefPrune::stats_diamond = 0;
1197
size_t RefPrune::stats_fanout = 0;
1198
size_t RefPrune::stats_fanout_raise = 0;
1199
1200
extern "C" {
1201
1202
API_EXPORT(void)
1203
LLVMPY_AddRefPrunePass_module(LLVMModulePassManagerRef MPM, int subpasses,
1204
size_t subgraph_limit) {
1205
llvm::unwrap(MPM)->addPass(
1206
createModuleToFunctionPassAdaptor(RefNormalizePass()));
1207
llvm::unwrap(MPM)->addPass(createModuleToFunctionPassAdaptor(
1208
RefPrunePass((Subpasses)subpasses, subgraph_limit)));
1209
}
1210
1211
API_EXPORT(void)
1212
LLVMPY_AddRefPrunePass_function(LLVMFunctionPassManagerRef FPM, int subpasses,
1213
size_t subgraph_limit) {
1214
llvm::unwrap(FPM)->addPass(RefNormalizePass());
1215
llvm::unwrap(FPM)->addPass(
1216
RefPrunePass((Subpasses)subpasses, subgraph_limit));
1217
}
1218
1219
/**
1220
* Struct for holding statistics about the amount of pruning performed by
1221
* each type of pruning algorithm.
1222
*/
1223
typedef struct PruneStats {
1224
size_t basicblock;
1225
size_t diamond;
1226
size_t fanout;
1227
size_t fanout_raise;
1228
} PRUNESTATS;
1229
1230
API_EXPORT(void)
1231
LLVMPY_DumpRefPruneStats(PRUNESTATS *buf, bool do_print) {
1232
/* PRUNESTATS is updated with the statistics about what has been pruned from
1233
* the RefPrune static state vars. This isn't threadsafe but neither is
1234
* the LLVM pass infrastructure so it's all done under a python thread lock.
1235
*
1236
* do_print if set will print the stats to stderr.
1237
*/
1238
if (do_print) {
1239
errs() << "refprune stats "
1240
<< "per-BB " << RefPrune::stats_per_bb << " "
1241
<< "diamond " << RefPrune::stats_diamond << " "
1242
<< "fanout " << RefPrune::stats_fanout << " "
1243
<< "fanout+raise " << RefPrune::stats_fanout_raise << " "
1244
<< "\n";
1245
};
1246
1247
buf->basicblock = RefPrune::stats_per_bb;
1248
buf->diamond = RefPrune::stats_diamond;
1249
buf->fanout = RefPrune::stats_fanout;
1250
buf->fanout_raise = RefPrune::stats_fanout_raise;
1251
}
1252
1253
} // extern "C"
1254
1255