//------------------------------------------------------------------ /// Scan a function to see if any instructions are interesting /// /// @param[in] f /// The function to be inspected. /// /// @return /// False if there was an error scanning; true otherwise. //------------------------------------------------------------------ virtual bool InspectFunction(llvm::Function &f) { for (llvm::Function::iterator bbi = f.begin(), last_bbi = f.end(); bbi != last_bbi; ++bbi) { if (!InspectBasicBlock(*bbi)) return false; } return true; }
bool BitcastCallEliminator::runOnFunction(llvm::Function &function) { bool res = false; bool doLoop = true; while (doLoop) { doLoop = false; for (llvm::Function::iterator i = function.begin(), e = function.end(); i != e; ++i) { llvm::BasicBlock *bb = &*i; bool bbchanged = false; for (llvm::BasicBlock::iterator ibb = bb->begin(), ebb = bb->end(); ibb != ebb; ++ibb) { llvm::Instruction *inst = &*ibb; if (llvm::isa<llvm::CallInst>(inst) && llvm::cast<llvm::CallInst>(inst)->getCalledFunction() == NULL) { llvm::CallInst *callInst = llvm::cast<llvm::CallInst>(inst); llvm::Value *calledValue = callInst->getCalledValue(); llvm::Value *bareCalledValue = calledValue->stripPointerCasts(); if (llvm::isa<llvm::Function>(bareCalledValue)) { const llvm::FunctionType *calledType = llvm::cast<llvm::FunctionType>(llvm::cast<llvm::PointerType>(calledValue->getType())->getContainedType(0)); const llvm::FunctionType *calleeType = llvm::cast<llvm::Function>(bareCalledValue)->getFunctionType(); if (calledType->getReturnType() == calleeType->getReturnType()) { if (argsMatch(calleeType, callInst)) { std::vector<llvm::Value*> args; unsigned int numArgs = callInst->getNumArgOperands(); for (unsigned int k = 0; k < numArgs; ++k) { args.push_back(callInst->getArgOperand(k)); } #if LLVM_VERSION < VERSION(3, 0) llvm::CallInst *newCall = llvm::CallInst::Create(bareCalledValue, args.begin(), args.end(), "", inst); #else llvm::CallInst *newCall = llvm::CallInst::Create(bareCalledValue, args, "", inst); #endif inst->replaceAllUsesWith(newCall); llvm::StringRef name = inst->getName(); inst->eraseFromParent(); newCall->setName(name); res = true; doLoop = true; bbchanged = true; } } } } if (bbchanged) { break; } } } } return res; }
bool InstructionCount::runOnFunction(llvm::Function &Fun) { ICount = 0; // A llvm::Function is just a list of llvm::BasicBlock. In order to get // instruction count we can visit all llvm::BasicBlocks ... for(llvm::Function::const_iterator I = Fun.begin(), E = Fun.end(); I != E; ++I) // ... and sum the llvm::BasicBlock size -- A llvm::BasicBlock size is just // a list of instructions! ICount += I->size(); return false; }
bool _runOnFunction(llvm::Function& f) { Timer _t2("(sum)"); Timer _t("initializing"); initialize(); _t.split("overhead"); // f.dump(); llvm::Module* cur_module = f.getParent(); #if LLVMREV < 217548 llvm::PassManager fake_pm; #else llvm::legacy::PassManager fake_pm; #endif llvm::InlineCostAnalysis* cost_analysis = new llvm::InlineCostAnalysis(); fake_pm.add(cost_analysis); // llvm::errs() << "doing fake run\n"; fake_pm.run(*fake_module); // llvm::errs() << "done with fake run\n"; bool did_any_inlining = false; // TODO I haven't gotten the callgraph-updating part of the inliner to work, // so it's not easy to tell what callsites have been inlined into (ie added to) // the function. // One simple-but-not-great way to handle it is to just iterate over the entire function // multiple times and re-inline things until we don't want to inline any more; // NPASSES controls the maximum number of times to attempt that. // Right now we actually don't need that, since we only inline fully-optimized // functions (from the stdlib), and those will already have had inlining // applied recursively. const int NPASSES = 1; for (int passnum = 0; passnum < NPASSES; passnum++) { _t.split("collecting calls"); std::vector<llvm::CallSite> calls; for (llvm::inst_iterator I = llvm::inst_begin(f), E = llvm::inst_end(f); I != E; ++I) { llvm::CallInst* call = llvm::dyn_cast<llvm::CallInst>(&(*I)); // From Inliner.cpp: if (!call || llvm::isa<llvm::IntrinsicInst>(call)) continue; // I->dump(); llvm::CallSite CS(call); llvm::Value* v = CS.getCalledValue(); llvm::ConstantExpr* ce = llvm::dyn_cast<llvm::ConstantExpr>(v); if (!ce) continue; assert(ce->isCast()); llvm::ConstantInt* l_addr = llvm::cast<llvm::ConstantInt>(ce->getOperand(0)); int64_t addr = l_addr->getSExtValue(); if (addr == (int64_t)printf) continue; llvm::Function* f = g.func_addr_registry.getLLVMFuncAtAddress((void*)addr); if (f == NULL) { if (VERBOSITY()) { printf("Giving up on inlining %s:\n", g.func_addr_registry.getFuncNameAtAddress((void*)addr, true).c_str()); call->dump(); } continue; } // We load the bitcode lazily, so check if we haven't yet fully loaded the function: if (f->isMaterializable()) { #if LLVMREV < 220600 f->Materialize(); #else f->materialize(); #endif } // It could still be a declaration, though I think the code won't generate this case any more: if (f->isDeclaration()) continue; // Keep this section as a release_assert since the code-to-be-inlined, as well as the inlining // decisions, can be different in release mode: int op_idx = -1; for (llvm::Argument& arg : f->args()) { ++op_idx; llvm::Type* op_type = call->getOperand(op_idx)->getType(); if (arg.getType() != op_type) { llvm::errs() << f->getName() << " has arg " << op_idx << " mismatched!\n"; llvm::errs() << "Given "; op_type->dump(); llvm::errs() << " but underlying function expected "; arg.getType()->dump(); llvm::errs() << '\n'; } RELEASE_ASSERT(arg.getType() == call->getOperand(op_idx)->getType(), ""); } assert(!f->isDeclaration()); CS.setCalledFunction(f); calls.push_back(CS); } // assert(0 && "TODO"); // printf("%ld\n", calls.size()); bool did_inline = false; _t.split("doing inlining"); while (calls.size()) { llvm::CallSite cs = calls.back(); calls.pop_back(); // if (VERBOSITY("irgen.inlining") >= 1) { // llvm::errs() << "Evaluating callsite "; // cs->dump(); //} llvm::InlineCost IC = cost_analysis->getInlineCost(cs, threshold); bool do_inline = false; if (IC.isAlways()) { if (VERBOSITY("irgen.inlining") >= 2) llvm::errs() << "always inline\n"; do_inline = true; } else if (IC.isNever()) { if (VERBOSITY("irgen.inlining") >= 2) llvm::errs() << "never inline\n"; do_inline = false; } else { if (VERBOSITY("irgen.inlining") >= 2) llvm::errs() << "Inline cost: " << IC.getCost() << '\n'; do_inline = (bool)IC; } if (VERBOSITY("irgen.inlining") >= 1) { if (!do_inline) llvm::outs() << "not "; llvm::outs() << "inlining "; cs->dump(); } if (do_inline) { static StatCounter num_inlines("num_inlines"); num_inlines.log(); // llvm::CallGraph cg(*f.getParent()); ////cg.addToCallGraph(cs->getCalledFunction()); // llvm::InlineFunctionInfo InlineInfo(&cg); llvm::InlineFunctionInfo InlineInfo; bool inlined = llvm::InlineFunction(cs, InlineInfo, false); did_inline = did_inline || inlined; did_any_inlining = did_any_inlining || inlined; // if (inlined) // f.dump(); } } if (!did_inline) { if (passnum >= NPASSES - 1 && VERBOSITY("irgen.inlining")) printf("quitting after %d passes\n", passnum + 1); break; } } // TODO would be nice to break out here and not have to rematerialize the function; // I think I have to do that even if no inlining happened from the "setCalledFunction" call above. // I thought that'd just change the CS object, but maybe it changes the underlying instruction as well? // if (!did_any_inlining) // return false; _t.split("remapping"); llvm::ValueToValueMapTy VMap; for (llvm::Function::iterator I = f.begin(), E = f.end(); I != E; ++I) { VMap[I] = I; } MyMaterializer materializer(cur_module); for (llvm::inst_iterator I = llvm::inst_begin(f), E = llvm::inst_end(f); I != E; ++I) { RemapInstruction(&(*I), VMap, llvm::RF_None, NULL, &materializer); } _t.split("cleaning up"); std::vector<llvm::GlobalValue*> to_remove; for (llvm::Module::global_iterator I = cur_module->global_begin(), E = cur_module->global_end(); I != E; ++I) { if (I->use_empty()) { to_remove.push_back(I); continue; } } for (int i = 0; i < to_remove.size(); i++) { to_remove[i]->eraseFromParent(); } for (llvm::Module::iterator I = cur_module->begin(), E = cur_module->end(); I != E;) { if (!I->isDeclaration()) { ++I; continue; } if (I->use_empty()) { I = cur_module->getFunctionList().erase(I); } else { ++I; } } return did_any_inlining; }
// Propagate conditions bool ConditionPropagator::runOnFunction(llvm::Function &F) { m_map.clear(); llvm::SmallVector<std::pair<const llvm::BasicBlock*, const llvm::BasicBlock*>, 32> backedgesVector; llvm::FindFunctionBackedges(F, backedgesVector); std::set<std::pair<const llvm::BasicBlock*, const llvm::BasicBlock*> > backedges; backedges.insert(backedgesVector.begin(), backedgesVector.end()); if (m_debug) { std::cout << "========================================" << std::endl; } for (llvm::Function::iterator bbi = F.begin(), bbe = F.end(); bbi != bbe; ++bbi) { llvm::BasicBlock *bb = &*bbi; std::set<llvm::BasicBlock*> preds; for (llvm::Function::iterator tmpi = F.begin(), tmpe = F.end(); tmpi != tmpe; ++tmpi) { if (isPred(&*tmpi, bb) && backedges.find(std::make_pair(&*tmpi, bb)) == backedges.end()) { if (m_debug) { std::cout << bb->getName().str() << " has non-backedge predecessor " << tmpi->getName().str() << std::endl; } preds.insert(&*tmpi); } } std::set<llvm::Value*> trueSet; std::set<llvm::Value*> falseSet; bool haveStarted = false; for (std::set<llvm::BasicBlock*>::iterator i = preds.begin(), e = preds.end(); i != e; ++i) { TrueFalseMap::iterator it = m_map.find(*i); if (it == m_map.end()) { std::cerr << "Did not find condition information for predecessor " << (*i)->getName().str() << "!" << std::endl; exit(99999); } if (!haveStarted) { trueSet = it->second.first; falseSet = it->second.second; haveStarted = true; } else { // intersect trueSet = intersect(trueSet, it->second.first); falseSet = intersect(falseSet, it->second.second); } } if (preds.size() == 1) { llvm::BasicBlock *pred = *(preds.begin()); // branch condition! if (!m_onlyLoopConditions || m_lcbs.find(pred) != m_lcbs.end()) { llvm::TerminatorInst *termi = pred->getTerminator(); if (llvm::isa<llvm::BranchInst>(termi)) { llvm::BranchInst *br = llvm::cast<llvm::BranchInst>(termi); if (br->isConditional()) { if (br->getSuccessor(0) == bb) { // branch on true trueSet.insert(br->getCondition()); } else { // branch on false falseSet.insert(br->getCondition()); } } } } // assumes! if (!m_onlyLoopConditions) { for (llvm::BasicBlock::iterator insti = pred->begin(), inste = pred->end(); insti != inste; ++insti) { if (llvm::isa<llvm::CallInst>(insti)) { llvm::CallInst *ci = llvm::cast<llvm::CallInst>(insti); llvm::Function *calledFunction = ci->getCalledFunction(); if (calledFunction != NULL) { std::string functionName = calledFunction->getName().str(); if (functionName == "__kittel_assume") { llvm::CallSite callSite(ci); trueSet.insert(callSite.getArgument(0)); } } } } } } if (m_debug) { std::cout << "In " << bb->getName().str() << ":" << std::endl; std::cout << "TRUE: "; printSet(trueSet); std::cout << std::endl; std::cout << "FALSE: "; printSet(falseSet); std::cout << std::endl; if (++bbi != bbe) { std::cout << std::endl; } --bbi; } m_map.insert(std::make_pair(bb, std::make_pair(trueSet, falseSet))); } return false; }