[cfe-commits] r161552 - in /cfe/trunk: include/clang/Frontend/Analyses.def include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h lib/StaticAnalyzer/Core/CallEvent.cpp lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp test/Analysis/inlining/DynDispatchBifurcate.m

Anna Zaks ganna at apple.com
Wed Aug 8 17:21:34 PDT 2012


Author: zaks
Date: Wed Aug  8 19:21:33 2012
New Revision: 161552

URL: http://llvm.org/viewvc/llvm-project?rev=161552&view=rev
Log:
[analyzer] Bifurcate the path with dynamic dispatch.

This is an initial (unoptimized) version. We split the path when
inlining ObjC instance methods. On one branch we always assume that the
type information for the given memory region is precise. On the other we
assume that we don't have the exact type info. It is important to check
since the class could be subclassed and the method can be overridden. If
we always inline we can loose coverage.

Had to refactor some of the call eval functions.

Added:
    cfe/trunk/test/Analysis/inlining/DynDispatchBifurcate.m
Modified:
    cfe/trunk/include/clang/Frontend/Analyses.def
    cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
    cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
    cfe/trunk/lib/StaticAnalyzer/Core/CallEvent.cpp
    cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp

Modified: cfe/trunk/include/clang/Frontend/Analyses.def
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Frontend/Analyses.def?rev=161552&r1=161551&r2=161552&view=diff
==============================================================================
--- cfe/trunk/include/clang/Frontend/Analyses.def (original)
+++ cfe/trunk/include/clang/Frontend/Analyses.def Wed Aug  8 19:21:33 2012
@@ -49,6 +49,7 @@
 ANALYSIS_IPA(None, "none", "Perform only intra-procedural analysis")
 ANALYSIS_IPA(Inlining, "inlining", "Inline callees when their definitions are available")
 ANALYSIS_IPA(DynamicDispatch, "dynamic", "Experimental: Enable inlining of dynamically dispatched methods")
+ANALYSIS_IPA(DynamicDispatchBifurcate, "dynamic-bifurcate", "Experimental: Enable inlining of dynamically dispatched methods, bifurcate paths when exact type info is unavailable")
 
 #ifndef ANALYSIS_INLINING_MODE
 #define ANALYSIS_INLINING_MODE(NAME, CMDFLAG, DESC)

Modified: cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h?rev=161552&r1=161551&r2=161552&view=diff
==============================================================================
--- cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h (original)
+++ cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h Wed Aug  8 19:21:33 2012
@@ -68,6 +68,14 @@
   }
 };
 
+struct RuntimeDefinition {
+  const Decl *Decl;
+  const MemRegion *Reg;
+  RuntimeDefinition(): Decl(0), Reg(0) {}
+  RuntimeDefinition(const class Decl *D): Decl(D), Reg(0) {}
+  RuntimeDefinition(const class Decl *D, const MemRegion *R): Decl(D), Reg(R){}
+};
+
 /// \brief Represents an abstract call to a function or method along a
 /// particular path.
 ///
@@ -161,7 +169,7 @@
   /// \brief Returns the definition of the function or method that will be
   /// called. Returns NULL if the definition cannot be found; ex: due to
   /// dynamic dispatch in ObjC methods.
-  virtual const Decl *getRuntimeDefinition() const = 0;
+  virtual RuntimeDefinition getRuntimeDefinition() const = 0;
 
   /// \brief Returns the expression whose value will be the result of this call.
   /// May be null.
@@ -336,12 +344,12 @@
     return cast<FunctionDecl>(CallEvent::getDecl());
   }
 
-  virtual const Decl *getRuntimeDefinition() const {
+  virtual RuntimeDefinition getRuntimeDefinition() const {
     const FunctionDecl *FD = getDecl();
     // Note that hasBody() will fill FD with the definition FunctionDecl.
     if (FD && FD->hasBody(FD))
-      return FD;
-    return 0;
+      return RuntimeDefinition(FD, 0);
+    return RuntimeDefinition();
   }
 
   virtual bool argumentsMayEscape() const;
@@ -432,7 +440,7 @@
     return getSVal(Base);
   }
 
-  virtual const Decl *getRuntimeDefinition() const;
+  virtual RuntimeDefinition getRuntimeDefinition() const;
 
   virtual void getInitialStackFrameContents(const StackFrameContext *CalleeCtx,
                                             BindingsTy &Bindings) const;
