[cfe-commits] [patch] Basic constant folding for analyzer's SimpleSValuator

Jordy Rose jediknil at belkadan.com
Fri Jun 18 16:48:29 PDT 2010


Adds analyzer support for idempotent and tautological binary operations
such as "a*0" and "a+0". This is not very powerful, but does make the
analyzer look a little smarter than it actually is.
-------------- next part --------------
Index: lib/Checker/SimpleSValuator.cpp
===================================================================
--- lib/Checker/SimpleSValuator.cpp	(revision 106339)
+++ lib/Checker/SimpleSValuator.cpp	(working copy)
@@ -34,6 +34,9 @@
                            QualType resultTy);
   virtual SVal EvalBinOpLN(const GRState *state, BinaryOperator::Opcode op,
                            Loc lhs, NonLoc rhs, QualType resultTy);
+  
+  SVal MakeSymIntVal(const SymExpr *LHS, BinaryOperator::Opcode op,
+                     const llvm::APSInt &RHS, QualType resultTy);
 };
 } // end anonymous namespace
 
@@ -211,6 +214,81 @@
   return ValMgr.makeTruthVal(isEqual ? lhs == rhs : lhs != rhs, resultTy);
 }
 
+SVal SimpleSValuator::MakeSymIntVal(const SymExpr *LHS,
+                                    BinaryOperator::Opcode op,
+                                    const llvm::APSInt &RHS,
+                                    QualType resultTy) {
+  bool isIdempotent = false;
+
+  // Check for a few special cases with known reductions first.
+  switch (op) {
+  default:
+    // We can't reduce this case; just treat it normally.
+    break;
+  case BinaryOperator::Mul:
+    // a*0 and a*1
+    if (RHS == 0)
+      return ValMgr.makeIntVal(0, resultTy);
+    else if (RHS == 1)
+      isIdempotent = true;
+    break;
+  case BinaryOperator::Div:
+    // a/0 and a/1
+    if (RHS == 0)
+      // This is also handled elsewhere.
+      return UndefinedVal();
+    else if (RHS == 1)
+      isIdempotent = true;
+    break;
+  case BinaryOperator::Rem:
+    // a%0 and a%1
+    if (RHS == 0)
+      // This is also handled elsewhere.
+      return UndefinedVal();
+    else if (RHS == 1)
+      return ValMgr.makeIntVal(0, resultTy);
+    break;
+  case BinaryOperator::Add:
+  case BinaryOperator::Sub:
+  case BinaryOperator::Shl:
+  case BinaryOperator::Shr:
+  case BinaryOperator::Xor:
+    // a+0, a-0, a<<0, a>>0, a^0
+    if (RHS == 0)
+      isIdempotent = true;
+    break;
+  case BinaryOperator::And:
+    // a&0 and a&(~0)
+    if (RHS == 0)
+      return ValMgr.makeIntVal(0, resultTy);
+    else if (RHS.isAllOnesValue())
+      isIdempotent = true;
+    break;
+  case BinaryOperator::Or:
+    // a|0 and a|(~0)
+    if (RHS == 0)
+      isIdempotent = true;
+    else if (RHS.isAllOnesValue()) {
+      BasicValueFactory &BVF = ValMgr.getBasicValueFactory();
+      const llvm::APSInt &Result = BVF.Convert(resultTy, RHS);
+      return nonloc::ConcreteInt(Result);
+    }
+    break;
+  }
+
+  // Idempotent ops (like a*1) can still change the type of an expression.
+  // Wrap the LHS up in a NonLoc again and let EvalCastNL do the dirty work.
+  if (isIdempotent)
+    if (SymbolRef LHSSym = dyn_cast<SymbolData>(LHS))
+      return EvalCastNL(nonloc::SymbolVal(LHSSym), resultTy);
+    else
+      return EvalCastNL(nonloc::SymExprVal(LHS), resultTy);
+
+  // If we reach this point, the expression cannot be simplified.
+  // Make a SymExprVal for the entire thing.
+  return ValMgr.makeNonLoc(LHS, op, RHS, resultTy);
+}
+
 SVal SimpleSValuator::EvalBinOpNN(const GRState *state,
                                   BinaryOperator::Opcode op,
                                   NonLoc lhs, NonLoc rhs,
@@ -228,6 +306,12 @@
       case BinaryOperator::GT:
       case BinaryOperator::NE:
         return ValMgr.makeTruthVal(false, resultTy);
+      case BinaryOperator::Xor:
+      case BinaryOperator::Sub:
+        return ValMgr.makeIntVal(0, resultTy);
+      case BinaryOperator::Or:
+      case BinaryOperator::And:
+        return EvalCastNL(lhs, resultTy);
     }
 
   while (1) {
@@ -336,23 +420,25 @@
             newRHS = BVF.EvaluateAPSInt(BinaryOperator::Sub,
                                         symIntExpr->getRHS(),
                                         rhsInt->getValue());
-          return ValMgr.makeNonLoc(symIntExpr->getLHS(), lop, *newRHS,
-                                   resultTy);
+          return MakeSymIntVal(symIntExpr->getLHS(), lop, *newRHS, resultTy);
         }
       }
 
       // Otherwise, make a SymExprVal out of the expression.
