// ----------------------------------------------------------------------------- Graph *SpanningTree::create_spanning_tree(Graph* g, Node* root) { if(root == NULL) throw std::runtime_error("create_spanning_tree NULL exception"); Graph *t = new Graph(FLAG_DAG); NodeSet visited; NodeStack node_stack; node_stack.push(root); while(!node_stack.empty()) { Node* n = node_stack.top(); node_stack.pop(); visited.insert(n); Node* tree_node1 = t->add_node_ptr(n->_value); EdgePtrIterator* eit = n->get_edges(); Edge* e; while((e = eit->next()) != NULL) { Node* inner_node = e->traverse(n); if(inner_node != NULL && visited.count(inner_node) == 0) { Node* tree_node2 = t->add_node_ptr(inner_node->_value); t->add_edge(tree_node1, tree_node2, e->weight, e->label); node_stack.push(inner_node); visited.insert(inner_node); } } delete eit; } return t; }
std::pair<NodeSet,bool> Liveness::getAllReachingDefsRecImpl(RegisterRef RefRR, NodeAddr<RefNode*> RefA, NodeSet &Visited, const NodeSet &Defs, unsigned Nest, unsigned MaxNest) { if (Nest > MaxNest) return { NodeSet(), false }; // Collect all defined registers. Do not consider phis to be defining // anything, only collect "real" definitions. RegisterAggr DefRRs(PRI); for (NodeId D : Defs) { const auto DA = DFG.addr<const DefNode*>(D); if (!(DA.Addr->getFlags() & NodeAttrs::PhiRef)) DefRRs.insert(DA.Addr->getRegRef(DFG)); } NodeList RDs = getAllReachingDefs(RefRR, RefA, false, true, DefRRs); if (RDs.empty()) return { Defs, true }; // Make a copy of the preexisting definitions and add the newly found ones. NodeSet TmpDefs = Defs; for (NodeAddr<NodeBase*> R : RDs) TmpDefs.insert(R.Id); NodeSet Result = Defs; for (NodeAddr<DefNode*> DA : RDs) { Result.insert(DA.Id); if (!(DA.Addr->getFlags() & NodeAttrs::PhiRef)) continue; NodeAddr<PhiNode*> PA = DA.Addr->getOwner(DFG); if (Visited.count(PA.Id)) continue; Visited.insert(PA.Id); // Go over all phi uses and get the reaching defs for each use. for (auto U : PA.Addr->members_if(DFG.IsRef<NodeAttrs::Use>, DFG)) { const auto &T = getAllReachingDefsRecImpl(RefRR, U, Visited, TmpDefs, Nest+1, MaxNest); if (!T.second) return { T.first, false }; Result.insert(T.first.begin(), T.first.end()); } } return { Result, true }; }
NodeSet Liveness::getAllReachingDefsRec(RegisterRef RefRR, NodeAddr<RefNode*> RefA, NodeSet &Visited, const NodeSet &Defs) { // Collect all defined registers. Do not consider phis to be defining // anything, only collect "real" definitions. RegisterSet DefRRs; for (const auto D : Defs) { const auto DA = DFG.addr<const DefNode*>(D); if (!(DA.Addr->getFlags() & NodeAttrs::PhiRef)) DefRRs.insert(DA.Addr->getRegRef()); } auto RDs = getAllReachingDefs(RefRR, RefA, true, DefRRs); if (RDs.empty()) return Defs; // Make a copy of the preexisting definitions and add the newly found ones. NodeSet TmpDefs = Defs; for (auto R : RDs) TmpDefs.insert(R.Id); NodeSet Result = Defs; for (NodeAddr<DefNode*> DA : RDs) { Result.insert(DA.Id); if (!(DA.Addr->getFlags() & NodeAttrs::PhiRef)) continue; NodeAddr<PhiNode*> PA = DA.Addr->getOwner(DFG); if (Visited.count(PA.Id)) continue; Visited.insert(PA.Id); // Go over all phi uses and get the reaching defs for each use. for (auto U : PA.Addr->members_if(DFG.IsRef<NodeAttrs::Use>, DFG)) { const auto &T = getAllReachingDefsRec(RefRR, U, Visited, TmpDefs); Result.insert(T.begin(), T.end()); } } return Result; }
/* * DeleteCFG() * Remove one CFG. */ void DeleteCFG(GraphNode *Root) { NodeVec VisitStack; NodeSet Visited; VisitStack.push_back(Root); while(VisitStack.size()) { GraphNode *Parent = VisitStack.back(); VisitStack.pop_back(); if (Visited.count(Parent)) continue; Visited.insert(Parent); NodeVec &Child = Parent->getSuccessor(); for (int i = 0, e = Child.size(); i < e; i++) VisitStack.push_back(Child[i]); } for (NodeSet::iterator I = Visited.begin(), E = Visited.end(); I != E; I++) delete *I; }
void Liveness::computePhiInfo() { RealUseMap.clear(); NodeList Phis; NodeAddr<FuncNode*> FA = DFG.getFunc(); auto Blocks = FA.Addr->members(DFG); for (NodeAddr<BlockNode*> BA : Blocks) { auto Ps = BA.Addr->members_if(DFG.IsCode<NodeAttrs::Phi>, DFG); Phis.insert(Phis.end(), Ps.begin(), Ps.end()); } // phi use -> (map: reaching phi -> set of registers defined in between) std::map<NodeId,std::map<NodeId,RegisterSet>> PhiUp; std::vector<NodeId> PhiUQ; // Work list of phis for upward propagation. // Go over all phis. for (NodeAddr<PhiNode*> PhiA : Phis) { // Go over all defs and collect the reached uses that are non-phi uses // (i.e. the "real uses"). auto &RealUses = RealUseMap[PhiA.Id]; auto PhiRefs = PhiA.Addr->members(DFG); // Have a work queue of defs whose reached uses need to be found. // For each def, add to the queue all reached (non-phi) defs. SetVector<NodeId> DefQ; NodeSet PhiDefs; for (auto R : PhiRefs) { if (!DFG.IsRef<NodeAttrs::Def>(R)) continue; DefQ.insert(R.Id); PhiDefs.insert(R.Id); } for (unsigned i = 0; i < DefQ.size(); ++i) { NodeAddr<DefNode*> DA = DFG.addr<DefNode*>(DefQ[i]); NodeId UN = DA.Addr->getReachedUse(); while (UN != 0) { NodeAddr<UseNode*> A = DFG.addr<UseNode*>(UN); if (!(A.Addr->getFlags() & NodeAttrs::PhiRef)) RealUses[getRestrictedRegRef(A)].insert(A.Id); UN = A.Addr->getSibling(); } NodeId DN = DA.Addr->getReachedDef(); while (DN != 0) { NodeAddr<DefNode*> A = DFG.addr<DefNode*>(DN); for (auto T : DFG.getRelatedRefs(A.Addr->getOwner(DFG), A)) { uint16_t Flags = NodeAddr<DefNode*>(T).Addr->getFlags(); // Must traverse the reached-def chain. Consider: // def(D0) -> def(R0) -> def(R0) -> use(D0) // The reachable use of D0 passes through a def of R0. if (!(Flags & NodeAttrs::PhiRef)) DefQ.insert(T.Id); } DN = A.Addr->getSibling(); } } // Filter out these uses that appear to be reachable, but really // are not. For example: // // R1:0 = d1 // = R1:0 u2 Reached by d1. // R0 = d3 // = R1:0 u4 Still reached by d1: indirectly through // the def d3. // R1 = d5 // = R1:0 u6 Not reached by d1 (covered collectively // by d3 and d5), but following reached // defs and uses from d1 will lead here. auto HasDef = [&PhiDefs] (NodeAddr<DefNode*> DA) -> bool { return PhiDefs.count(DA.Id); }; for (auto UI = RealUses.begin(), UE = RealUses.end(); UI != UE; ) { // For each reached register UI->first, there is a set UI->second, of // uses of it. For each such use, check if it is reached by this phi, // i.e. check if the set of its reaching uses intersects the set of // this phi's defs. auto &Uses = UI->second; for (auto I = Uses.begin(), E = Uses.end(); I != E; ) { auto UA = DFG.addr<UseNode*>(*I); NodeList RDs = getAllReachingDefs(UI->first, UA); if (std::any_of(RDs.begin(), RDs.end(), HasDef)) ++I; else I = Uses.erase(I); } if (Uses.empty()) UI = RealUses.erase(UI); else ++UI; } // If this phi reaches some "real" uses, add it to the queue for upward // propagation. if (!RealUses.empty()) PhiUQ.push_back(PhiA.Id); // Go over all phi uses and check if the reaching def is another phi. // Collect the phis that are among the reaching defs of these uses. // While traversing the list of reaching defs for each phi use, collect // the set of registers defined between this phi (Phi) and the owner phi // of the reaching def. for (auto I : PhiRefs) { if (!DFG.IsRef<NodeAttrs::Use>(I)) continue; NodeAddr<UseNode*> UA = I; auto &UpMap = PhiUp[UA.Id]; RegisterSet DefRRs; for (NodeAddr<DefNode*> DA : getAllReachingDefs(UA)) { if (DA.Addr->getFlags() & NodeAttrs::PhiRef) UpMap[DA.Addr->getOwner(DFG).Id] = DefRRs; else DefRRs.insert(DA.Addr->getRegRef()); } } } if (Trace) { dbgs() << "Phi-up-to-phi map:\n"; for (auto I : PhiUp) { dbgs() << "phi " << Print<NodeId>(I.first, DFG) << " -> {"; for (auto R : I.second) dbgs() << ' ' << Print<NodeId>(R.first, DFG) << Print<RegisterSet>(R.second, DFG); dbgs() << " }\n"; } } // Propagate the reached registers up in the phi chain. // // The following type of situation needs careful handling: // // phi d1<R1:0> (1) // | // ... d2<R1> // | // phi u3<R1:0> (2) // | // ... u4<R1> // // The phi node (2) defines a register pair R1:0, and reaches a "real" // use u4 of just R1. The same phi node is also known to reach (upwards) // the phi node (1). However, the use u4 is not reached by phi (1), // because of the intervening definition d2 of R1. The data flow between // phis (1) and (2) is restricted to R1:0 minus R1, i.e. R0. // // When propagating uses up the phi chains, get the all reaching defs // for a given phi use, and traverse the list until the propagated ref // is covered, or until or until reaching the final phi. Only assume // that the reference reaches the phi in the latter case. for (unsigned i = 0; i < PhiUQ.size(); ++i) { auto PA = DFG.addr<PhiNode*>(PhiUQ[i]); auto &RealUses = RealUseMap[PA.Id]; for (auto U : PA.Addr->members_if(DFG.IsRef<NodeAttrs::Use>, DFG)) { NodeAddr<UseNode*> UA = U; auto &UpPhis = PhiUp[UA.Id]; for (auto UP : UpPhis) { bool Changed = false; auto &MidDefs = UP.second; // Collect the set UpReached of uses that are reached by the current // phi PA, and are not covered by any intervening def between PA and // the upward phi UP. RegisterSet UpReached; for (auto T : RealUses) { if (!isRestricted(PA, UA, T.first)) continue; if (!RAI.covers(MidDefs, T.first)) UpReached.insert(T.first); } if (UpReached.empty()) continue; // Update the set PRUs of real uses reached by the upward phi UP with // the actual set of uses (UpReached) that the UP phi reaches. auto &PRUs = RealUseMap[UP.first]; for (auto R : UpReached) { unsigned Z = PRUs[R].size(); PRUs[R].insert(RealUses[R].begin(), RealUses[R].end()); Changed |= (PRUs[R].size() != Z); } if (Changed) PhiUQ.push_back(UP.first); } } } if (Trace) { dbgs() << "Real use map:\n"; for (auto I : RealUseMap) { dbgs() << "phi " << Print<NodeId>(I.first, DFG); NodeAddr<PhiNode*> PA = DFG.addr<PhiNode*>(I.first); NodeList Ds = PA.Addr->members_if(DFG.IsRef<NodeAttrs::Def>, DFG); if (!Ds.empty()) { RegisterRef RR = NodeAddr<DefNode*>(Ds[0]).Addr->getRegRef(); dbgs() << '<' << Print<RegisterRef>(RR, DFG) << '>'; } else { dbgs() << "<noreg>"; } dbgs() << " -> " << Print<RefMap>(I.second, DFG) << '\n'; } } }
void Liveness::computePhiInfo() { RealUseMap.clear(); NodeList Phis; NodeAddr<FuncNode*> FA = DFG.getFunc(); NodeList Blocks = FA.Addr->members(DFG); for (NodeAddr<BlockNode*> BA : Blocks) { auto Ps = BA.Addr->members_if(DFG.IsCode<NodeAttrs::Phi>, DFG); Phis.insert(Phis.end(), Ps.begin(), Ps.end()); } // phi use -> (map: reaching phi -> set of registers defined in between) std::map<NodeId,std::map<NodeId,RegisterAggr>> PhiUp; std::vector<NodeId> PhiUQ; // Work list of phis for upward propagation. // Go over all phis. for (NodeAddr<PhiNode*> PhiA : Phis) { // Go over all defs and collect the reached uses that are non-phi uses // (i.e. the "real uses"). RefMap &RealUses = RealUseMap[PhiA.Id]; NodeList PhiRefs = PhiA.Addr->members(DFG); // Have a work queue of defs whose reached uses need to be found. // For each def, add to the queue all reached (non-phi) defs. SetVector<NodeId> DefQ; NodeSet PhiDefs; for (NodeAddr<RefNode*> R : PhiRefs) { if (!DFG.IsRef<NodeAttrs::Def>(R)) continue; DefQ.insert(R.Id); PhiDefs.insert(R.Id); } // Collect the super-set of all possible reached uses. This set will // contain all uses reached from this phi, either directly from the // phi defs, or (recursively) via non-phi defs reached by the phi defs. // This set of uses will later be trimmed to only contain these uses that // are actually reached by the phi defs. for (unsigned i = 0; i < DefQ.size(); ++i) { NodeAddr<DefNode*> DA = DFG.addr<DefNode*>(DefQ[i]); // Visit all reached uses. Phi defs should not really have the "dead" // flag set, but check it anyway for consistency. bool IsDead = DA.Addr->getFlags() & NodeAttrs::Dead; NodeId UN = !IsDead ? DA.Addr->getReachedUse() : 0; while (UN != 0) { NodeAddr<UseNode*> A = DFG.addr<UseNode*>(UN); uint16_t F = A.Addr->getFlags(); if ((F & (NodeAttrs::Undef | NodeAttrs::PhiRef)) == 0) { RegisterRef R = PRI.normalize(A.Addr->getRegRef(DFG)); RealUses[R.Reg].insert({A.Id,R.Mask}); } UN = A.Addr->getSibling(); } // Visit all reached defs, and add them to the queue. These defs may // override some of the uses collected here, but that will be handled // later. NodeId DN = DA.Addr->getReachedDef(); while (DN != 0) { NodeAddr<DefNode*> A = DFG.addr<DefNode*>(DN); for (auto T : DFG.getRelatedRefs(A.Addr->getOwner(DFG), A)) { uint16_t Flags = NodeAddr<DefNode*>(T).Addr->getFlags(); // Must traverse the reached-def chain. Consider: // def(D0) -> def(R0) -> def(R0) -> use(D0) // The reachable use of D0 passes through a def of R0. if (!(Flags & NodeAttrs::PhiRef)) DefQ.insert(T.Id); } DN = A.Addr->getSibling(); } } // Filter out these uses that appear to be reachable, but really // are not. For example: // // R1:0 = d1 // = R1:0 u2 Reached by d1. // R0 = d3 // = R1:0 u4 Still reached by d1: indirectly through // the def d3. // R1 = d5 // = R1:0 u6 Not reached by d1 (covered collectively // by d3 and d5), but following reached // defs and uses from d1 will lead here. auto InPhiDefs = [&PhiDefs] (NodeAddr<DefNode*> DA) -> bool { return PhiDefs.count(DA.Id); }; for (auto UI = RealUses.begin(), UE = RealUses.end(); UI != UE; ) { // For each reached register UI->first, there is a set UI->second, of // uses of it. For each such use, check if it is reached by this phi, // i.e. check if the set of its reaching uses intersects the set of // this phi's defs. NodeRefSet &Uses = UI->second; for (auto I = Uses.begin(), E = Uses.end(); I != E; ) { auto UA = DFG.addr<UseNode*>(I->first); // Undef flag is checked above. assert((UA.Addr->getFlags() & NodeAttrs::Undef) == 0); RegisterRef R(UI->first, I->second); NodeList RDs = getAllReachingDefs(R, UA); // If none of the reaching defs of R are from this phi, remove this // use of R. I = any_of(RDs, InPhiDefs) ? std::next(I) : Uses.erase(I); } UI = Uses.empty() ? RealUses.erase(UI) : std::next(UI); } // If this phi reaches some "real" uses, add it to the queue for upward // propagation. if (!RealUses.empty()) PhiUQ.push_back(PhiA.Id); // Go over all phi uses and check if the reaching def is another phi. // Collect the phis that are among the reaching defs of these uses. // While traversing the list of reaching defs for each phi use, accumulate // the set of registers defined between this phi (PhiA) and the owner phi // of the reaching def. NodeSet SeenUses; for (auto I : PhiRefs) { if (!DFG.IsRef<NodeAttrs::Use>(I) || SeenUses.count(I.Id)) continue; NodeAddr<PhiUseNode*> PUA = I; if (PUA.Addr->getReachingDef() == 0) continue; RegisterRef UR = PUA.Addr->getRegRef(DFG); NodeList Ds = getAllReachingDefs(UR, PUA, true, false, NoRegs); RegisterAggr DefRRs(PRI); for (NodeAddr<DefNode*> D : Ds) { if (D.Addr->getFlags() & NodeAttrs::PhiRef) { NodeId RP = D.Addr->getOwner(DFG).Id; std::map<NodeId,RegisterAggr> &M = PhiUp[PUA.Id]; auto F = M.find(RP); if (F == M.end()) M.insert(std::make_pair(RP, DefRRs)); else F->second.insert(DefRRs); } DefRRs.insert(D.Addr->getRegRef(DFG)); } for (NodeAddr<PhiUseNode*> T : DFG.getRelatedRefs(PhiA, PUA)) SeenUses.insert(T.Id); } } if (Trace) { dbgs() << "Phi-up-to-phi map with intervening defs:\n"; for (auto I : PhiUp) { dbgs() << "phi " << Print<NodeId>(I.first, DFG) << " -> {"; for (auto R : I.second) dbgs() << ' ' << Print<NodeId>(R.first, DFG) << Print<RegisterAggr>(R.second, DFG); dbgs() << " }\n"; } } // Propagate the reached registers up in the phi chain. // // The following type of situation needs careful handling: // // phi d1<R1:0> (1) // | // ... d2<R1> // | // phi u3<R1:0> (2) // | // ... u4<R1> // // The phi node (2) defines a register pair R1:0, and reaches a "real" // use u4 of just R1. The same phi node is also known to reach (upwards) // the phi node (1). However, the use u4 is not reached by phi (1), // because of the intervening definition d2 of R1. The data flow between // phis (1) and (2) is restricted to R1:0 minus R1, i.e. R0. // // When propagating uses up the phi chains, get the all reaching defs // for a given phi use, and traverse the list until the propagated ref // is covered, or until reaching the final phi. Only assume that the // reference reaches the phi in the latter case. for (unsigned i = 0; i < PhiUQ.size(); ++i) { auto PA = DFG.addr<PhiNode*>(PhiUQ[i]); NodeList PUs = PA.Addr->members_if(DFG.IsRef<NodeAttrs::Use>, DFG); RefMap &RUM = RealUseMap[PA.Id]; for (NodeAddr<UseNode*> UA : PUs) { std::map<NodeId,RegisterAggr> &PUM = PhiUp[UA.Id]; RegisterRef UR = PRI.normalize(UA.Addr->getRegRef(DFG)); for (const std::pair<NodeId,RegisterAggr> &P : PUM) { bool Changed = false; const RegisterAggr &MidDefs = P.second; // Collect the set PropUp of uses that are reached by the current // phi PA, and are not covered by any intervening def between the // currently visited use UA and the the upward phi P. if (MidDefs.hasCoverOf(UR)) continue; // General algorithm: // for each (R,U) : U is use node of R, U is reached by PA // if MidDefs does not cover (R,U) // then add (R-MidDefs,U) to RealUseMap[P] // for (const std::pair<RegisterId,NodeRefSet> &T : RUM) { RegisterRef R = DFG.restrictRef(RegisterRef(T.first), UR); if (!R) continue; for (std::pair<NodeId,LaneBitmask> V : T.second) { RegisterRef S = DFG.restrictRef(RegisterRef(R.Reg, V.second), R); if (!S) continue; if (RegisterRef SS = MidDefs.clearIn(S)) { NodeRefSet &RS = RealUseMap[P.first][SS.Reg]; Changed |= RS.insert({V.first,SS.Mask}).second; } } } if (Changed) PhiUQ.push_back(P.first); } } } if (Trace) { dbgs() << "Real use map:\n"; for (auto I : RealUseMap) { dbgs() << "phi " << Print<NodeId>(I.first, DFG); NodeAddr<PhiNode*> PA = DFG.addr<PhiNode*>(I.first); NodeList Ds = PA.Addr->members_if(DFG.IsRef<NodeAttrs::Def>, DFG); if (!Ds.empty()) { RegisterRef RR = NodeAddr<DefNode*>(Ds[0]).Addr->getRegRef(DFG); dbgs() << '<' << Print<RegisterRef>(RR, DFG) << '>'; } else { dbgs() << "<noreg>"; } dbgs() << " -> " << Print<RefMap>(I.second, DFG) << '\n'; } } }
PointerAnalysis::ArgumentAttributes PointerAnalysis::objectPass(Function *funct, PointerAnalysis::ArgumentAttributes argAttrs) { ArgumentAttributes ret(1); ret[0] = UNBOUND_ATTR; ArgumentAttributes UNBOUND(1); ret[0] = UNBOUND_ATTR; WorkList<Node> worklist; NodeSet visits; ValueMap exactOut, boundOut; ValueSet exactArgs, boundArgs; BoundMap exactBounds; llvm::DataLayout DL(module); // how to test no funct body? if (funct->begin() == funct->end()) return ret; for (auto &globalVal: module->globals()) { GlobalVariable *gv = dyn_cast<GlobalVariable>(&globalVal); exactBounds[gv] = globalBounds[gv]; } auto args = funct->arg_begin(); for (size_t i = 0; i < funct->arg_size(); i++) { if (argAttrs[i] == UNBOUND_ATTR) { ++args; continue; } Value *arg = &(*args); boundArgs.insert( arg ); if (argAttrs[i] != DYNBOUND_ATTR) { exactArgs.insert( arg ); exactBounds[arg] = argAttrs[i]; } ++args; } outs() << "********** visiting " << funct->getName() << " **********\n"; printX(argAttrs); printX(exactArgs); printX(boundArgs); Node entry = &funct->getEntryBlock(); worklist.enqueue(entry); std::unordered_set<Node> exits; for (auto &bb: *funct) if ( llvm::succ_begin(&bb) == llvm::succ_end(&bb) ) exits.insert(&bb); while (!worklist.empty()) { Node next = worklist.dequeue(); bool changed = false; bool visited = (visits.count(next) != 0); visits.insert(next); outs() << "visiting " << next->getName() << "\n"; ValueSet oldExact = getOrInsert(next, exactOut), oldBound = getOrInsert(next, boundOut); ValueSet exactTemp, boundTemp; if (next == entry) { exactTemp = merge( exactTemp, exactArgs ); exactTemp = merge( exactTemp, globals ); boundTemp = merge( boundTemp, boundArgs ); boundTemp = merge( boundTemp, globals ); } else { for (auto i = pred_begin(next); i != pred_end(next); ++i) { exactTemp = merge( exactTemp, getOrInsert(*i, exactOut) ); boundTemp = merge( boundTemp, getOrInsert(*i, boundOut) ); } } for (auto &i : *next) { Instruction *inst = &i; if (isa<CallInst>(inst)) { // do inter-procedual analysis // what if I am calling some external library: handle that either // if (is a well-known exteral function) { // some external library // analyze it at best effort // } else { // not an external function // compare the args to the last time we enter this callsite // if they are the same, skip (TODO: pull out what we've got last time) // otherwise analyze it again // } CallInst *call_inst = dyn_cast<CallInst> (inst); Function *callee = call_inst->getCalledFunction(); if (callee == nullptr) continue; if (isExternalLibrary(inst)) { bool isAlloca = isAllocation(inst); int allocaSize = getConstantAllocSize(inst); if (isAlloca) { boundTemp.insert(inst); if (allocaSize) { exactTemp.insert(inst); exactBounds[inst] = allocaSize; } } if (isaPointer(inst)) { if ( lookupName(retArg0Funcs, callee->getName().data()) ) { Value *arg0 = call_inst->getArgOperand(0); if (test(arg0, boundTemp)) { boundTemp.insert(inst); } if (test(arg0, exactTemp)) { exactTemp.insert(inst); exactBounds[inst] = exactBounds[arg0]; } } if ( lookupName(retArg1Funcs, callee->getName().data()) ) { Value *arg1 = call_inst->getArgOperand(1); if (test(arg1, boundTemp)) { boundTemp.insert(inst); } if (test(arg1, exactTemp)) { exactTemp.insert(inst); exactBounds[inst] = exactBounds[arg1]; } } if ( lookupName(retArg2Funcs, callee->getName().data()) ) { Value *arg2 = call_inst->getArgOperand(2); if (test(arg2, boundTemp)) { boundTemp.insert(inst); } if (test(arg2, exactTemp)) { exactTemp.insert(inst); exactBounds[inst] = exactBounds[arg2]; } } } } else if ( !callee->isVarArg()) { bool first = (callsites.count(call_inst) == 0); callsites.insert(call_inst); auto oldArgs = argsCall[call_inst]; ArgumentAttributes args(call_inst->getNumArgOperands()); for (unsigned i = 0; i < call_inst->getNumArgOperands(); i++) { Value *argi = call_inst->getArgOperand(i); if (exactTemp.count(argi)) args[i] = exactBounds[argi]; else if (boundTemp.count(argi)) args[i] = DYNBOUND_ATTR; else args[i] = UNBOUND_ATTR; } outs() << ">>> begin call\n"; bool changed = first || ( unEqual(args, oldArgs) ); ArgumentAttributes r; if (changed) { argsCall[call_inst] = args; retCall[call_inst] = UNBOUND; r = objectPass(callee, args); retCall[call_inst] = r; } else { r = retCall[call_inst]; } assert("r must have size 1" && r.size() == 1); outs() << ">>> return from call\n"; if (r[0] == DYNBOUND_ATTR) boundTemp.insert( inst ); else if (r[0] != UNBOUND_ATTR) { boundTemp.insert( inst ); exactTemp.insert( inst ); exactBounds[inst] = r[0]; } } continue; } if (!isaPointer(inst)) continue; Type* deRefTy = inst->getType()->getContainedType(0); uint64_t deBound = sizeOf(deRefTy, DL); // handle a Pointer if ( isa<AllocaInst>(inst) ) { AllocaInst *alloca_inst = dyn_cast<AllocaInst>(inst); Type *allocatedTy = alloca_inst->getAllocatedType(); uint64_t stride = sizeOf(allocatedTy, DL); uint64_t length = getAllocaArraySize(alloca_inst); boundTemp.insert(inst); if (length) { exactTemp.insert(inst); exactBounds[inst] = stride * length; } } if ( isa<PHINode>(inst) ) { // possibly an inbound, but never transitive const PHINode *phi = dyn_cast<PHINode>(inst); bool allInbound = true; for (unsigned i = 0; i < phi->getNumIncomingValues(); i++) { Value *iv = phi->getIncomingValue(i); if (!test(iv, boundTemp)) { allInbound = false; break; } } if (allInbound) { boundTemp.insert(inst); } } if ( isa<GetElementPtrInst>(inst) ) { // inbound && exact && offset in range => inbound && transitive GetElementPtrInst *gep_inst = dyn_cast<GetElementPtrInst>(inst); Value *basePtr = gep_inst->getPointerOperand(); uint64_t origBound = exactBounds[basePtr]; APInt offset(64, false); bool testB = test(basePtr, boundTemp); bool testE = test(basePtr, exactTemp); bool testC = gep_inst->accumulateConstantOffset(DL, offset); // should I convert to byte count? A: NO. uint64_t intOff = offset.getLimitedValue(); // outs() << "getelementptr: " << inst->getName() << ", offset=" << intOff << "\n"; bool testR = testC && (origBound > 0) && (intOff + deBound <= origBound); if ( testB && testE && testR ) { boundTemp.insert(inst); exactTemp.insert(inst); exactBounds[inst] = exactBounds[basePtr] - (intOff); } } if ( isa<CastInst>(inst) ) { // inbound && transitive && bound decreased => inbound && transitive CastInst *cast_inst = dyn_cast<CastInst>(inst); if (cast_inst->getSrcTy()->isPointerTy()) { Value *srcPtr = cast_inst->getOperand(0); bool testB = test(srcPtr, boundTemp); bool testE = test(srcPtr, exactTemp); uint64_t origBound = exactBounds[srcPtr]; bool testR = (origBound > 0) && (deBound <= origBound); if ( testB && testE && testR ) { boundTemp.insert(inst); exactTemp.insert(inst); exactBounds[inst] = origBound; } } } // TODO: handle load/store // default case: nothing change } changed = !visited || unEqual(exactTemp, oldExact) || unEqual(boundTemp, oldBound); if (changed) { for (auto i = succ_begin(next); i != succ_end(next); ++i) { worklist.enqueue(*i); } } exactOut[next] = exactTemp; boundOut[next] = boundTemp; } ArgumentAttributes retSet(exits.size()); bool hasUnbound = false, hasDynbound = false; outs() << "<<< " << funct->getName() << " Returns :\n"; int i = 0; for (auto bb: exits) { outs() << bb->getName() << ":\t"; joinWith(boundOut[bb], funct); printX(boundOut[bb]); Instruction *term_inst = bb->getTerminator(); ReturnInst *ret_inst = dyn_cast<ReturnInst>(term_inst); if (ret_inst != nullptr) { Value *retVal = ret_inst->getReturnValue(); if ( retVal == nullptr || (!exactOut[bb].count(retVal) && !boundOut[bb].count(retVal)) ) { hasUnbound = true; break; } if (exactOut[bb].count(retVal)) retSet[i++] = exactBounds[retVal]; else { retSet[i++] = DYNBOUND_ATTR; hasDynbound = true; } } } outs() << "<<<\n"; ArgumentAttribute ret0 = retSet[0]; for (int i = 0; i < retSet.size(); i++) if (retSet[i] != ret0) hasDynbound = true; if (hasUnbound) ret[0] = UNBOUND_ATTR; else if (hasDynbound) ret[0] = DYNBOUND_ATTR; else { ret[0] = ret0; } if (!funct->getReturnType()->isPointerTy()) { //assert( "If is not pointer muxt be unbound" && // (ret[0] == UNBOUND_ATTR) ); ret[0] = UNBOUND_ATTR; } printX(ret); return ret; }