static void maybe_print_statistics(llvm::Module *M, const char *prefix = nullptr) { if (!statistics) return; using namespace llvm; uint64_t inum, bnum, fnum, gnum; inum = bnum = fnum = gnum = 0; for (auto I = M->begin(), E = M->end(); I != E; ++I) { // don't count in declarations if (I->size() == 0) continue; ++fnum; for (const BasicBlock& B : *I) { ++bnum; inum += B.size(); } } for (auto I = M->global_begin(), E = M->global_end(); I != E; ++I) ++gnum; if (prefix) errs() << prefix; errs() << "Globals/Functions/Blocks/Instr.: " << gnum << " " << fnum << " " << bnum << " " << inum << "\n"; }
int verifyAndWriteModule() { if (!verifyModule()) { errs() << "ERR: Verifying module failed, the IR is not valid\n"; errs() << "INFO: Saving anyway so that you can check it\n"; return 1; } if (!writeModule()) { errs() << "Saving sliced module failed\n"; return 1; } // exit code return 0; }
bool writeModule() { // compose name if not given std::string fl; if (!options.outputFile.empty()) { fl = options.outputFile; } else { fl = options.inputFile; replace_suffix(fl, ".sliced"); } // open stream to write to std::ofstream ofs(fl); llvm::raw_os_ostream ostream(ofs); // write the module errs() << "INFO: saving sliced module to: " << fl.c_str() << "\n"; #if (LLVM_VERSION_MAJOR > 6) llvm::WriteBitcodeToFile(*M, ostream); #else llvm::WriteBitcodeToFile(M, ostream); #endif return true; }
void annotate(const std::set<LLVMNode *> *criteria = nullptr) { // compose name std::string fl(options.inputFile); fl.replace(fl.end() - 3, fl.end(), "-debug.ll"); // open stream to write to std::ofstream ofs(fl); llvm::raw_os_ostream outputstream(ofs); std::string module_comment = "; -- Generated by llvm-slicer --\n" "; * slicing criteria: '" + options.slicingCriteria + "'\n" + "; * forward slice: '" + std::to_string(options.forwardSlicing) + "'\n" + "; * remove slicing criteria: '" + std::to_string(options.removeSlicingCriteria) + "'\n" + "; * undefined are pure: '" + std::to_string(options.dgOptions.RDAOptions.undefinedArePure) + "'\n" + "; * pointer analysis: "; if (options.dgOptions.PTAOptions.analysisType == LLVMPointerAnalysisOptions::AnalysisType::fi) module_comment += "flow-insensitive\n"; else if (options.dgOptions.PTAOptions.analysisType == LLVMPointerAnalysisOptions::AnalysisType::fs) module_comment += "flow-sensitive\n"; else if (options.dgOptions.PTAOptions.analysisType == LLVMPointerAnalysisOptions::AnalysisType::inv) module_comment += "flow-sensitive with invalidate\n"; module_comment+= "; * PTA field sensitivity: "; if (options.dgOptions.PTAOptions.fieldSensitivity == Offset::UNKNOWN) module_comment += "full\n\n"; else module_comment += std::to_string(*options.dgOptions.PTAOptions.fieldSensitivity) + "\n\n"; errs() << "INFO: Saving IR with annotations to " << fl << "\n"; auto annot = new dg::debug::LLVMDGAssemblyAnnotationWriter(annotationOptions, dg->getPTA(), dg->getRDA(), criteria); annot->emitModuleComment(std::move(module_comment)); llvm::Module *M = dg->getModule(); M->print(outputstream, annot); delete annot; }
void dumpToDot(const char *suffix = nullptr) { // compose new name std::string fl(options.inputFile); if (suffix) replace_suffix(fl, suffix); else replace_suffix(fl, ".dot"); errs() << "INFO: Dumping DG to to " << fl << "\n"; if (bb_only) { debug::LLVMDGDumpBlocks dumper(dg, dump_opts, fl.c_str()); dumper.dump(); } else { debug::LLVMDG2Dot dumper(dg, dump_opts, fl.c_str()); dumper.dump(); } }
int main(int argc, char *argv[]) { setupStackTraceOnError(argc, argv); SlicerOptions options = parseSlicerOptions(argc, argv); // dump_dg_only implies dumg_dg if (dump_dg_only) dump_dg = true; llvm::LLVMContext context; std::unique_ptr<llvm::Module> M = parseModule(context, options); if (!M) { llvm::errs() << "Failed parsing '" << options.inputFile << "' file:\n"; return 1; } if (!M->getFunction(options.dgOptions.entryFunction)) { llvm::errs() << "The entry function not found: " << options.dgOptions.entryFunction << "\n"; return 1; } maybe_print_statistics(M.get(), "Statistics before "); // remove unused from module, we don't need that ModuleWriter writer(options, M.get()); writer.removeUnusedFromModule(); if (remove_unused_only) { errs() << "INFO: removed unused parts of module, exiting...\n"; maybe_print_statistics(M.get(), "Statistics after "); return writer.saveModule(should_verify_module); } /// --------------- // slice the code /// --------------- // we do not want to slice away any assumptions // about the code // FIXME: do this optional only for SV-COMP options.additionalSlicingCriteria = { "__VERIFIER_assume", "__VERIFIER_exit", "klee_assume", }; options.untouchedFunctions = { "__VERIFIER_assume", "__VERIFIER_exit" }; Slicer slicer(M.get(), options); if (!slicer.buildDG()) { errs() << "ERROR: Failed building DG\n"; return 1; } ModuleAnnotator annotator(options, &slicer.getDG(), parseAnnotationOptions(annotationOpts)); auto criteria_nodes = getSlicingCriteriaNodes(slicer.getDG(), options.slicingCriteria); if (criteria_nodes.empty()) { llvm::errs() << "Did not find slicing criteria: '" << options.slicingCriteria << "'\n"; if (annotator.shouldAnnotate()) { slicer.computeDependencies(); annotator.annotate(); } if (!slicer.createEmptyMain()) return 1; maybe_print_statistics(M.get(), "Statistics after "); return writer.cleanAndSaveModule(should_verify_module); } // mark nodes that are going to be in the slice if (!slicer.mark(criteria_nodes)) { llvm::errs() << "Finding dependent nodes failed\n"; return 1; } // print debugging llvm IR if user asked for it if (annotator.shouldAnnotate()) annotator.annotate(&criteria_nodes); DGDumper dumper(options, &slicer.getDG(), dump_bb_only); if (dump_dg) { dumper.dumpToDot(); if (dump_dg_only) return 0; } // slice the graph if (!slicer.slice()) { errs() << "ERROR: Slicing failed\n"; return 1; } if (dump_dg) { dumper.dumpToDot(".sliced.dot"); } // remove unused from module again, since slicing // could and probably did make some other parts unused maybe_print_statistics(M.get(), "Statistics after "); return writer.cleanAndSaveModule(should_verify_module); }
int main(int argc, char *argv[]) { llvm::Module *M; llvm::LLVMContext context; llvm::SMDiagnostic SMD; const char *module = nullptr; unsigned type = FLOW_SENSITIVE | FLOW_INSENSITIVE; // parse options for (int i = 1; i < argc; ++i) { // run only given points-to analysis if (strcmp(argv[i], "-pta") == 0) { if (strcmp(argv[i+1], "fs") == 0) type = FLOW_SENSITIVE; else if (strcmp(argv[i+1], "fi") == 0) type = FLOW_INSENSITIVE; else { errs() << "Unknown PTA type" << argv[i + 1] << "\n"; abort(); } /*} else if (strcmp(argv[i], "-v") == 0) { verbose = true;*/ } else { module = argv[i]; } } if (!module) { errs() << "Usage: % llvm-pta-compare [-pta fs|fi] IR_module\n"; return 1; } #if ((LLVM_VERSION_MAJOR == 3) && (LLVM_VERSION_MINOR <= 5)) M = llvm::ParseIRFile(module, SMD, context); #else auto _M = llvm::parseIRFile(module, SMD, context); // _M is unique pointer, we need to get Module * M = _M.get(); #endif if (!M) { llvm::errs() << "Failed parsing '" << module << "' file:\n"; SMD.print(argv[0], errs()); return 1; } dg::debug::TimeMeasure tm; LLVMPointerAnalysis *PTAfs = nullptr; LLVMPointerAnalysis *PTAfi = nullptr; if (type & FLOW_INSENSITIVE) { PTAfi = new LLVMPointerAnalysis(M); tm.start(); PTAfi->run<analysis::pta::PointerAnalysisFI>(); tm.stop(); tm.report("INFO: Points-to flow-insensitive analysis took"); } if (type & FLOW_SENSITIVE) { PTAfs = new LLVMPointerAnalysis(M); tm.start(); PTAfs->run<analysis::pta::PointerAnalysisFS>(); tm.stop(); tm.report("INFO: Points-to flow-sensitive analysis took"); } int ret = 0; if (type == (FLOW_SENSITIVE | FLOW_INSENSITIVE)) { ret = !verify_ptsets(M, PTAfi, PTAfs); if (ret == 0) llvm::errs() << "FS is a subset of FI, all OK\n"; } delete PTAfi; delete PTAfs; return ret; }