@@ -545,8 +553,8 @@
     return BR->getDecl();
   }
 
-  virtual const Decl *getRuntimeDefinition() const {
-    return getBlockDecl();
+  virtual RuntimeDefinition getRuntimeDefinition() const {
+    return RuntimeDefinition(getBlockDecl(), 0);
   }
 
   virtual void getInitialStackFrameContents(const StackFrameContext *CalleeCtx,
@@ -650,7 +658,7 @@
   /// \brief Returns the value of the implicit 'this' object.
   virtual SVal getCXXThisVal() const;
 
-  virtual const Decl *getRuntimeDefinition() const;
+  virtual RuntimeDefinition getRuntimeDefinition() const;
 
   virtual void getInitialStackFrameContents(const StackFrameContext *CalleeCtx,
                                             BindingsTy &Bindings) const;
@@ -786,7 +794,7 @@
     llvm_unreachable("Unknown message kind");
   }
 
-  virtual const Decl *getRuntimeDefinition() const;
+  virtual RuntimeDefinition getRuntimeDefinition() const;
 
   virtual void getInitialStackFrameContents(const StackFrameContext *CalleeCtx,
                                             BindingsTy &Bindings) const;

Modified: cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h?rev=161552&r1=161551&r2=161552&view=diff
==============================================================================
--- cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h (original)
+++ cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h Wed Aug  8 19:21:33 2012
@@ -489,7 +489,19 @@
                     const ProgramPointTag *tag, bool isLoad);
 
   bool shouldInlineDecl(const Decl *D, ExplodedNode *Pred);
-  bool inlineCall(const CallEvent &Call, ExplodedNode *Pred);
+  bool inlineCall(const CallEvent &Call, const Decl *D, NodeBuilder &Bldr,
+                  ExplodedNode *Pred, ProgramStateRef State);
+
+  /// \brief Conservatively evaluate call by invalidating regions and binding
+  /// a conjured return value.
+  void conservativeEvalCall(const CallEvent &Call, NodeBuilder &Bldr,
+                            ExplodedNode *Pred, ProgramStateRef State);
+
+  /// \brief Either inline or process the call conservatively (or both), based
+  /// on DynamicDispatchBifurcation data.
+  void BifurcateCall(const MemRegion *BifurReg,
+                     const CallEvent &Call, const Decl *D, NodeBuilder &Bldr,
+                     ExplodedNode *Pred);
 
   bool replayWithoutInlining(ExplodedNode *P, const LocationContext *CalleeLC);
 };

Modified: cfe/trunk/lib/StaticAnalyzer/Core/CallEvent.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/CallEvent.cpp?rev=161552&r1=161551&r2=161552&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/CallEvent.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/CallEvent.cpp Wed Aug  8 19:21:33 2012
@@ -382,14 +382,14 @@
 }
 
 