-      return ValMgr.makeNonLoc(symIntExpr, op, rhsInt->getValue(), resultTy);
+      return MakeSymIntVal(symIntExpr, op, rhsInt->getValue(), resultTy);
     }
     case nonloc::ConcreteIntKind: {
+      const nonloc::ConcreteInt& lhsInt = cast<nonloc::ConcreteInt>(lhs);
+
       if (isa<nonloc::ConcreteInt>(rhs)) {
-        const nonloc::ConcreteInt& lhsInt = cast<nonloc::ConcreteInt>(lhs);
         return lhsInt.evalBinOp(ValMgr, op, cast<nonloc::ConcreteInt>(rhs));
-      }
-      else {
+      } else {
+        const llvm::APSInt& lhsValue = lhsInt.getValue();
+        
         // Swap the left and right sides and flip the operator if doing so
         // allows us to better reason about the expression (this is a form
         // of expression canonicalization).
+        // While we're at it, catch some special cases for non-commutative ops.
         NonLoc tmp = rhs;
         rhs = lhs;
         lhs = tmp;
@@ -366,7 +452,18 @@
           case BinaryOperator::NE:
           case BinaryOperator::Add:
           case BinaryOperator::Mul:
+          case BinaryOperator::And:
+          case BinaryOperator::Xor:
+          case BinaryOperator::Or:
             continue;
+          case BinaryOperator::Shr:
+            if (lhsValue.isAllOnesValue() && lhsValue.isSigned())
+              return lhsInt;
+            // FALL-THROUGH
+          case BinaryOperator::Shl:
+            if (lhsValue == 0)
+              return lhsInt;
+            return UnknownVal();
           default:
             return UnknownVal();
         }
@@ -402,9 +499,9 @@
         }
       
       if (isa<nonloc::ConcreteInt>(rhs)) {
-        return ValMgr.makeNonLoc(slhs->getSymbol(), op,
-                                 cast<nonloc::ConcreteInt>(rhs).getValue(),
-                                 resultTy);
+        return MakeSymIntVal(slhs->getSymbol(), op,
+                             cast<nonloc::ConcreteInt>(rhs).getValue(),
+                             resultTy);
       }
 
       return UnknownVal();
Index: test/Analysis/constant-folding.c
===================================================================
--- test/Analysis/constant-folding.c	(revision 0)
+++ test/Analysis/constant-folding.c	(revision 0)
@@ -0,0 +1,43 @@
+// RUN: %clang_cc1 -analyze -analyzer-experimental-internal-checks -analyzer-check-objc-mem -analyzer-experimental-checks -verify %s
+
+// Triggers a warning if the analyzer reaches this point in the control flow.
+#define UNREACHABLE ((void)*(char*)0)
+
+// There should be no warnings at all.
+
+void testComparisons (int a) {
+  // Sema can already catch the simple comparison a==a,
+  // since that's usually a logic error (and not path-dependent).
+  int b = a;
+  if (!(b==a)) UNREACHABLE;
+  if (!(b>=a)) UNREACHABLE;
+  if (!(b<=a)) UNREACHABLE;
+  if (b!=a) UNREACHABLE;
+  if (b>a) UNREACHABLE;
+  if (b<a) UNREACHABLE;
+}
+
+void testSelfOperations (int a) {
+  if ((a|a) != a) UNREACHABLE;
+  if ((a&a) != a) UNREACHABLE;
+  if ((a^a) != 0) UNREACHABLE;
+  if ((a-a) != 0) UNREACHABLE;
+}
+
+void testIdempotent (int a) {
+  if ((a*1) != a) UNREACHABLE;
+  if ((a/1) != a) UNREACHABLE;
+  if ((a+0) != a) UNREACHABLE;
+  if ((a-0) != a) UNREACHABLE;
+  if ((a<<0) != a) UNREACHABLE;
+  if ((a>>0) != a) UNREACHABLE;
+  if ((a^0) != a) UNREACHABLE;
+  if ((a&(~0)) != a) UNREACHABLE;
+  if ((a|0) != a) UNREACHABLE;
+}
+
+void testReductionToConstant (int a) {
+  if ((a*0) != 0) UNREACHABLE;
+  if ((a&0) != 0) UNREACHABLE;
+  if ((a|(~0)) != (~0)) UNREACHABLE;
+}


More information about the cfe-commits mailing list