From 6dc7b11d32d6bbd8f4e0dcf067ad59727ac56027 Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Thu, 6 Sep 2007 23:00:42 +0000 Subject: [PATCH] Added "Dead Stores", a flow-sensitive checker that checks for stores to variables that are no longer live. This analysis is built on top of CFGs and the LiveVariables analysis. changes to driver: added driver option "-check-dead-stores" to run the analysis llvm-svn: 41754 --- clang/Analysis/LiveVariables.cpp | 17 +++++++++++- clang/Driver/ASTStreamers.cpp | 27 +++++++++++++++++++ clang/Driver/ASTStreamers.h | 2 ++ clang/Driver/Makefile | 2 +- clang/Driver/clang.cpp | 10 +++++-- clang/clang.xcodeproj/project.pbxproj | 14 ++++++++++ clang/include/clang/Analysis/LiveVariables.h | 13 ++++++--- clang/include/clang/Basic/DiagnosticKinds.def | 3 +++ 8 files changed, 80 insertions(+), 8 deletions(-) diff --git a/clang/Analysis/LiveVariables.cpp b/clang/Analysis/LiveVariables.cpp index 629065190223..b5248fe99e34 100644 --- a/clang/Analysis/LiveVariables.cpp +++ b/clang/Analysis/LiveVariables.cpp @@ -372,7 +372,7 @@ void LiveVariables::runOnBlock(const CFGBlock* B, LiveVariablesAuditor* Auditor) // liveness queries // -bool LiveVariables::IsLive(const CFGBlock* B, const Decl* D) const { +bool LiveVariables::isLive(const CFGBlock* B, const Decl* D) const { BlockLivenessMap::const_iterator I = LiveAtBlockEntryMap.find(B); assert (I != LiveAtBlockEntryMap.end()); @@ -382,6 +382,12 @@ bool LiveVariables::IsLive(const CFGBlock* B, const Decl* D) const { return I->second[VI->second.Idx]; } +bool LiveVariables::isLive(llvm::BitVector& Live, const Decl* D) const { + VarInfoMap::const_iterator VI = VarInfos.find(D); + assert (VI != VarInfos.end()); + return Live[VI->second.Idx]; +} + bool LiveVariables::KillsVar(const Stmt* S, const Decl* D) const { VarInfoMap::const_iterator VI = VarInfos.find(D); assert (VI != VarInfos.end()); @@ -404,6 +410,15 @@ const LiveVariables::VarInfo& LiveVariables::getVarInfo(const Decl* D) const { return const_cast(this)->getVarInfo(D); } +//===----------------------------------------------------------------------===// +// Defaults for LiveVariablesAuditor + +void LiveVariablesAuditor::AuditStmt(Stmt* S, LiveVariables& L, + llvm::BitVector& V) {} + +void LiveVariablesAuditor::AuditBlockExit(const CFGBlock* B, LiveVariables& L, + llvm::BitVector& V) {} + //===----------------------------------------------------------------------===// // printing liveness state for debugging // diff --git a/clang/Driver/ASTStreamers.cpp b/clang/Driver/ASTStreamers.cpp index b5ea29328e22..95d39fa35287 100644 --- a/clang/Driver/ASTStreamers.cpp +++ b/clang/Driver/ASTStreamers.cpp @@ -15,6 +15,7 @@ #include "clang/AST/AST.h" #include "clang/AST/CFG.h" #include "clang/Analysis/LiveVariables.h" +#include "clang/Analysis/LocalCheckers.h" #include "clang/Lex/Preprocessor.h" #include "clang/Sema/ASTStreamer.h" using namespace clang; @@ -209,4 +210,30 @@ void clang::AnalyzeLiveVariables(Preprocessor &PP, unsigned MainFileID) ASTStreamer_Terminate(Streamer); } +void clang::RunDeadStoresCheck(Preprocessor &PP, unsigned MainFileID,bool Stats) +{ + ASTContext Context(PP.getTargetInfo(), PP.getIdentifierTable()); + ASTStreamerTy *Streamer = ASTStreamer_Init(PP, Context, MainFileID); + + while (Decl *D = ASTStreamer_ReadTopLevelDecl(Streamer)) { + if (FunctionDecl *FD = dyn_cast(D)) { + if (FD->getBody()) { + if (CFG* C = CFG::buildCFG(FD->getBody())) { + clang::CheckDeadStores(*C,PP); + } + else + fprintf(stderr," Error processing CFG.\n"); + } + } + } + + if (Stats) { + fprintf(stderr, "\nSTATISTICS:\n"); + ASTStreamer_PrintStats(Streamer); + Context.PrintStats(); + } + + ASTStreamer_Terminate(Streamer); +} + diff --git a/clang/Driver/ASTStreamers.h b/clang/Driver/ASTStreamers.h index 34ce8647b0e1..f568e8e866e1 100644 --- a/clang/Driver/ASTStreamers.h +++ b/clang/Driver/ASTStreamers.h @@ -29,6 +29,8 @@ void DumpCFGs(Preprocessor &PP, unsigned MainFileID, void AnalyzeLiveVariables(Preprocessor &PP, unsigned MainFileID); +void RunDeadStoresCheck(Preprocessor &PP, unsigned MainFileID, bool Stats); + } // end clang namespace #endif diff --git a/clang/Driver/Makefile b/clang/Driver/Makefile index eca316e2042c..3577a2bca14d 100644 --- a/clang/Driver/Makefile +++ b/clang/Driver/Makefile @@ -3,6 +3,6 @@ CPPFLAGS += -I$(PROJ_SRC_DIR)/../include CXXFLAGS = -fno-rtti TOOLNAME = clang -USEDLIBS = clangCodeGen.a clangSEMA.a clangAnalysis.a clangAST.a clangParse.a clangLex.a clangBasic.a LLVMCore.a LLVMSupport.a LLVMSystem.a +USEDLIBS = clangCodeGen.a clangAnalysis.a clangSEMA.a clangAST.a clangParse.a clangLex.a clangBasic.a LLVMCore.a LLVMSupport.a LLVMSystem.a include $(LEVEL)/Makefile.common diff --git a/clang/Driver/clang.cpp b/clang/Driver/clang.cpp index ed540aed06e9..79a09f17611c 100644 --- a/clang/Driver/clang.cpp +++ b/clang/Driver/clang.cpp @@ -53,8 +53,9 @@ enum ProgActions { ParseASTCheck, // Parse ASTs and check diagnostics. ParseAST, // Parse ASTs. ParseCFGDump, // Parse ASTS. Build CFGs. Print CFGs. - ParseCFGView, // Parse ASTS. Build CFGs. View CFGs (Graphviz). + ParseCFGView, // Parse ASTS. Build CFGs. View CFGs. AnalysisLiveVariables, // Print results of live-variable analysis. + WarnDeadStores, // Run DeadStores checker on parsed ASTs. ParsePrintCallbacks, // Parse and print each callback. ParseSyntaxOnly, // Parse and perform semantic analysis. ParseNoop, // Parse with noop callbacks. @@ -93,6 +94,8 @@ ProgAction(llvm::cl::desc("Choose output type:"), llvm::cl::ZeroOrMore, "Run parser, then build and view CFGs with Graphviz."), clEnumValN(AnalysisLiveVariables, "dump-live-variables", "Print results of live variable analysis."), + clEnumValN(WarnDeadStores, "check-dead-stores", + "Flag warnings of stores to dead variables."), clEnumValN(EmitLLVM, "emit-llvm", "Build ASTs then convert to LLVM, emit .ll file"), clEnumValEnd)); @@ -851,7 +854,10 @@ static void ProcessInputFile(Preprocessor &PP, unsigned MainFileID, break; case AnalysisLiveVariables: AnalyzeLiveVariables(PP, MainFileID); - break; + break; + case WarnDeadStores: + RunDeadStoresCheck(PP, MainFileID, Stats); + break; case EmitLLVM: EmitLLVMFromASTs(PP, MainFileID, Stats); break; diff --git a/clang/clang.xcodeproj/project.pbxproj b/clang/clang.xcodeproj/project.pbxproj index 21ec8d128e85..0ca23958c7d4 100644 --- a/clang/clang.xcodeproj/project.pbxproj +++ b/clang/clang.xcodeproj/project.pbxproj @@ -13,6 +13,7 @@ 1A869AA80BA21ABA008DA07A /* LiteralSupport.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1A869AA70BA21ABA008DA07A /* LiteralSupport.cpp */; }; 1ABC36940C7A4BDC006DB0AB /* CGBuiltin.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1ABC36930C7A4BDC006DB0AB /* CGBuiltin.cpp */; }; 35260CA50C7F75C000D66CE9 /* ExprCXX.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 35260CA40C7F75C000D66CE9 /* ExprCXX.cpp */; }; + 355CF6840C90A8D400A08AA3 /* DeadStores.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 355CF6830C90A8D400A08AA3 /* DeadStores.cpp */; }; 356EF9B50C8F7DDF006650F5 /* LiveVariables.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 356EF9B40C8F7DDF006650F5 /* LiveVariables.cpp */; }; 84D9A8880C1A57E100AC7ABC /* AttributeList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84D9A8870C1A57E100AC7ABC /* AttributeList.cpp */; }; 84D9A88C0C1A581300AC7ABC /* AttributeList.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 84D9A88B0C1A581300AC7ABC /* AttributeList.h */; }; @@ -210,6 +211,8 @@ 1ABC36930C7A4BDC006DB0AB /* CGBuiltin.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CGBuiltin.cpp; path = CodeGen/CGBuiltin.cpp; sourceTree = ""; }; 35260CA40C7F75C000D66CE9 /* ExprCXX.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ExprCXX.cpp; path = AST/ExprCXX.cpp; sourceTree = ""; }; 3547129D0C88881300B3E1D5 /* PrettyPrinter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PrettyPrinter.h; path = clang/AST/PrettyPrinter.h; sourceTree = ""; }; + 355CF6820C90A8B600A08AA3 /* LocalCheckers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LocalCheckers.h; path = clang/Analysis/LocalCheckers.h; sourceTree = ""; }; + 355CF6830C90A8D400A08AA3 /* DeadStores.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DeadStores.cpp; path = Analysis/DeadStores.cpp; sourceTree = ""; }; 356EF9B20C8F7DBA006650F5 /* LiveVariables.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LiveVariables.h; path = clang/Analysis/LiveVariables.h; sourceTree = ""; }; 356EF9B40C8F7DDF006650F5 /* LiveVariables.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = LiveVariables.cpp; path = Analysis/LiveVariables.cpp; sourceTree = ""; }; 84D9A8870C1A57E100AC7ABC /* AttributeList.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = AttributeList.cpp; path = Parse/AttributeList.cpp; sourceTree = ""; }; @@ -377,9 +380,18 @@ name = Products; sourceTree = ""; }; + 355CF6850C90A8D600A08AA3 /* LocalCheckers */ = { + isa = PBXGroup; + children = ( + 355CF6830C90A8D400A08AA3 /* DeadStores.cpp */, + ); + name = LocalCheckers; + sourceTree = ""; + }; 356EF9AF0C8F7DA4006650F5 /* Analysis */ = { isa = PBXGroup; children = ( + 355CF6820C90A8B600A08AA3 /* LocalCheckers.h */, 356EF9B20C8F7DBA006650F5 /* LiveVariables.h */, ); name = Analysis; @@ -389,6 +401,7 @@ isa = PBXGroup; children = ( 356EF9B40C8F7DDF006650F5 /* LiveVariables.cpp */, + 355CF6850C90A8D600A08AA3 /* LocalCheckers */, ); name = Analysis; sourceTree = ""; @@ -741,6 +754,7 @@ 35260CA50C7F75C000D66CE9 /* ExprCXX.cpp in Sources */, DE2255FC0C8004E600D370A5 /* ParseDeclCXX.cpp in Sources */, 356EF9B50C8F7DDF006650F5 /* LiveVariables.cpp in Sources */, + 355CF6840C90A8D400A08AA3 /* DeadStores.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/clang/include/clang/Analysis/LiveVariables.h b/clang/include/clang/Analysis/LiveVariables.h index 4f1e5c595a75..c63dc7c0d248 100644 --- a/clang/include/clang/Analysis/LiveVariables.h +++ b/clang/include/clang/Analysis/LiveVariables.h @@ -30,14 +30,14 @@ namespace clang { class LiveVariablesAuditor { public: - virtual ~LiveVariablesAuditor(); + virtual ~LiveVariablesAuditor() {} /// AuditStmt - A callback invoked right before invoking the liveness /// transfer function on the given statement. If the liveness information /// has been previously calculated by running LiveVariables::runOnCFG, /// then V contains the liveness information after the execution of /// the given statement. - virtual void AuditStmt(Stmt* S, LiveVariables& L, llvm::BitVector& V) = 0; + virtual void AuditStmt(Stmt* S, LiveVariables& L, llvm::BitVector& V); /// AuditBlockExit - A callback invoked right before invoking the liveness /// transfer function on the given block. If the liveness information @@ -45,7 +45,7 @@ public: /// then V contains the liveness information after the execution of /// the given block. virtual void AuditBlockExit(const CFGBlock* B, LiveVariables& L, - llvm::BitVector& V) = 0; + llvm::BitVector& V); }; class LiveVariables { @@ -100,7 +100,12 @@ public: /// IsLive - Return true if a variable is live at beginning of a specified // block. - bool IsLive(const CFGBlock* B, const Decl* D) const; + bool isLive(const CFGBlock* B, const Decl* D) const; + + /// IsLive - Return true if a variable is live according to the provided + /// livness bitvector. This is typically used by classes that subclass + /// LiveVariablesAuditor. + bool isLive(llvm::BitVector& V, const Decl* D) const; /// getVarInfo - Return the liveness information associated with a given /// variable. diff --git a/clang/include/clang/Basic/DiagnosticKinds.def b/clang/include/clang/Basic/DiagnosticKinds.def index 5009fa724eed..a3249d742b0c 100644 --- a/clang/include/clang/Basic/DiagnosticKinds.def +++ b/clang/include/clang/Basic/DiagnosticKinds.def @@ -748,6 +748,9 @@ DIAG(warn_ret_stack_ref, WARNING, DIAG(warn_floatingpoint_eq, WARNING, "comparing floating point with == or != is unsafe") +// CHECK: stores to variables that are no longer live (dead stores) +DIAG(warn_dead_store, WARNING, "value stored to variable is never used") + // CFString checking DIAG(err_cfstring_literal_not_string_constant, ERROR, "CFString literal is not a string constant") -- GitLab