From: Ted Kremenek Date: Thu, 3 Jan 2008 22:12:28 +0000 (+0000) Subject: Initial checking of GRConstantPropagation.cpp, which implements a constant X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=7978452741ef13718695a6ac442e437c3c33ff95;p=clang Initial checking of GRConstantPropagation.cpp, which implements a constant propagation analysis via graph reachability. This analysis (which is incomplete) will be the basis for later developments on the core engine for path-sensitive analysis analysis. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@45552 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/Analysis/GRConstantPropagation.cpp b/Analysis/GRConstantPropagation.cpp new file mode 100644 index 0000000000..ead92d7f38 --- /dev/null +++ b/Analysis/GRConstantPropagation.cpp @@ -0,0 +1,325 @@ +//===-- GRConstantPropagation.cpp --------------------------------*- C++ -*-==// +// +// [ Constant Propagation via Graph Reachability ] +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This files defines a simple analysis that performs path-sensitive +// constant propagation within a function. An example use of this analysis +// is to perform simple checks for NULL dereferences. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/PathSensitive/SimulGraph.h" +#include "clang/AST/Expr.h" +#include "clang/AST/CFG.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/DataTypes.h" +#include "llvm/ADT/APInt.h" +#include "llvm/ADT/APFloat.h" +#include "llvm/ADT/ImmutableMap.h" + +using namespace clang; +using llvm::APInt; +using llvm::APFloat; +using llvm::dyn_cast; +using llvm::cast; + +//===----------------------------------------------------------------------===// +// ConstV - Represents a variant over APInt, APFloat, and const char +//===----------------------------------------------------------------------===// + +namespace { +class ConstV { + uintptr_t Data; +public: + enum VariantType { VTString = 0x0, VTObjCString = 0x1, + VTFloat = 0x2, VTInt = 0x3, + Flags = 0x3 }; + + ConstV(const StringLiteral* v) + : Data(reinterpret_cast(v) | VTString) {} + + ConstV(const ObjCStringLiteral* v) + : Data(reinterpret_cast(v) | VTObjCString) {} + + ConstV(llvm::APInt* v) + : Data(reinterpret_cast(v) | VTInt) {} + + ConstV(llvm::APFloat* v) + : Data(reinterpret_cast(v) | VTFloat) {} + + + inline void* getData() const { return (void*) (Data & ~Flags); } + inline VariantType getVT() const { return (VariantType) (Data & Flags); } + + inline void Profile(llvm::FoldingSetNodeID& ID) const { + ID.AddPointer(getData()); + } +}; +} // end anonymous namespace + +// Overload machinery for casting from ConstV to contained classes. + +namespace llvm { + +#define CV_OBJ_CAST(CLASS,FLAG)\ +template<> inline bool isa(const ConstV& V) {\ + return V.getVT() == FLAG;\ +}\ +\ +template <> struct cast_retty_impl {\ + typedef const CLASS* ret_type;\ +}; + +CV_OBJ_CAST(APInt,ConstV::VTInt) +CV_OBJ_CAST(APFloat,ConstV::VTFloat) +CV_OBJ_CAST(StringLiteral,ConstV::VTString) +CV_OBJ_CAST(ObjCStringLiteral,ConstV::VTObjCString) + +#undef CV_OBJ_CAST + +template <> struct simplify_type { + typedef void* SimpleType; + static SimpleType getSimplifiedValue(const ConstV &Val) { + return Val.getData(); + } +}; + +} // end llvm namespace + +//===----------------------------------------------------------------------===// +/// The checker. +//===----------------------------------------------------------------------===// + +namespace { + + +class GRCP { + + //==---------------------------------==// + // Type definitions. + //==---------------------------------==// + +public: + typedef llvm::ImmutableMap StateTy; + typedef SimulVertex VertexTy; + typedef SimulGraph GraphTy; + typedef llvm::SmallVector StmtStackTy; + typedef llvm::DenseMap ParentMapTy; + + /// DFSWorkList - A nested class that represents a worklist that processes + /// vertices in LIFO order. + class DFSWorkList { + llvm::SmallVector Vertices; + public: + bool hasWork() const { return !Vertices.empty(); } + + /// Enqueue - Add a vertex to the worklist. + void Enqueue(VertexTy* V) { Vertices.push_back(V); } + + /// Dequeue - Remove a vertex from the worklist. + VertexTy* Dequeue() { + assert (hasWork()); + VertexTy* V = Vertices.back(); + Vertices.pop_back(); + return V; + } + }; + + //==---------------------------------==// + // Data. + //==---------------------------------==// + +private: + CFG& cfg; + + /// StateFactory - States are simply maps from Decls to constants. This + /// object is a collection of all the states (immutable maps) that are + /// created by the analysis. This object owns the created maps. + StateTy::Factory StateFactory; + + /// Graph - The simulation graph. Each vertex is a (location,state) pair. + GraphTy Graph; + + /// ParentMap - A lazily populated map from a Stmt* to its parent Stmt*. + ParentMapTy ParentMap; + + /// StmtStack - A stack of statements/expressions that records the + /// statement hierarchy starting from the Stmt* of the last dequeued + /// vertex. Used to lazily populate ParentMap. + StmtStackTy StmtStack; + + /// WorkList - A set of queued vertices that need to be processed by the + /// worklist algorithm. + DFSWorkList WorkList; + + //==---------------------------------==// + // Edge processing. + //==---------------------------------==// + + void MakeVertex(const ProgramEdge& Loc, StateTy State, VertexTy* PredV) { + std::pair V = Graph.getVertex(Loc,State); + V.first->addPredecessor(PredV); + if (V.second) WorkList.Enqueue(V.first); + } + + void MakeVertex(const ProgramEdge& Loc, VertexTy* PredV) { + MakeVertex(Loc,PredV->getState(),PredV); + } + + void VisitBlkBlk(const BlkBlkEdge& E, VertexTy* PredV); + void VisitBlkStmt(const BlkStmtEdge& E, VertexTy* PredV); + void VisitStmtBlk(const StmtBlkEdge& E, VertexTy* PredV); + + void ProcessEOP(VertexTy* PredV); + void ProcessStmt(Stmt* S, VertexTy* PredV); + void ProcessTerminator(Stmt* Terminator ,VertexTy* PredV); + + //==---------------------------------==// + // Disable copying. + //==---------------------------------==// + + GRCP(const GRCP&); // Do not implement. + GRCP& operator=(const GRCP&); + + //==--------------------------------==// + // Public API. + //==--------------------------------==// + +public: + GRCP(CFG& c); + + /// getGraph - Returns the simulation graph. + const GraphTy& getGraph() const { return Graph; } + + /// ExecuteWorkList - Run the worklist algorithm for a maximum number of + /// steps. Returns true if there is still simulation state on the worklist. + bool ExecuteWorkList(unsigned Steps = 1000000); +}; +} // end anonymous namespace + + +//==--------------------------------------------------------==// +// Public API. +//==--------------------------------------------------------==// + +GRCP::GRCP(CFG& c) : cfg(c) { + // Get the entry block. Make sure that it has 1 (and only 1) successor. + CFGBlock* Entry = &c.getEntry(); + + assert (Entry->empty() && "Entry block must be empty."); + assert (Entry->succ_size() == 1 && "Entry block must have 1 successor."); + + // Get the first (and only) successor of Entry. + CFGBlock* Succ = *(Entry->succ_begin()); + + // Construct an edge representing the starting location in the function. + BlkBlkEdge StartLoc(Entry,Succ); + + // Get the vertex. Make it a root in the graph. + VertexTy* Root = Graph.getVertex(StartLoc,StateFactory.GetEmptyMap()).first; + Graph.addRoot(Root); + + // Enqueue the root so that it can be processed by the worklist. + WorkList.Enqueue(Root); +} + + +bool GRCP::ExecuteWorkList(unsigned Steps) { + while (Steps && WorkList.hasWork()) { + --Steps; + VertexTy* V = WorkList.Dequeue(); + + // Dispatch on the location type. + switch (V->getLocation().getKind()) { + case ProgramEdge::BlkBlk: + VisitBlkBlk(cast(V->getLocation()),V); + break; + + case ProgramEdge::BlkStmt: + VisitBlkStmt(cast(V->getLocation()),V); + break; + + case ProgramEdge::StmtBlk: + VisitStmtBlk(cast(V->getLocation()),V); + break; + + default: + assert (false && "Unsupported edge type."); + } + } + + return WorkList.hasWork(); +} + +//==--------------------------------------------------------==// +// Edge processing. +//==--------------------------------------------------------==// + +void GRCP::VisitBlkBlk(const BlkBlkEdge& E, GRCP::VertexTy* PredV) { + + const CFGBlock* Blk = E.Dst(); + + // FIXME: we will dispatch to a function that manipulates the state + // at the entrance to a block. + + if (!Blk->empty()) { + // If 'Blk' has at least one statement, create a BlkStmtEdge and create + // the appropriate vertex. This is the common case. + MakeVertex(BlkStmtEdge(Blk,Blk->front()), PredV->getState(), PredV); + } + else { + // Otherwise, create a vertex at the BlkStmtEdge right before the terminator + // (if any) is evaluated. + MakeVertex(StmtBlkEdge(NULL,Blk),PredV->getState(), PredV); + } +} + +void GRCP::VisitBlkStmt(const BlkStmtEdge& E, GRCP::VertexTy* PredV) { + + // Check if we are entering the EXIT block. + if (E.Src() == &cfg.getExit()) { + assert (cfg.getExit().size() == 0 && "EXIT block cannot contain Stmts."); + // Process the End-Of-Path. + ProcessEOP(PredV); + return; + } + + // Normal block. Process as usual. + if (Stmt* S = E.Dst()) + ProcessStmt(S,PredV); + else { + // No statement. Create an edge right before the terminator is evaluated. + MakeVertex(StmtBlkEdge(NULL,E.Src()), PredV->getState(), PredV); + } +} + +void GRCP::VisitStmtBlk(const StmtBlkEdge& E, GRCP::VertexTy* PredV) { + CFGBlock* Blk = E.Dst(); + + if (Stmt* Terminator = Blk->getTerminator()) + ProcessTerminator(Terminator,PredV); + else { + // No terminator. We should have only 1 successor. + assert (Blk->succ_size() == 1); + MakeVertex(BlkBlkEdge(Blk,*(Blk->succ_begin())), PredV); + } +} + +void GRCP::ProcessEOP(GRCP::VertexTy* PredV) { + assert(false && "Not implemented."); +} + +void GRCP::ProcessStmt(Stmt* S, GRCP::VertexTy* PredV) { + assert(false && "Not implemented."); +} + +void GRCP::ProcessTerminator(Stmt* Terminator,GRCP::VertexTy* PredV) { + assert(false && "Not implemented."); +}