-const Decl *CXXInstanceCall::getRuntimeDefinition() const {
-  const Decl *D = SimpleCall::getRuntimeDefinition();
+RuntimeDefinition CXXInstanceCall::getRuntimeDefinition() const {
+  const Decl *D = SimpleCall::getRuntimeDefinition().Decl;
   if (!D)
-    return 0;
+    return RuntimeDefinition();
 
   const CXXMethodDecl *MD = cast<CXXMethodDecl>(D);
   if (!MD->isVirtual())
-    return MD;
+    return RuntimeDefinition(MD, 0);
 
   // If the method is virtual, see if we can find the actual implementation
   // based on context-sensitivity.
@@ -398,9 +398,9 @@
   // because a /partially/ constructed object can be referred to through a
   // base pointer. We'll eventually want to use DynamicTypeInfo here.
   if (const CXXMethodDecl *Devirtualized = devirtualize(MD, getCXXThisVal()))
-    return Devirtualized;
+    return RuntimeDefinition(Devirtualized, 0);
 
-  return 0;
+  return RuntimeDefinition();
 }
 
 void CXXInstanceCall::getInitialStackFrameContents(
@@ -512,14 +512,14 @@
     Regions.push_back(static_cast<const MemRegion *>(Data));
 }
 
-const Decl *CXXDestructorCall::getRuntimeDefinition() const {
-  const Decl *D = AnyFunctionCall::getRuntimeDefinition();
+RuntimeDefinition CXXDestructorCall::getRuntimeDefinition() const {
+  const Decl *D = AnyFunctionCall::getRuntimeDefinition().Decl;
   if (!D)
-    return 0;
+    return RuntimeDefinition();
 
   const CXXMethodDecl *MD = cast<CXXMethodDecl>(D);
   if (!MD->isVirtual())
-    return MD;
+    return RuntimeDefinition(MD, 0);
 
   // If the method is virtual, see if we can find the actual implementation
   // based on context-sensitivity.
@@ -528,9 +528,9 @@
   // because a /partially/ constructed object can be referred to through a
   // base pointer. We'll eventually want to use DynamicTypeInfo here.
   if (const CXXMethodDecl *Devirtualized = devirtualize(MD, getCXXThisVal()))
-    return Devirtualized;
+    return RuntimeDefinition(Devirtualized, 0);
 
-  return 0;
+  return RuntimeDefinition();
 }
 
 void CXXDestructorCall::getInitialStackFrameContents(
@@ -659,7 +659,7 @@
   return static_cast<ObjCMessageKind>(Info.getInt());
 }
 
-const Decl *ObjCMethodCall::getRuntimeDefinition() const {
+RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const {
   const ObjCMessageExpr *E = getOriginExpr();
   assert(E);
   Selector Sel = E->getSelector();
@@ -669,12 +669,16 @@
     // Find the the receiver type.
     const ObjCObjectPointerType *ReceiverT = 0;
     QualType SupersType = E->getSuperType();
+    const MemRegion *Receiver = 0;
+
     if (!SupersType.isNull()) {
+      // Super always means the type of immediate predecessor to the method
+      // where the call occurs.
       ReceiverT = cast<ObjCObjectPointerType>(SupersType);
     } else {
-      const MemRegion *Receiver = getReceiverSVal().getAsRegion();
+      Receiver = getReceiverSVal().getAsRegion();
       if (!Receiver)
-        return 0;
+        return RuntimeDefinition();
 
       QualType DynType = getState()->getDynamicTypeInfo(Receiver).getType();
       ReceiverT = dyn_cast<ObjCObjectPointerType>(DynType);
@@ -683,7 +687,7 @@
     // Lookup the method implementation.
     if (ReceiverT)
       if (ObjCInterfaceDecl *IDecl = ReceiverT->getInterfaceDecl())
-        return IDecl->lookupPrivateMethod(Sel);
+        return RuntimeDefinition(IDecl->lookupPrivateMethod(Sel), Receiver);
 
   } else {
     // This is a class method.
@@ -691,11 +695,11 @@
     // class name.
     if (ObjCInterfaceDecl *IDecl = E->getReceiverInterface()) {
       // Find/Return the method implementation.
-      return IDecl->lookupPrivateClassMethod(Sel);
+      return RuntimeDefinition(IDecl->lookupPrivateClassMethod(Sel), 0);
     }
   }
 
-  return 0;
+  return RuntimeDefinition();
 }
 
 void ObjCMethodCall::getInitialStackFrameContents(

Modified: cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp?rev=161552&r1=161551&r2=161552&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp Wed Aug  8 19:21:33 2012
@@ -11,12 +11,15 @@
 //
 //===----------------------------------------------------------------------===//
 
+#define DEBUG_TYPE "ExprEngine"
+
 #include "clang/Analysis/Analyses/LiveVariables.h"
 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
 #include "clang/AST/DeclCXX.h"
 #include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/Statistic.h"
 #include "llvm/Support/SaveAndRestore.h"
 
 #define CXX_INLINING_ENABLED 1
@@ -24,6 +27,9 @@
 using namespace clang;
 using namespace ento;
 
+STATISTIC(NumOfDynamicDispatchPathSplits,
+  "The # of times we split the path due to imprecise dynamic dispatch info");
+
 void ExprEngine::processCallEnter(CallEnter CE, ExplodedNode *Pred) {
   // Get the entry block in the CFG of the callee.
   const StackFrameContext *calleeCtx = CE.getCalleeContext();
@@ -273,14 +279,27 @@
   return true;
 }
 
-bool ExprEngine::inlineCall(const CallEvent &Call,
-                            ExplodedNode *Pred) {
-  if (!getAnalysisManager().shouldInlineCall())
-    return false;
-
-  const Decl *D = Call.getRuntimeDefinition();
-  if (!D)
-    return false;
+/// The GDM component containing the dynamic dispatch bifurcation info. When
+/// the exact type of the receiver is not known, we want to explore both paths -
+/// one on which we do inline it and the other one on which we don't. This is
+/// done to ensure we do not drop coverage.
+/// This is the map from the receiver region to a bool, specifying either we
+/// consider this region's information precise or not along the given path.
+namespace clang {
+namespace ento {
+struct DynamicDispatchBifurcationMap {};
+typedef llvm::ImmutableMap<const MemRegion*,
+                           int> DynamicDispatchBifur;
+template<> struct ProgramStateTrait<DynamicDispatchBifurcationMap>
+    :  public ProgramStatePartialTrait<DynamicDispatchBifur> {
+  static void *GDMIndex() { static int index; return &index; }
+};
+}}
+
+bool ExprEngine::inlineCall(const CallEvent &Call, const Decl *D,
+                            NodeBuilder &Bldr, ExplodedNode *Pred,
+                            ProgramStateRef State) {
+  assert(D);
 
   const LocationContext *CurLC = Pred->getLocationContext();
   const StackFrameContext *CallerSFC = CurLC->getCurrentStackFrame();
@@ -359,7 +378,8 @@
     break;
   }
   case CE_ObjCMessage:
-    if (getAnalysisManager().IPAMode != DynamicDispatch)
+    if (!(getAnalysisManager().IPAMode == DynamicDispatch ||
+          getAnalysisManager().IPAMode == DynamicDispatchBifurcate))
       return false;
     break;
   }
@@ -384,7 +404,7 @@
 
   // Construct a new state which contains the mapping from actual to
   // formal arguments.
-  ProgramStateRef State = Pred->getState()->enterStackFrame(Call, CalleeSFC);
+  State = State->enterStackFrame(Call, CalleeSFC);
 
   bool isNew;
   if (ExplodedNode *N = G.getNode(Loc, State, false, &isNew)) {
@@ -392,6 +412,11 @@
     if (isNew)
       Engine.getWorkList()->enqueue(N);
   }
+
+  // If we decided to inline the call, the successor has been manually
+  // added onto the work list so remove it from the node builder.
+  Bldr.takeNodes(Pred);
+
   return true;
 }
 
@@ -491,6 +516,18 @@
   return State->BindExpr(E, LCtx, R);
 }
 
+// Conservatively evaluate call by invalidating regions and binding
+// a conjured return value.
+void ExprEngine::conservativeEvalCall(const CallEvent &Call, NodeBuilder &Bldr,
+                                      ExplodedNode *Pred, ProgramStateRef State) {
+  unsigned Count = currentBuilderContext->getCurrentBlockCount();
+  State = Call.invalidateRegions(Count, State);
+  State = bindReturnValue(Call, Pred->getLocationContext(), State);
+
+  // And make the result node.
+  Bldr.generateNode(Call.getProgramPoint(), State, Pred);
+}
+
 void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred,
                                  const CallEvent &CallTemplate) {
   // Make sure we have the most recent state attached to the call.
@@ -506,23 +543,67 @@
   if (InlinedFailedState) {
     // If we already tried once and failed, make sure we don't retry later.
     State = InlinedFailedState;
-  } else if (inlineCall(*Call, Pred)) {
-    // If we decided to inline the call, the successor has been manually
-    // added onto the work list and we should not perform our generic
-    // call-handling steps.
-    Bldr.takeNodes(Pred);
-    return;
+  } else if (getAnalysisManager().shouldInlineCall()) {
+    RuntimeDefinition RD = Call->getRuntimeDefinition();
+    const Decl *D = RD.Decl;
+    if (D) {
+      // Explore with and without inlining the call.
+      const MemRegion *BifurReg = RD.Reg;
+      if (BifurReg &&
+          getAnalysisManager().IPAMode == DynamicDispatchBifurcate) {
+        BifurcateCall(BifurReg, *Call, D, Bldr, Pred);
+        return;
+      } else {
+        // We are not bifurcating and we do have a Decl, so just inline.
+        if (inlineCall(*Call, D, Bldr, Pred, State))
+          return;
+      }
+    }
   }
 
   // If we can't inline it, handle the return value and invalidate the regions.
-  unsigned Count = currentBuilderContext->getCurrentBlockCount();
-  State = Call->invalidateRegions(Count, State);
-  State = bindReturnValue(*Call, Pred->getLocationContext(), State);
+  conservativeEvalCall(*Call, Bldr, Pred, State);
+}
 
-  // And make the result node.
-  Bldr.generateNode(Call->getProgramPoint(), State, Pred);
+void ExprEngine::BifurcateCall(const MemRegion *BifurReg,
+                               const CallEvent &Call, const Decl *D,
+                               NodeBuilder &Bldr, ExplodedNode *Pred) {
+  assert(BifurReg);
+
+  // Check if we've performed the split already - note, we only want
+  // to split the path once per memory region.
+  ProgramStateRef State = Pred->getState();
+  DynamicDispatchBifur BM = State->get<DynamicDispatchBifurcationMap>();
+  for (DynamicDispatchBifur::iterator I = BM.begin(),
+                                      E = BM.end(); I != E; ++I) {
+    if (I->first == BifurReg) {
+      // If we are on "inline path", keep inlining if possible.
+      if (I->second == true)
+        if (inlineCall(Call, D, Bldr, Pred, State))
+          return;
+      // If inline failed, or we are on the path where we assume we
+      // don't have enough info about the receiver to inline, conjure the
+      // return value and invalidate the regions.
+      conservativeEvalCall(Call, Bldr, Pred, State);
+      return;
+    }
+  }
+
+  // If we got here, this is the first time we process a message to this
+  // region, so split the path.
+  ProgramStateRef IState =
+      State->set<DynamicDispatchBifurcationMap>(BifurReg, true);
+  inlineCall(Call, D, Bldr, Pred, IState);
+
+  ProgramStateRef NoIState =
+      State->set<DynamicDispatchBifurcationMap>(BifurReg, false);
+  conservativeEvalCall(Call, Bldr, Pred, NoIState);
+
+  NumOfDynamicDispatchPathSplits++;
+  return;
 }
 
+
 void ExprEngine::VisitReturnStmt(const ReturnStmt *RS, ExplodedNode *Pred,
                                  ExplodedNodeSet &Dst) {
   

Added: cfe/trunk/test/Analysis/inlining/DynDispatchBifurcate.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/inlining/DynDispatchBifurcate.m?rev=161552&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/inlining/DynDispatchBifurcate.m (added)
+++ cfe/trunk/test/Analysis/inlining/DynDispatchBifurcate.m Wed Aug  8 19:21:33 2012
@@ -0,0 +1,51 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core -analyzer-ipa=dynamic-bifurcate -verify %s
+
+typedef signed char BOOL;
+typedef struct objc_class *Class;
+typedef struct objc_object {
+    Class isa;
+} *id;
+ at protocol NSObject  - (BOOL)isEqual:(id)object; @end
+ at interface NSObject <NSObject> {}
++(id)alloc;
+-(id)init;
+-(id)autorelease;
+-(id)copy;
+- (Class)class;
+-(id)retain;
+ at end
+
+ at interface MyParent : NSObject
+- (int)getZero;
+ at end
+ at implementation MyParent
+- (int)getZero {
+    return 0;
+}
+ at end
+
+ at interface MyClass : MyParent
+- (int)getZero;
+ at end
+
+MyClass *getObj();
+
+// Check that we explore both paths - on one the calla are inlined and they are 
+// not inlined on the other.
+// In this case, p can be either the object of type MyParent* or MyClass*:
+// - If it's MyParent*, getZero returns 0.
+// - If it's MyClass*, getZero returns 1 and 'return 5/m' is reachable.
+ at implementation MyClass
++ (int) testTypeFromParam:(MyParent*) p {
+  int m = 0;
+  int z = [p getZero];
+  if (z)
+    return 5/m; // expected-warning {{Division by zero}}
+  return 5/[p getZero];// expected-warning {{Division by zero}}
+}
+
+- (int)getZero {
+    return 1;
+}
+
+ at end
\ No newline at end of file





More information about the cfe-commits mailing list