void LLVMDefUseAnalysis::addDataDependence(LLVMNode *node, llvm::Value *rdval) { LLVMNode *rdnode = dg->getNode(rdval); if (!rdnode) { // that means that the value is not from this graph. // We need to add interprocedural edge llvm::Function *F = llvm::cast<llvm::Instruction>(rdval)->getParent()->getParent(); LLVMNode *entryNode = dg->getGlobalNode(F); assert(entryNode && "Don't have built function"); // get the graph where the node lives LLVMDependenceGraph *graph = entryNode->getDG(); assert(graph != dg && "Cannot find a node"); rdnode = graph->getNode(rdval); if (!rdnode) { llvmutils::printerr("[DU] error: DG doesn't have val: ", rdval); abort(); return; } } assert(rdnode); rdnode->addDataDependence(node); }
void LLVMDGVerifier::checkGraph(llvm::Function *F, LLVMDependenceGraph *g) { using namespace llvm; LLVMNode *entry = g->getEntry(); if (!entry) { fault("has no entry for %s", F->getName().data()); return; } const llvm::Function *func = dyn_cast<Function>(entry->getKey()); if (!func) { fault("key in entry node is not a llvm::Function"); return; } size_t a, b; a = g->getBlocks().size(); b = func->size(); if (a != b) fault("have constructed %lu BBlocks but function has %lu basic blocks", a, b); for (BasicBlock& llvmBB : *F) { LLVMBBlock *BB = g->getBlocks()[&llvmBB]; if (!BB) { fault("missing BasicBlock"); errs() << llvmBB << "\n"; } else checkBBlock(&llvmBB, BB); } }
bool LLVMReachingDefsAnalysis::handleStoreInst(LLVMNode *storeNode, DefMap *df, PointsToSetT *&strong_update) { bool changed = false; llvm::StoreInst *SI = cast<StoreInst>(storeNode->getValue()); LLVMNode *ptrNode = getOperand(storeNode, SI->getPointerOperand(), 0); assert(ptrNode && "No pointer operand"); // update definitions PointsToSetT& S = ptrNode->getPointsTo(); // if we have only one concrete pointer (known pointer // with known offset), it is safe to do strong update if (S.size() == 1) { const Pointer& ptr = *S.begin(); // NOTE: we don't have good mechanism to diferentiate // heap-allocated objects yet, so if the pointer points to heap, // we must do weak update if (ptr.isKnown() && !ptr.offset.isUnknown() && !ptr.pointsToHeap()) { changed |= df->update(ptr, storeNode); strong_update = &S; return changed; } // else fall-through to weak update } // weak update for (const Pointer& ptr : ptrNode->getPointsTo()) changed |= df->add(ptr, storeNode); return changed; }
bool LLVMReachingDefsAnalysis::handleIntrinsicCall(LLVMNode *callNode, CallInst *CI, DefMap *df) { bool changed = false; IntrinsicInst *I = cast<IntrinsicInst>(CI); Value *dest; switch (I->getIntrinsicID()) { case Intrinsic::memmove: case Intrinsic::memcpy: case Intrinsic::memset: dest = I->getOperand(0); break; default: return handleUndefinedCall(callNode, CI, df); } LLVMNode *destNode = getOperand(callNode, dest, 1); assert(destNode && "No operand for intrinsic call"); for (const Pointer& ptr : destNode->getPointsTo()) { // we could compute all the concrete offsets, but // these functions usually set the whole memory, // so if we use UNKNOWN_OFFSET, the effect is the same changed |= df->add(Pointer(ptr.obj, UNKNOWN_OFFSET), callNode); } return changed; }
bool LLVMReachingDefsAnalysis::handleUndefinedCall(LLVMNode *callNode, CallInst *CI, DefMap *df) { bool changed = false; for (unsigned n = 1, e = callNode->getOperandsNum(); n < e; ++n) { Value *llvmOp = CI->getOperand(n - 1); if (!llvmOp->getType()->isPointerTy()) continue; if (isa<Constant>(llvmOp->stripInBoundsOffsets())) continue; LLVMNode *op = getOperand(callNode, llvmOp, n); assert(op && "unhandled pointer operand in undef call"); // with undefined call we must assume that any // memory that was passed via pointer was modified // and on unknown offset // XXX we should handle external globals too for (const Pointer& ptr : op->getPointsTo()) changed |= df->add(Pointer(ptr.obj, UNKNOWN_OFFSET), callNode); } return changed; }
static bool handleParams(LLVMNode *callNode, unsigned vararg, LLVMDGParameters *params, DefMap *df, DefMap *subgraph_df) { bool changed = false; // operand[0] is the called func for (int i = 1, e = callNode->getOperandsNum(); i < e; ++i) { LLVMNode *op = callNode->getOperand(i); if (!op) continue; if (!op->isPointerTy()) continue; LLVMDGParameter *p = params->find(op->getKey()); if (!p) { #ifdef DEBUG_ENABLED if (i - 1 < (int) vararg) DBG("ERR: no actual param for " << *op->getKey()); #endif continue; } changed |= handleParam(op, p->out, df, subgraph_df); } return changed; }
void LLVMDGVerifier::checkBBlock(const llvm::BasicBlock *llvmBB, LLVMBBlock *BB) { using namespace llvm; auto BBIT = BB->getNodes().begin(); for (const Instruction& I : *llvmBB) { LLVMNode *node = *BBIT; // check if we have the CFG edges set if (node->getKey() != &I) fault("wrong node in BB"); checkNode(&I, node); ++BBIT; } // FIXME: check successors and predecessors }
void LLVMDefUseAnalysis::handleInlineAsm(LLVMNode *callNode) { CallInst *CI = cast<CallInst>(callNode->getValue()); LLVMDependenceGraph *dg = callNode->getDG(); // the last operand is the asm itself, so iterate only to e - 1 for (unsigned i = 0, e = CI->getNumOperands(); i < e - 1; ++i) { Value *opVal = CI->getOperand(i); if (!opVal->getType()->isPointerTy()) continue; LLVMNode *opNode = dg->getNode(opVal->stripInBoundsOffsets()); if (!opNode) { // FIXME: ConstantExpr llvmutils::printerr("WARN: unhandled inline asm operand: ", opVal); continue; } assert(opNode && "Do not have an operand for inline asm"); // if nothing else, this call at least uses the operands opNode->addDataDependence(callNode); } }
static std::set<LLVMNode *> getSlicingCriteriaNodes(LLVMDependenceGraph& dg, const std::string& slicingCriteria) { std::set<LLVMNode *> nodes; std::vector<std::string> criteria = splitList(slicingCriteria); assert(!criteria.empty() && "Did not get slicing criteria"); std::vector<std::string> line_criteria; std::vector<std::string> node_criteria; std::tie(line_criteria, node_criteria) = splitStringVector(criteria, [](std::string& s) -> bool { return s.find(':') != std::string::npos; } ); // if user wants to slice with respect to the return of main, // insert the ret instructions to the nodes. for (const auto& c : node_criteria) { if (c == "ret") { LLVMNode *exit = dg.getExit(); // We could insert just the exit node, but this way we will // get annotations to the functions. for (auto it = exit->rev_control_begin(), et = exit->rev_control_end(); it != et; ++it) { nodes.insert(*it); } } } // map the criteria to nodes if (!node_criteria.empty()) dg.getCallSites(node_criteria, &nodes); if (!line_criteria.empty()) getLineCriteriaNodes(dg, line_criteria, nodes); return nodes; }