From: Ted Kremenek Date: Thu, 6 Sep 2007 21:26:58 +0000 (+0000) Subject: LiveVariables: X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=27b07c5fb9926c9d69fa1d8239e96ad0b7a178f7;p=clang LiveVariables: - Finished 99% of analysis logic. Probably a few bugs. - Added querying functions to query liveness. - Added better pretty printing of liveness. - Added better bookkeeping of per-variable liveness information. - Added LiveVariablesAuditor interface, which allows "lazy" querying of intra-basic block liveness information. Driver: - Minor cleanups involved in dumping liveness information. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@41753 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/Analysis/LiveVariables.cpp b/Analysis/LiveVariables.cpp index 83c9b98044..6290651902 100644 --- a/Analysis/LiveVariables.cpp +++ b/Analysis/LiveVariables.cpp @@ -12,13 +12,15 @@ //===----------------------------------------------------------------------===// #include "clang/Analysis/LiveVariables.h" +#include "clang/Basic/SourceManager.h" #include "clang/AST/Expr.h" #include "clang/AST/CFG.h" #include "clang/AST/StmtVisitor.h" #include "clang/Lex/IdentifierTable.h" #include "llvm/ADT/SmallPtrSet.h" -#include +#include +#include using namespace clang; @@ -101,11 +103,18 @@ namespace { class LivenessTFuncs : public StmtVisitor { LiveVariables& L; llvm::BitVector Live; - llvm::BitVector Killed; + llvm::BitVector KilledAtLeastOnce; + Stmt* CurrentStmt; + const CFGBlock* CurrentBlock; + bool blockPreviouslyProcessed; + LiveVariablesAuditor* Auditor; public: - LivenessTFuncs(LiveVariables& l) : L(l) { + LivenessTFuncs(LiveVariables& l, LiveVariablesAuditor* A = NULL) + : L(l), CurrentStmt(NULL), CurrentBlock(NULL), + blockPreviouslyProcessed(false), Auditor(A) + { Live.resize(l.getNumDecls()); - Killed.resize(l.getNumDecls()); + KilledAtLeastOnce.resize(l.getNumDecls()); } void VisitStmt(Stmt* S); @@ -113,6 +122,8 @@ public: void VisitBinaryOperator(BinaryOperator* B); void VisitAssign(BinaryOperator* B); void VisitStmtExpr(StmtExpr* S); + void VisitDeclStmt(DeclStmt* DS); + void VisitUnaryOperator(UnaryOperator* U); unsigned getIdx(const Decl* D) { LiveVariables::VarInfoMap& V = L.getVarInfoMap(); @@ -122,19 +133,25 @@ public: } bool ProcessBlock(const CFGBlock* B); - llvm::BitVector* getLiveness(const CFGBlock* B); - + llvm::BitVector* getBlockEntryLiveness(const CFGBlock* B); + LiveVariables::VarInfo& KillVar(Decl* D); }; void LivenessTFuncs::VisitStmt(Stmt* S) { + if (Auditor) + Auditor->AuditStmt(S,L,Live); + // Evaluate the transfer functions for all subexpressions. Note that // each invocation of "Visit" will have a side-effect: "Liveness" and "Kills" - // will be updated. + // will be updated. for (Stmt::child_iterator I = S->child_begin(),E = S->child_end(); I != E;++I) Visit(*I); } void LivenessTFuncs::VisitDeclRefExpr(DeclRefExpr* DR) { + if (Auditor) + Auditor->AuditStmt(DR,L,Live); + // Register a use of the variable. Live.set(getIdx(DR->getDecl())); } @@ -151,33 +168,101 @@ void LivenessTFuncs::VisitBinaryOperator(BinaryOperator* B) { case BinaryOperator::Comma: // Do nothing. These operations are broken up into multiple // statements in the CFG. All these expressions do is return - // the value of their subexpressions, but these expressions will + // the value of their subexpressions, but these subexpressions will // be evalualated elsewhere in the CFG. break; // FIXME: handle '++' and '--' - default: + default: if (B->isAssignmentOp()) VisitAssign(B); - else Visit(B); + else VisitStmt(B); } } +void LivenessTFuncs::VisitUnaryOperator(UnaryOperator* U) { + switch (U->getOpcode()) { + case UnaryOperator::PostInc: + case UnaryOperator::PostDec: + case UnaryOperator::PreInc: + case UnaryOperator::PreDec: + case UnaryOperator::AddrOf: + // Walk through the subexpressions, blasting through ParenExprs until + // we either find a DeclRefExpr or some non-DeclRefExpr expression. + for (Stmt* S = U->getSubExpr() ; ; ) { + if (ParenExpr* P = dyn_cast(S)) { + S = P->getSubExpr(); + continue; + } + else if (DeclRefExpr* DR = dyn_cast(S)) { + // Treat the --/++/& operator as a kill. + LiveVariables::VarInfo& V = KillVar(DR->getDecl()); + + if (!blockPreviouslyProcessed) + V.AddKill(CurrentStmt,DR); + + VisitDeclRefExpr(DR); + } + else + Visit(S); + + break; + } + break; + + default: + VisitStmt(U->getSubExpr()); + break; + } +} + +LiveVariables::VarInfo& LivenessTFuncs::KillVar(Decl* D) { + LiveVariables::VarInfoMap::iterator I = L.getVarInfoMap().find(D); + + assert (I != L.getVarInfoMap().end() && + "Declaration not managed by variable map in LiveVariables"); + + // Mark the variable dead, and remove the current block from + // the set of blocks where the variable may be alive the entire time. + Live.reset(I->second.Idx); + I->second.V.AliveBlocks.reset(CurrentBlock->getBlockID()); + + return I->second.V; +} void LivenessTFuncs::VisitAssign(BinaryOperator* B) { + if (Auditor) + Auditor->AuditStmt(B,L,Live); + + // Check if we are assigning to a variable. Stmt* LHS = B->getLHS(); - + if (DeclRefExpr* DR = dyn_cast(LHS)) { - unsigned i = getIdx(DR->getDecl()); - Live.reset(i); - Killed.set(i); + LiveVariables::VarInfo& V = KillVar(DR->getDecl()); + + // We only need to register kills once, so we check if this block + // has been previously processed. + if (!blockPreviouslyProcessed) + V.AddKill(CurrentStmt,DR); } - else Visit(LHS); + else + Visit(LHS); Visit(B->getRHS()); } +void LivenessTFuncs::VisitDeclStmt(DeclStmt* DS) { + if (Auditor) + Auditor->AuditStmt(DS,L,Live); + + // Declarations effectively "kill" a variable since they cannot possibly + // be live before they are declared. Declarations, however, are not kills + // in the sense that the value is obliterated, so we do not register + // DeclStmts as a "kill site" for a variable. + for (Decl* D = DS->getDecl(); D != NULL ; D = D->getNextDeclarator()) + KillVar(D); +} -llvm::BitVector* LivenessTFuncs::getLiveness(const CFGBlock* B) { +llvm::BitVector* LivenessTFuncs::getBlockEntryLiveness(const CFGBlock* B) { LiveVariables::BlockLivenessMap& BMap = L.getLiveAtBlockEntryMap(); LiveVariables::BlockLivenessMap::iterator I = BMap.find(B); @@ -185,34 +270,55 @@ llvm::BitVector* LivenessTFuncs::getLiveness(const CFGBlock* B) { } bool LivenessTFuncs::ProcessBlock(const CFGBlock* B) { - // First: merge all predecessors. + + CurrentBlock = B; Live.reset(); - Killed.reset(); + KilledAtLeastOnce.reset(); + + // Check if this block has been previously processed. + LiveVariables::BlockLivenessMap& BMap = L.getLiveAtBlockEntryMap(); + LiveVariables::BlockLivenessMap::iterator BI = BMap.find(B); + + blockPreviouslyProcessed = BI != BMap.end(); + // Merge liveness information from all predecessors. for (CFGBlock::const_succ_iterator I=B->succ_begin(),E=B->succ_end();I!=E;++I) - if (llvm::BitVector* V = getLiveness(*I)) + if (llvm::BitVector* V = getBlockEntryLiveness(*I)) Live |= *V; + + if (Auditor) + Auditor->AuditBlockExit(B,L,Live); + + // Tentatively mark all variables alive at the end of the current block + // as being alive during the whole block. We then cull these out as + // we process the statements of this block. + for (LiveVariables::VarInfoMap::iterator + I=L.getVarInfoMap().begin(), E=L.getVarInfoMap().end(); I != E; ++I) + if (Live[I->second.Idx]) + I->second.V.AliveBlocks.set(B->getBlockID()); - // Second: march up the statements and process the transfer functions. + // March up the statements and process the transfer functions. for (CFGBlock::const_reverse_iterator I=B->rbegin(), E=B->rend(); I!=E; ++I) { - Visit(*I); + CurrentStmt = *I; + Visit(CurrentStmt); } - // Third: compare the computed "Live" values with what we already have - // for this block. + // Compare the computed "Live" values with what we already have + // for the entry to this block. bool hasChanged = false; + - LiveVariables::BlockLivenessMap& BMap = L.getLiveAtBlockEntryMap(); - LiveVariables::BlockLivenessMap::iterator I = BMap.find(B); - if (I == BMap.end()) { + if (!blockPreviouslyProcessed) { + // We have not previously calculated liveness information for this block. + // Lazily instantiate a bitvector, and copy the bits from Live. hasChanged = true; llvm::BitVector& V = BMap[B]; V.resize(L.getNumDecls()); - V |= Live; + V = Live; } - else if (I->second != Live) { + else if (BI->second != Live) { hasChanged = true; - I->second = Live; + BI->second = Live; } return hasChanged; @@ -224,7 +330,7 @@ bool LivenessTFuncs::ProcessBlock(const CFGBlock* B) { // runOnCFG - Method to run the actual liveness computation. // -void LiveVariables::runOnCFG(const CFG& cfg) { +void LiveVariables::runOnCFG(const CFG& cfg, LiveVariablesAuditor* Auditor) { // Scan a CFG for DeclRefStmts. For each one, create a VarInfo object. { RegisterDecls R(*this,cfg); @@ -236,7 +342,7 @@ void LiveVariables::runOnCFG(const CFG& cfg) { WorkList.enqueue(&cfg.getExit()); // Create the state for transfer functions. - LivenessTFuncs TF(*this); + LivenessTFuncs TF(*this,Auditor); // Process the worklist until it is empty. @@ -248,35 +354,86 @@ void LiveVariables::runOnCFG(const CFG& cfg) { WorkList.enqueue(*I); } - // Go through each block and reserve a bitvector. + // Go through each block and reserve a bitvector. This is needed if + // a block was never visited by the worklist algorithm. for (CFG::const_iterator I = cfg.begin(), E = cfg.end(); I != E; ++I) LiveAtBlockEntryMap[&(*I)].resize(NumDecls); } + +void LiveVariables::runOnBlock(const CFGBlock* B, LiveVariablesAuditor* Auditor) +{ + assert (NumDecls && "You must use runOnCFG before using runOnBlock."); + LivenessTFuncs TF(*this,Auditor); + TF.ProcessBlock(B); +} + +//===----------------------------------------------------------------------===// +// liveness queries +// + +bool LiveVariables::IsLive(const CFGBlock* B, const Decl* D) const { + BlockLivenessMap::const_iterator I = LiveAtBlockEntryMap.find(B); + assert (I != LiveAtBlockEntryMap.end()); + + VarInfoMap::const_iterator VI = VarInfos.find(D); + assert (VI != VarInfos.end()); + + return I->second[VI->second.Idx]; +} + +bool LiveVariables::KillsVar(const Stmt* S, const Decl* D) const { + VarInfoMap::const_iterator VI = VarInfos.find(D); + assert (VI != VarInfos.end()); + + for (VarInfo::KillsSet::const_iterator + I = VI->second.V.Kills.begin(), E = VI->second.V.Kills.end(); I!=E;++I) + if (I->first == S) + return true; + + return false; +} + +LiveVariables::VarInfo& LiveVariables::getVarInfo(const Decl* D) { + VarInfoMap::iterator VI = VarInfos.find(D); + assert (VI != VarInfos.end()); + return VI->second.V; +} + +const LiveVariables::VarInfo& LiveVariables::getVarInfo(const Decl* D) const { + return const_cast(this)->getVarInfo(D); +} + //===----------------------------------------------------------------------===// // printing liveness state for debugging // -void LiveVariables::printLiveness(const llvm::BitVector& V, - std::ostream& OS) const { +void LiveVariables::dumpLiveness(const llvm::BitVector& V, + SourceManager& SM) const { for (VarInfoMap::iterator I = VarInfos.begin(), E=VarInfos.end(); I!=E; ++I) { if (V[I->second.Idx]) { - OS << I->first->getIdentifier()->getName() << "\n"; + + SourceLocation PhysLoc = SM.getPhysicalLoc(I->first->getLocation()); + + fprintf(stderr, " %s <%s:%u:%u>\n", + I->first->getIdentifier()->getName(), + SM.getSourceName(PhysLoc), + SM.getLineNumber(PhysLoc), + SM.getColumnNumber(PhysLoc)); } } } -void LiveVariables::printBlockLiveness(std::ostream& OS) const { +void LiveVariables::dumpBlockLiveness(SourceManager& M) const { for (BlockLivenessMap::iterator I = LiveAtBlockEntryMap.begin(), E = LiveAtBlockEntryMap.end(); I != E; ++I) { - OS << "\n[ B" << I->first->getBlockID() - << " (live variables at block entry) ]\n"; - printLiveness(I->second, OS); - } -} -void LiveVariables::DumpBlockLiveness() const { - printBlockLiveness(std::cerr); + fprintf(stderr, + "\n[ B%d (live variables at block entry) ]\n", + I->first->getBlockID()); + + dumpLiveness(I->second,M); + } } \ No newline at end of file diff --git a/Driver/ASTStreamers.cpp b/Driver/ASTStreamers.cpp index a1494e4e1e..b5ea29328e 100644 --- a/Driver/ASTStreamers.cpp +++ b/Driver/ASTStreamers.cpp @@ -198,7 +198,7 @@ void clang::AnalyzeLiveVariables(Preprocessor &PP, unsigned MainFileID) if (CFG* C = CFG::buildCFG(FD->getBody())) { LiveVariables L; L.runOnCFG(*C); - L.DumpBlockLiveness(); + L.dumpBlockLiveness(PP.getSourceManager()); } else fprintf(stderr," Error processing CFG.\n"); diff --git a/Driver/clang.cpp b/Driver/clang.cpp index dde1c5737b..ed540aed06 100644 --- a/Driver/clang.cpp +++ b/Driver/clang.cpp @@ -92,9 +92,9 @@ ProgAction(llvm::cl::desc("Choose output type:"), llvm::cl::ZeroOrMore, clEnumValN(ParseCFGView, "view-cfg", "Run parser, then build and view CFGs with Graphviz."), clEnumValN(AnalysisLiveVariables, "dump-live-variables", - "Run parser and print results of live variable analysis."), + "Print results of live variable analysis."), clEnumValN(EmitLLVM, "emit-llvm", - "Build ASTs then convert to LLVM, emit .ll file"), + "Build ASTs then convert to LLVM, emit .ll file"), clEnumValEnd)); //===----------------------------------------------------------------------===// diff --git a/include/clang/Analysis/LiveVariables.h b/include/clang/Analysis/LiveVariables.h index 38b992f8bb..4f1e5c595a 100644 --- a/include/clang/Analysis/LiveVariables.h +++ b/include/clang/Analysis/LiveVariables.h @@ -16,16 +16,37 @@ #include "llvm/ADT/BitVector.h" #include "llvm/ADT/DenseMap.h" -#include #include namespace clang { class Stmt; - class DeclRefStmt; + class DeclRefExpr; class Decl; class CFG; class CFGBlock; + class SourceManager; + class LiveVariables; + +class LiveVariablesAuditor { +public: + 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; + + /// AuditBlockExit - A callback invoked right before invoking the liveness + /// transfer function on the given block. If the liveness information + /// has been previously calculated by running LiveVariables::runOnCFG, + /// 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; +}; class LiveVariables { public: @@ -39,11 +60,17 @@ public: /// Kills - List of statements which are the last use of a variable /// (kill it) in their basic block. The first pointer in the pair /// is the statement in the list of statements of a basic block where - /// this occurs, while the DeclRefStmt is the subexpression of this + /// this occurs, while the DeclRefExpr is the subexpression of this /// statement where the actual last reference takes place. - std::vector< std::pair > Kills; + typedef std::vector< std::pair > KillsSet; + KillsSet Kills; - void print(std::ostream& OS) const; + // AddKill - Adds a kill site to the list of places where a + // a variable is killed. + void AddKill(Stmt* S, DeclRefExpr* DR) { + Kills.push_back(std::make_pair(const_cast(S), + const_cast(DR))); + } }; struct VPair { @@ -59,7 +86,13 @@ public: LiveVariables() : NumDecls(0) {} /// runOnCFG - Computes live variable information for a given CFG. - void runOnCFG(const CFG& cfg); + void runOnCFG(const CFG& cfg, LiveVariablesAuditor* A = NULL); + + /// runOnBlock - Computes live variable information for a given block. + /// This should usually be invoked only after previously computing + /// live variable information using runOnCFG, and is intended to + /// only be used for auditing the liveness within a block. + void runOnBlock(const CFGBlock* B, LiveVariablesAuditor* A); /// KillsVar - Return true if the specified statement kills the /// specified variable. @@ -80,12 +113,11 @@ public: const VarInfoMap& getVarInfoMap() const { return VarInfos; } - // printLiveness - void printLiveness(const llvm::BitVector& V, std::ostream& OS) const; + // dumpLiveness + void dumpLiveness(const llvm::BitVector& V, SourceManager& M) const; - // printBlockLiveness - void printBlockLiveness(std::ostream& OS) const; - void DumpBlockLiveness() const; + // dumpBlockLiveness + void dumpBlockLiveness(SourceManager& M) const; // getLiveAtBlockEntryMap BlockLivenessMap& getLiveAtBlockEntryMap() { return LiveAtBlockEntryMap; }