1#include "core.h"23#include "llvm/IR/BasicBlock.h"4#include "llvm/IR/Function.h"5#include "llvm/IR/Instructions.h"6#include "llvm/Pass.h"78#include "llvm/ADT/SmallSet.h"910#include "llvm/Analysis/Passes.h"11#include "llvm/Analysis/PostDominators.h"12#include "llvm/Support/raw_ostream.h"1314#include "llvm/InitializePasses.h"15#include "llvm/LinkAllPasses.h"1617#include <iostream>18#include <vector>1920// #define DEBUG_PRINT 121#define DEBUG_PRINT 02223using namespace llvm;2425namespace llvm {26struct OpaqueModulePassManager;27typedef OpaqueModulePassManager *LLVMModulePassManagerRef;28DEFINE_SIMPLE_CONVERSION_FUNCTIONS(ModulePassManager, LLVMModulePassManagerRef)2930struct OpaqueFunctionPassManager;31typedef OpaqueFunctionPassManager *LLVMFunctionPassManagerRef;32DEFINE_SIMPLE_CONVERSION_FUNCTIONS(FunctionPassManager,33LLVMFunctionPassManagerRef)34} // namespace llvm3536namespace {37/**38* Checks if a call instruction is an incref39*40* Parameters:41* - call_inst, a call instruction42*43* Returns:44* - true if call_inst is an incref, false otherwise45*/46bool IsIncRef(CallInst *call_inst) {47Value *callee = call_inst->getCalledOperand();48return callee->getName() == "NRT_incref";49}5051/**52* Checks if a call instruction is an decref53*54* Parameters:55* - call_inst, a call instruction56*57* Returns:58* - true if call_inst is an decref, false otherwise59*/60bool IsDecRef(CallInst *call_inst) {61Value *callee = call_inst->getCalledOperand();62return callee->getName() == "NRT_decref";63}6465/**66* Checks if an instruction is a "refop" (either an incref or a decref).67*68* Parameters:69* - ii, the instruction to check70*71* Returns:72* - the instruction ii, if it is a "refop", NULL otherwise73*/74CallInst *GetRefOpCall(Instruction *ii) {75if (ii->getOpcode() == Instruction::Call) {76CallInst *call_inst = dyn_cast<CallInst>(ii);77if (IsIncRef(call_inst) || IsDecRef(call_inst)) {78return call_inst;79}80}81return NULL;82}8384/**85* RAII push-pop of elements onto a stack.86*87* Template parameter <Tstack>:88* - the type of the stack89*/90template <class Tstack> struct raiiStack {91Tstack &stack;9293typedef typename Tstack::value_type value_type;9495/**96* ctor pushes `elem` onto `stack`97*/98raiiStack(Tstack &stack, value_type &elem) : stack(stack) {99stack.push_back(elem);100}101/**102* dtor pops back of `stack`103*/104~raiiStack() { stack.pop_back(); }105};106107/**108* A FunctionPass to reorder incref/decref instructions such that decrefs occur109* logically after increfs. This is a pre-requisite pass to the pruner passes.110*/111struct RefNormalize {112113bool runOnFunction(Function &F) {114bool mutated = false;115// For each basic block in F116for (BasicBlock &bb : F) {117// This find a incref in the basic block118119bool has_incref = false;120// check the instructions in the basic block121for (Instruction &ii : bb) {122// see if it is a refop123CallInst *refop = GetRefOpCall(&ii);124// if it is a refop and it is an incref, set flag and break125if (refop != NULL && IsIncRef(refop)) {126has_incref = true;127break;128}129}130131// block has an incref132if (has_incref) {133// This moves decrefs to the back just before the terminator.134135SmallVector<CallInst *, 10> to_be_moved;136// walk the instructions in the block137for (Instruction &ii : bb) {138// query the instruction, if its a refop store to refop139// if not store NULL to refop140CallInst *refop = GetRefOpCall(&ii);141// if the refop is not NULL and it is also a decref then142// shove it into the to_be_moved vector143if (refop != NULL && IsDecRef(refop)) {144to_be_moved.push_back(refop);145}146}147// Walk the to_be_moved vector of instructions, these are all148// decrefs by construction.149for (CallInst *decref : to_be_moved) {150// move the decref to a location prior to the block151// terminator and set mutated.152auto term = bb.getTerminator();153decref->moveBefore(term->getIterator());154mutated |= true;155}156}157}158return mutated;159}160};161162typedef enum {163None = 0b0000,164PerBasicBlock = 0b0001,165Diamond = 0b0010,166Fanout = 0b0100,167FanoutRaise = 0b1000,168All = PerBasicBlock | Diamond | Fanout | FanoutRaise169} Subpasses;170171struct RefPrune {172static char ID;173static size_t stats_per_bb;174static size_t stats_diamond;175static size_t stats_fanout;176static size_t stats_fanout_raise;177178// Fixed size for how deep to recurse in the fanout case prior to giving up.179static const size_t FANOUT_RECURSE_DEPTH = 15;180typedef SmallSet<BasicBlock *, FANOUT_RECURSE_DEPTH> SmallBBSet;181182DominatorTree &DT;183PostDominatorTree &PDT;184/**185* Enum for setting which subpasses to run, there is no interdependence.186*/187Subpasses flags;188189// The maximum number of nodes that the fanout pruners will look at.190size_t subgraph_limit;191192RefPrune(DominatorTree &DT, PostDominatorTree &PDT,193Subpasses flags = Subpasses::All, size_t subgraph_limit = -1)194: DT(DT), PDT(PDT), flags(flags), subgraph_limit(subgraph_limit) {}195196bool isSubpassEnabledFor(Subpasses expected) {197return (flags & expected) == expected;198}199200bool runOnFunction(Function &F) {201// state for LLVM function pass mutated IR202bool mutated = false;203204// local state for capturing mutation by any selected pass, any mutation205// at all propagates into mutated for return.206bool local_mutated;207do {208local_mutated = false;209if (isSubpassEnabledFor(Subpasses::PerBasicBlock))210local_mutated |= runPerBasicBlockPrune(F);211if (isSubpassEnabledFor(Subpasses::Diamond))212local_mutated |= runDiamondPrune(F);213if (isSubpassEnabledFor(Subpasses::Fanout))214local_mutated |= runFanoutPrune(F, /*prune_raise*/ false);215if (isSubpassEnabledFor(Subpasses::FanoutRaise))216local_mutated |= runFanoutPrune(F, /*prune_raise*/ true);217mutated |= local_mutated;218} while (local_mutated);219220return mutated;221}222223/**224* Per BasicBlock pruning pass.225*226* Assumes all increfs are before all decrefs.227* Cleans up all refcount operations on NULL pointers.228* Cleans up all redundant incref/decref pairs.229*230* This pass works on a block at a time and does not change the CFG.231* Incref/Decref removal is restricted to the basic block.232*233* General idea is to be able to prune within a block as follows:234*235* ┌─────────────┐236* │ Block entry │237* | Instruction │238* | Incref(A) │ ──> No match, cannot remove.239* | Incref(B) │ ──┐240* | Instruction │ │ Matching pair, can be removed.241* | Instruction │ │242* | Decref(B) │ <─┘243* | Instruction │244* | Decref(NULL)│ ──> Decref on NULL, can be removed.245* | Terminator │246* └─────────────┘247* Parameters:248* - F a Function249*250* Returns:251* - true if pruning took place, false otherwise252*253*/254bool runPerBasicBlockPrune(Function &F) {255bool mutated = false;256257// walk the basic blocks in Function F.258for (BasicBlock &bb : F) {259// allocate some buffers260SmallVector<CallInst *, 10> incref_list, decref_list, null_list;261262// This is a scanning phase looking to classify instructions into263// inrefs, decrefs and operations on already NULL pointers.264// walk the instructions in the current basic block265for (Instruction &ii : bb) {266// If the instruction is a refop267CallInst *ci;268if ((ci = GetRefOpCall(&ii))) {269if (!isNonNullFirstArg(ci)) {270// Drop refops on NULL pointers271null_list.push_back(ci);272} else if (IsIncRef(ci)) {273incref_list.push_back(ci);274} else if (IsDecRef(ci)) {275decref_list.push_back(ci);276}277}278}279280// First: Remove refops on NULL281for (CallInst *ci : null_list) {282ci->eraseFromParent();283mutated = true;284285// Do we care about differentiating between prunes of NULL286// and prunes of pairs?287stats_per_bb += 1;288}289290// Second: Find matching pairs of incref decref291while (incref_list.size() > 0) {292// get an incref293CallInst *incref = incref_list.pop_back_val();294// walk decrefs295for (size_t i = 0; i < decref_list.size(); ++i) {296CallInst *decref = decref_list[i];297// is this instruction a decref thats non-NULL and298// the decref related to the incref?299if (decref && isRelatedDecref(incref, decref)) {300if (DEBUG_PRINT) {301errs() << "Prune: matching pair in BB:\n";302incref->dump();303decref->dump();304incref->getParent()->dump();305}306// strip incref and decref from blck307incref->eraseFromParent();308decref->eraseFromParent();309310// set stripped decref to null311decref_list[i] = NULL;312// set mutated bit and update prune stats313mutated = true;314stats_per_bb += 2;315break;316}317}318}319}320return mutated;321}322323/**324* "Diamond" pruning pass.325*326* Looks to prune across basic blocks in cases where incref/decref pairs327* appear in a "diamond" shape CFG structure. For example:328*329* ┌────────────┐330* │ incref (A) │331* └────────────┘332* / \333* / \334* / \335* . .336* . .337* ┌────────────┐ ┌────────────┐338* │ MORE CFG │ │ MORE CFG │339* └────────────┘ └────────────┘340* . .341* . .342* \ /343* \ /344* \ /345* \ /346* ┌────────────┐347* │ decref (A) │348* └────────────┘349*350* Condition for prune is that, in an incref/decref pair:351* - the incref dominates the decref.352* - the decref postdominates the incref.353* - in the blocks in the CFG between the basic blocks containing the354* incref/decref pair there's no other decref present (this is355* conservative and is to handle aliasing of references).356* - that the decref is not in a cycle dominated by the incref (i.e. decref357* in a loop).358*359* Parameters:360* - F a Function361*362* Returns:363* - true if pruning took place, false otherwise364*365*/366bool runDiamondPrune(Function &F) {367bool mutated = false;368369// Find all increfs and decrefs in the Function and store them in370// incref_list and decref_list respectively.371std::vector<CallInst *> incref_list, decref_list;372listRefOps(F, IsIncRef, incref_list);373listRefOps(F, IsDecRef, decref_list);374375// Walk the incref list376for (CallInst *&incref : incref_list) {377// NULL is the token for already erased, skip on it378if (incref == NULL)379continue;380381// Walk the decref_list382for (CallInst *&decref : decref_list) {383// NULL is the token for already erased, skip on it384if (decref == NULL)385continue;386387// Diamond prune is for refops not in the same BB388if (incref->getParent() == decref->getParent())389continue;390391// If the refops are unrelated, skip392if (!isRelatedDecref(incref, decref))393continue;394395// incref DOM decref && decref POSTDOM incref396if (DT.dominates(incref, decref) &&397PDT.dominates(decref, incref)) {398// check that the decref cannot be executed multiple times399SmallBBSet tail_nodes;400tail_nodes.insert(decref->getParent());401if (!verifyFanoutBackward(incref, incref->getParent(),402&tail_nodes, false))403404continue;405406// scan the CFG between the incref and decref BBs, if407// there's a decref present then skip, this is conservative.408if (hasDecrefBetweenGraph(incref->getParent(),409decref->getParent())) {410continue;411} else {412413if (DEBUG_PRINT) {414errs() << F.getName() << "-------------\n";415errs() << incref->getParent()->getName() << "\n";416incref->dump();417errs() << decref->getParent()->getName() << "\n";418decref->dump();419}420421// erase instruction from block and set NULL marker for422// bookkeeping purposes423incref->eraseFromParent();424decref->eraseFromParent();425incref = NULL;426decref = NULL;427428stats_diamond += 2;429}430// mark mutated431mutated = true;432break;433}434}435}436return mutated;437}438439/**440* "Fan-out" pruning passes.441*442* Prunes "fan-out"s, this is where control flow from a block containing an443* incref "fans out" into blocks that contain corresponding decrefs.444*445* There are two supported fan-out shape CFG structures.446*447* Supported case 1, simple "fan-out" with no raise, prune occurs when the448* incref dominates the predecessor blocks containing associated decrefs.449*450* ┌────────────┐451* │ incref (A) │452* └────────────┘453* / \454* / \455* / \456* . .457* . .458* ┌────────────┐ ┌────────────┐459* │ MORE CFG │ │ MORE CFG │460* └────────────┘ └────────────┘461* . .462* . .463* ┌────────────┐ ┌────────────┐464* │ decref (A) │ │ decref (A) │465* └────────────┘ └────────────┘466* . .467* . .468* . .469*470*471* Supported case 2, simple "fan-out" with raise, prune occurs when the472* incref dominates the predecessor blocks containing associated decrefs473* with the exception of the raise block (this is to "forgive" the474* occasional missing decref in a raise block).475*476* ┌────────────┐477* │ incref (A) │478* └────────────┘479* / \480* / \481* / \482* . .483* . .484* ┌────────────┐ ┌────────────┐485* │ MORE CFG │ │ MORE CFG │486* └────────────┘ └────────────┘487* . .488* . .489* ┌────────────┐ ┌────────────┐490* │ decref (A) │ │ raise │491* └────────────┘ └────────────┘492* .493* .494* ┌────────────┐495* │ MORE CFG │496* └────────────┘497*498* a complex pattern about fanout-raise499* https://github.com/numba/llvmlite/issues/1023500* ┌────────────┐501* │ incref │502* │ incref │503* └────────────┘504* / \505* / \506* ┌────────────┐ \507* │ decref | \508* └────────────┘ \509* / \ \510* / \ \511* ┌────────────┐ ┌────────────┐ \512* │ decref | │ incref | \513* └────────────┘ └────────────┘ \514* / \ \515* / \ \516* ┌────────────┐ ┌────────────┐517* │ decref | │ raise |518* │ decref | └────────────┘519* └────────────┘520*521* Parameters:522* - F a Function523* - prune_raise_exit, if false case 1 is considered, if true case 2 is524* considered.525*526* Returns:527* - true if pruning took place, false otherwise528*/529bool runFanoutPrune(Function &F, bool prune_raise_exit) {530bool mutated = false;531532// Find all Increfs and store them in incref_list533std::vector<CallInst *> incref_list;534listRefOps(F, IsIncRef, incref_list);535536// Remember incref-blocks that will always fail.537SmallBBSet bad_blocks;538// walk the incref_list539for (CallInst *incref : incref_list) {540// Skip blocks that will always fail.541if (bad_blocks.count(incref->getParent())) {542continue; // skip543}544545// Is there *any* decref in the parent node of the incref?546// If so skip this incref (considering that aliases may exist).547if (hasAnyDecrefInNode(incref->getParent())) {548// be careful of potential alias549continue; // skip550}551552SmallBBSet decref_blocks;553// Check for the chosen "fan out" condition554if (findFanout(incref, bad_blocks, &decref_blocks,555prune_raise_exit)) {556if (DEBUG_PRINT) {557F.viewCFG();558errs() << "------------\n";559errs() << "incref " << incref->getParent()->getName()560<< "\n";561errs() << " decref_blocks.size()" << decref_blocks.size()562<< "\n";563incref->dump();564}565// Remove first related decref in each block566// for each block567for (BasicBlock *each : decref_blocks) {568// for each instruction569for (Instruction &ii : *each) {570CallInst *decref;571// walrus:572// is the current instruction the decref associated with573// the incref under consideration, if so assign to574// decref and continue.575if ((decref = isRelatedDecref(incref, &ii))) {576if (DEBUG_PRINT) {577errs()578<< decref->getParent()->getName() << "\n";579decref->dump();580}581// Remove this decref from its block582decref->eraseFromParent();583584// update counters based on decref removal585if (prune_raise_exit)586stats_fanout_raise += 1;587else588stats_fanout += 1;589break;590}591}592}593// remove the incref from its block594incref->eraseFromParent();595596// update counters based on incref removal597if (prune_raise_exit)598stats_fanout_raise += 1;599else600stats_fanout += 1;601mutated = true;602}603}604return mutated;605}606607/**608* This searches for the "fan-out" condition and returns true if it is609* found.610*611* Parameters:612* - incref: the incref from which fan-out should be checked.613* - bad_blocks: a set of blocks that are known to not satisfy the614* the fanout condition. Mutated by this function.615* - decref_blocks: pointer to a set of basic blocks, this is mutated by616* this function, on return it contains the basic blocks containing617* decrefs related to the incref618* - prune_raise_exit: this is a bool to signal whether to just look for619* the fan-out case or also look for the fan-out with raise condition,620* if true the fan-out with raise condition is considered else it is621* not.622*623* Returns:624* - true if the fan-out condition specified by `prune_raise_exit` was625* found, false otherwise.626*/627bool findFanout(CallInst *incref, SmallBBSet &bad_blocks,628SmallBBSet *decref_blocks, bool prune_raise_exit) {629630// get the basic block of the incref instruction631BasicBlock *head_node = incref->getParent();632633// work space, a set of basic blocks to hold the block which contain634// raises, only used in the case of prune_raise_exit635SmallBBSet raising_blocks, *p_raising_blocks = NULL;636// Set up pointer to raising_blocks637if (prune_raise_exit)638p_raising_blocks = &raising_blocks;639640if (findFanoutDecrefCandidates(incref, head_node, bad_blocks,641decref_blocks, p_raising_blocks)) {642if (DEBUG_PRINT) {643errs() << "forward pass candids.size() = "644<< decref_blocks->size() << "\n";645errs() << " " << head_node->getName() << "\n";646incref->dump();647}648if (decref_blocks->size() == 0) {649// no decref blocks650if (DEBUG_PRINT) {651errs() << "missing decref blocks = "652<< raising_blocks.size() << "\n";653}654return false;655}656if (prune_raise_exit) {657if (raising_blocks.size() == 0) {658// no raising blocks659if (DEBUG_PRINT) {660errs() << "missing raising blocks = "661<< raising_blocks.size() << "\n";662for (auto bb : *decref_blocks) {663errs() << " " << bb->getName() << "\n";664}665}666return false;667}668669// combine decref_blocks into raising blocks for checking the670// exit node condition671for (BasicBlock *bb : *decref_blocks) {672raising_blocks.insert(bb);673}674if (verifyFanoutBackward(incref, head_node, p_raising_blocks,675prune_raise_exit))676return true;677678} else if (verifyFanoutBackward(incref, head_node, decref_blocks,679prune_raise_exit)) {680return true;681}682}683return false;684}685686/**687* Forward pass.688*689* Walk the successors of the incref node recursively until a decref690* or an exit node is found.691*692* In the case of a decref node, the node is added to decref_blocks only if693* it contains a decref associated with the incref and there is no interior694* back-edge to a predecessor in the current sub-graph.695*696* In the case of an exit, it must be a raise for it to be added to697* raising_blocks.698*699* Parameters:700* - incref: The incref under consideration.701* - cur_node: The basic block in which incref is found.702* - bad_blocks: a set of blocks that are known to not satisfy the703* the fanout condition. Mutated by this function.704* - decref_blocks: pointer to a set of basic blocks, it is mutated by this705* function and on successful return contains the basic blocks which have706* a decref related to the supplied incref in them.707* - raising_blocks: point to a set of basic blocks OR NULL. If not-NULL708* it is mutated by this function and on successful return contains the709* basic blocks which have a raise in them that is reachable from the710* incref.711*712* Return condition:713* depends on the value of raising_blocks:714* == NULL -> return true iff all paths from the incref have led to a715* decref.716* != NULL -> return true iff all paths from the incref have led to717* either a decref or a raising exit.718*/719bool findFanoutDecrefCandidates(CallInst *incref, BasicBlock *cur_node,720SmallBBSet &bad_blocks,721SmallBBSet *decref_blocks,722SmallBBSet *raising_blocks) {723// stack of basic blocks for the walked path(s)724SmallVector<BasicBlock *, FANOUT_RECURSE_DEPTH> path_stack;725bool found = false;726// Get the terminator of the basic block containing the incref, the727// search starts from here.728auto term = cur_node->getTerminator();729730// RAII push cur_node onto the work stack731raiiStack<SmallVectorImpl<BasicBlock *>> raii_path_stack(path_stack,732cur_node);733734// This is a pass-by-ref accumulator.735unsigned subgraph_size = 0;736737// Walk the successors of the terminator.738for (unsigned i = 0; i < term->getNumSuccessors(); ++i) {739// Get the successor740BasicBlock *child = term->getSuccessor(i);741// Walk the successor looking for decrefs742found =743walkChildForDecref(incref, child, path_stack, subgraph_size,744bad_blocks, decref_blocks, raising_blocks);745// if not found, return false746if (!found)747return found; // found must be false748}749return found;750}751752/**753* "Walk" a child node looking for blocks containing decrefs or raises that754* meet the conditions described in findFanoutDecrefCandidates.755*756* Parameters:757* - incref: The incref under consideration758* - cur_node: The current basic block being assessed759* - path_stack: A stack of basic blocks representing unsearched paths760* - bad_blocks: a set of blocks that are known to not satisfy the761* the fanout condition. Mutated by this function.762* - subgraph_size: accumulator to count the subgraph size (node count).763* - decref_blocks: a set that stores references to accepted blocks that764* contain decrefs associated with the incref.765* - raising_blocks: a set that stores references to accepted blocks that766* contain raises.767*768* Returns:769* - true if the conditions above hold, false otherwise.770*/771bool walkChildForDecref(CallInst *incref, BasicBlock *cur_node,772SmallVectorImpl<BasicBlock *> &path_stack,773unsigned &subgraph_size, SmallBBSet &bad_blocks,774SmallBBSet *decref_blocks,775SmallBBSet *raising_blocks) {776// If the current path stack exceeds the recursion depth, stop, return777// false.778if (path_stack.size() >= FANOUT_RECURSE_DEPTH)779return false;780781// Reject subgraph that is bigger than the subgraph_limit782if (++subgraph_size > subgraph_limit) {783// mark head-node as always fail because that subgraph is too big784// to analyze.785bad_blocks.insert(incref->getParent());786return false;787}788789// Check for the back-edge condition...790// If the current block is in the path stack791if (basicBlockInList(cur_node, path_stack)) {792// If the current node is TOS793if (cur_node == path_stack[0]) {794// Reject interior node back-edge to start of sub-graph.795// This means that the incref can be executed multiple times796// before reaching the decref.797798// mark head-node as always fail.799bad_blocks.insert(incref->getParent());800return false;801}802// it is a legal backedge; skip803return true;804}805806// Does the current block have a related decref?807if (hasDecrefInNode(incref, cur_node)) {808// Add to the list of decref_blocks809decref_blocks->insert(cur_node);810return true; // done for this path811}812813// Are there any decrefs in the current node?814if (hasAnyDecrefInNode(cur_node)) {815// Because we don't know about aliasing816817// mark head-node as always fail.818bad_blocks.insert(incref->getParent());819return false;820}821822// If raising_blocks is non-NULL, see if the current node is a block823// which raises, if so add to the raising_blocks list, this path is now824// finished.825if (raising_blocks && isRaising(cur_node)) {826raising_blocks->insert(cur_node);827return true; // done for this path828}829830// Continue searching by recursing into successors of the current831// block.832833// First RAII push cur_node as TOS834raiiStack<SmallVectorImpl<BasicBlock *>> raii_push_pop(path_stack,835cur_node);836bool found = false;837// get cur_node terminator838auto term = cur_node->getTerminator();839// walk successors of the current node840for (unsigned i = 0; i < term->getNumSuccessors(); ++i) {841// get a successor842BasicBlock *child = term->getSuccessor(i);843// recurse844found =845walkChildForDecref(incref, child, path_stack, subgraph_size,846bad_blocks, decref_blocks, raising_blocks);847if (!found)848return false;849}850// If this is a leaf node, returns false.851return found;852}853854/**855* Backward pass.856* Check the tail-node condition for the fanout subgraph:857* The reverse walks from all exit-nodes must end with the head-node858* and the tail-nodes cannot be executed multiple times.859*860* Parameters:861* - incref: the incref instruction862* - head_node: the basic block containing the arg incref863* - tail_nodes: a set containing the basic block(s) in which decrefs864* corresponding to the arg incref instruction exist.865*866* Returns:867* - true if it could be verified that there's no loop structure868* surrounding the use of the decrefs, false else.869*870*/871bool verifyFanoutBackward(CallInst *incref, BasicBlock *head_node,872const SmallBBSet *tail_nodes,873bool prune_raise_exit) {874// push the tail nodes into a work list875SmallVector<BasicBlock *, 10> todo;876for (BasicBlock *bb : *tail_nodes) {877todo.push_back(bb);878}879880// visited is for bookkeeping to hold reference to those nodes which881// have already been visited.882SmallBBSet visited;883// while there is work...884while (todo.size() > 0) {885SmallVector<BasicBlock *, FANOUT_RECURSE_DEPTH> workstack;886// pop an element from the work list into the work stack887workstack.push_back(todo.pop_back_val());888889// while there's work on the local workstack890while (workstack.size() > 0) {891// Get a basic block892BasicBlock *cur_node = workstack.pop_back_val();893// If cur_node is a raising block, then skip it894if (prune_raise_exit && isRaising(cur_node)) {895continue;896}897// if the block has been seen before then skip898if (visited.count(cur_node)) {899// Already visited900continue; // skip901}902903if (cur_node == &head_node->getParent()->getEntryBlock()) {904// Arrived at the entry node of the function.905// This means the reverse walk from a tail-node can906// bypass the head-node (incref node) of this fanout907// subgraph.908return false;909}910911// remember that we have visited this node already912visited.insert(cur_node);913914// Walk into all predecessors915// pred_begin and pred_end are defined under Functions in:916// http://llvm.org/doxygen/IR_2CFG_8h.html917auto it = pred_begin(cur_node), end = pred_end(cur_node);918for (; it != end; ++it) {919auto pred = *it;920if (tail_nodes->count(pred)) {921// reject because a predecessor is a block containing922// a decref matching the incref923return false;924}925if (pred != head_node) {926// If the predecessor is the head-node,927// this path is ok; otherwise, continue to walk up.928workstack.push_back(pred);929}930}931}932}933// analysis didn't exit, all conditions must be ok, return true934return true;935}936937/**938* Check if a basic block is a block which raises, based on writing to the939* `%excinfo`.940*941* Parameters:942* - bb a basic block943*944* Returns:945* - true if basic block bb contains a raise with the appropriate metadata,946* false otherwise947*/948bool isRaising(const BasicBlock *bb) {949for (auto &instruction : *bb) {950if (instruction.getOpcode() == Instruction::Store) {951auto name = dyn_cast<StoreInst>(&instruction)952->getPointerOperand()953->getName();954if (name.compare("excinfo") == 0 &&955instruction.hasMetadata("numba_exception_output")) {956return true;957}958}959}960return false;961}962963/**964* Does "Is a basic block in a given list"965*966* Template parameter <T>:967* - the type of list968*969* Parameters:970* - bb a basic block971* - list a list-like container in which to search972*973* Returns:974* - true if bb is in list, false else.975*/976template <class T>977bool basicBlockInList(const BasicBlock *bb, const T &list) {978for (BasicBlock *each : list) {979if (bb == each)980return true;981}982return false;983}984985/**986* Check to see if a basic block contains a decref related to a given incref987*988* Parameters:989* - incref an incref990* - bb a basic block991*992* Returns:993* - true if basic block bb contains a decref related to incref, false994* otherwise.995*/996bool hasDecrefInNode(CallInst *incref, BasicBlock *bb) {997for (Instruction &ii : *bb) {998if (isRelatedDecref(incref, &ii) != NULL) {999return true;1000}1001}1002return false;1003}10041005/**1006* Checks if an instruction is a decref that is related to the given incref.1007*1008* Parameters:1009* - incref an incref1010* - bb a basic block1011*1012* Returns:1013* - returns input ii as a CallInst* if it is a decref related to incref,1014* NULL otherwise.1015*/1016CallInst *isRelatedDecref(CallInst *incref, Instruction *ii) {1017CallInst *suspect;1018if ((suspect = GetRefOpCall(ii))) {1019if (!IsDecRef(suspect)) {1020return NULL;1021}1022if (incref->getArgOperand(0) != suspect->getArgOperand(0)) {1023return NULL;1024}1025return suspect;1026}1027return NULL;1028}10291030/**1031* Checks if the first argument to the supplied call_inst is NULL and1032* returns true if so, false otherwise.1033*1034* Parameters:1035* - call_inst, a call instruction to check.1036*1037* Returns:1038* - true is the first argument to call_inst is not NULL, false otherwise1039*/1040bool isNonNullFirstArg(CallInst *call_inst) {1041auto val = call_inst->getArgOperand(0);1042auto ptr = dyn_cast<ConstantPointerNull>(val);1043return ptr == NULL;1044}10451046/**1047* Scans a basic block for decrefs and returns true if one is found,1048* false otherwise1049*1050* Parameters:1051* - bb, a basic block1052*1053* Returns:1054* - true if there is a decref in the basic block, false otherwise.1055*/1056bool hasAnyDecrefInNode(BasicBlock *bb) {1057for (Instruction &ii : *bb) {1058CallInst *refop = GetRefOpCall(&ii);1059if (refop != NULL && IsDecRef(refop)) {1060return true;1061}1062}1063return false;1064}10651066/**1067* Determines if there is a decref between two nodes in a graph.1068*1069* NOTE: Required condition: head_node dominates tail_node1070*1071* Parameters:1072* - head_node, a basic block which is the head of the graph1073* - tail_node, a basic block which is the tail of the graph1074*1075* Returns:1076* - true if there is a decref, false else1077*1078*/1079bool hasDecrefBetweenGraph(BasicBlock *head_node, BasicBlock *tail_node) {1080// This function implements a depth-first search.10811082// visited keeps track of the visited blocks1083SmallBBSet visited;1084// stack keeps track of blocks to be checked.1085SmallVector<BasicBlock *, 20> stack;1086// start with the head_node;1087stack.push_back(head_node);1088do {1089BasicBlock *cur_node = stack.pop_back_val();1090// First, is the current BB already visited, if so return false,1091// its already been checked.1092if (visited.count(cur_node)) {1093continue; // skip1094}1095// remember that it is visited1096visited.insert(cur_node);1097if (DEBUG_PRINT) {1098errs() << "Check..." << cur_node->getName() << "\n";1099}11001101// scan the current BB for decrefs, if any are present return true1102if (hasAnyDecrefInNode(cur_node))1103return true;11041105// get the terminator of the current node1106Instruction *term = cur_node->getTerminator();1107// walk the successor blocks1108for (unsigned i = 0; i < term->getNumSuccessors(); ++i) {1109BasicBlock *child = term->getSuccessor(i);1110// if the successor is the tail node, skip1111if (child == tail_node)1112continue;1113// else check the subgraph between the current successor and the1114// tail1115stack.push_back(child);1116}1117} while (stack.size() > 0);1118return false;1119}11201121typedef bool (*test_refops_function)(CallInst *);11221123/**1124* Walks the basic blocks of a function F, scans each instruction, if the1125* instruction is a refop and calling `test_refops_function` on it evaluates1126* to true then add it to list.1127*1128* Templates Parameter <T>1129* - the type of list1130*1131* Parameters:1132* - F a LLVM function1133* - test_refops_function a function that takes an Instruction instance and1134* returns true if the instance is a refop, false otherwise1135* - list, a list-like container to hold reference to instruction instances1136* which are identified by test_refops_function.1137*/1138template <class T>1139void listRefOps(Function &F, test_refops_function fn, T &list) {1140// For each basic block in the function1141for (BasicBlock &bb : F) {1142// For each instruction the basic block1143for (Instruction &ii : bb) {1144CallInst *ci;1145// if the instruction is a refop1146if ((ci = GetRefOpCall(&ii))) {1147// and the test_refops_function returns true when called1148// on the instruction1149if (fn(ci)) {1150// add to the list1151list.push_back(ci);1152}1153}1154}1155}1156}1157}; // end of struct RefPrune11581159} // namespace11601161class RefPrunePass : public PassInfoMixin<RefPrunePass> {11621163public:1164Subpasses flags;1165size_t subgraph_limit;1166RefPrunePass(Subpasses flags = Subpasses::All, size_t subgraph_limit = -1)1167: flags(flags), subgraph_limit(subgraph_limit) {}11681169PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM) {1170auto &DT = AM.getResult<DominatorTreeAnalysis>(F);1171auto &PDT = AM.getResult<PostDominatorTreeAnalysis>(F);1172if (RefPrune(DT, PDT, flags, subgraph_limit).runOnFunction(F)) {1173return PreservedAnalyses::none();1174}11751176return PreservedAnalyses::all();1177}1178};11791180class RefNormalizePass : public PassInfoMixin<RefNormalizePass> {11811182public:1183RefNormalizePass() = default;11841185PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM) {1186if (RefNormalize().runOnFunction(F)) {1187return PreservedAnalyses::none();1188}11891190return PreservedAnalyses::all();1191}1192};11931194size_t RefPrune::stats_per_bb = 0;1195size_t RefPrune::stats_diamond = 0;1196size_t RefPrune::stats_fanout = 0;1197size_t RefPrune::stats_fanout_raise = 0;11981199extern "C" {12001201API_EXPORT(void)1202LLVMPY_AddRefPrunePass_module(LLVMModulePassManagerRef MPM, int subpasses,1203size_t subgraph_limit) {1204llvm::unwrap(MPM)->addPass(1205createModuleToFunctionPassAdaptor(RefNormalizePass()));1206llvm::unwrap(MPM)->addPass(createModuleToFunctionPassAdaptor(1207RefPrunePass((Subpasses)subpasses, subgraph_limit)));1208}12091210API_EXPORT(void)1211LLVMPY_AddRefPrunePass_function(LLVMFunctionPassManagerRef FPM, int subpasses,1212size_t subgraph_limit) {1213llvm::unwrap(FPM)->addPass(RefNormalizePass());1214llvm::unwrap(FPM)->addPass(1215RefPrunePass((Subpasses)subpasses, subgraph_limit));1216}12171218/**1219* Struct for holding statistics about the amount of pruning performed by1220* each type of pruning algorithm.1221*/1222typedef struct PruneStats {1223size_t basicblock;1224size_t diamond;1225size_t fanout;1226size_t fanout_raise;1227} PRUNESTATS;12281229API_EXPORT(void)1230LLVMPY_DumpRefPruneStats(PRUNESTATS *buf, bool do_print) {1231/* PRUNESTATS is updated with the statistics about what has been pruned from1232* the RefPrune static state vars. This isn't threadsafe but neither is1233* the LLVM pass infrastructure so it's all done under a python thread lock.1234*1235* do_print if set will print the stats to stderr.1236*/1237if (do_print) {1238errs() << "refprune stats "1239<< "per-BB " << RefPrune::stats_per_bb << " "1240<< "diamond " << RefPrune::stats_diamond << " "1241<< "fanout " << RefPrune::stats_fanout << " "1242<< "fanout+raise " << RefPrune::stats_fanout_raise << " "1243<< "\n";1244};12451246buf->basicblock = RefPrune::stats_per_bb;1247buf->diamond = RefPrune::stats_diamond;1248buf->fanout = RefPrune::stats_fanout;1249buf->fanout_raise = RefPrune::stats_fanout_raise;1250}12511252} // extern "C"125312541255