[cfe-commits] r107706 - in /cfe/trunk: include/clang/AST/Stmt.h include/clang/Checker/PathSensitive/SVals.h include/clang/Driver/CC1Options.td include/clang/Frontend/AnalyzerOptions.h lib/AST/Stmt.cpp lib/Checker/AnalysisConsumer.cpp lib/Checker/

Chandler Carruth chandlerc at google.com
Tue Jul 6 17:39:17 PDT 2010


On Tue, Jul 6, 2010 at 2:43 PM, Tom Care <tcare at apple.com> wrote:

> Author: tcare
> Date: Tue Jul  6 16:43:29 2010
> New Revision: 107706
>
> URL: http://llvm.org/viewvc/llvm-project?rev=107706&view=rev
> Log:
> Added a path-sensitive idempotent operation checker
> (-analyzer-idempotent-operation). Finds idempotent and/or tautological
> operations in a path sensitive context, flagging operations that have no
> effect or a predictable effect.
>
> Example:
> {
> int a = 1;
> int b = 5;
> int c = b / a; // a is 1 on all paths
> }
>
> - New IdempotentOperationChecker class
> - Moved recursive Stmt functions in r107675 to IdempotentOperationChecker
> - Minor refactoring of SVal to allow checking for any integer
> - Added command line option for check
> - Added basic test cases
>
> Added:
>    cfe/trunk/lib/Checker/IdempotentOperationChecker.cpp
>    cfe/trunk/test/Analysis/idempotent-operations.c
> Modified:
>    cfe/trunk/include/clang/AST/Stmt.h
>    cfe/trunk/include/clang/Checker/PathSensitive/SVals.h
>    cfe/trunk/include/clang/Driver/CC1Options.td
>    cfe/trunk/include/clang/Frontend/AnalyzerOptions.h
>    cfe/trunk/lib/AST/Stmt.cpp
>    cfe/trunk/lib/Checker/AnalysisConsumer.cpp
>    cfe/trunk/lib/Checker/CMakeLists.txt
>    cfe/trunk/lib/Checker/GRExprEngineExperimentalChecks.h
>    cfe/trunk/lib/Checker/SVals.cpp
>    cfe/trunk/lib/Frontend/CompilerInvocation.cpp
>
> Modified: cfe/trunk/include/clang/AST/Stmt.h
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Stmt.h?rev=107706&r1=107705&r2=107706&view=diff
>
> ==============================================================================
> --- cfe/trunk/include/clang/AST/Stmt.h (original)
> +++ cfe/trunk/include/clang/AST/Stmt.h Tue Jul  6 16:43:29 2010
> @@ -253,25 +253,6 @@
>   ///  within CFGs.
>   bool hasImplicitControlFlow() const;
>
> -  /// contains* - Useful recursive methods to see if a statement contains
> an
> -  ///   element somewhere. Used in static analysis to reduce false
> positives.
> -  static bool containsMacro(const Stmt *S);
> -  static bool containsEnum(const Stmt *S);
> -  static bool containsZeroConstant(const Stmt *S);
> -  static bool containsOneConstant(const Stmt *S);
> -  template <class T> static bool containsStmt(const Stmt *S) {
> -    if (isa<T>(S))
> -        return true;
> -
> -    for (Stmt::const_child_iterator I = S->child_begin(); I !=
> S->child_end(); ++I)
> -        if (const Stmt *child = *I)
> -          if (containsStmt<T>(child))
> -            return true;
> -
> -      return false;
> -  }
> -
> -
>   /// Child Iterators: All subclasses must implement child_begin and
> child_end
>   ///  to permit easy iteration over the substatements/subexpessions of an
>   ///  AST node.  This permits easy iteration over all nodes in the AST.
>
> Modified: cfe/trunk/include/clang/Checker/PathSensitive/SVals.h
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Checker/PathSensitive/SVals.h?rev=107706&r1=107705&r2=107706&view=diff
>
> ==============================================================================
> --- cfe/trunk/include/clang/Checker/PathSensitive/SVals.h (original)
> +++ cfe/trunk/include/clang/Checker/PathSensitive/SVals.h Tue Jul  6
> 16:43:29 2010
> @@ -98,6 +98,8 @@
>
>   bool isConstant() const;
>
> +  bool isConstant(int I) const;
> +
>   bool isZeroConstant() const;
>
>   /// hasConjuredSymbol - If this SVal wraps a conjured symbol, return
> true;
>
> Modified: cfe/trunk/include/clang/Driver/CC1Options.td
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Driver/CC1Options.td?rev=107706&r1=107705&r2=107706&view=diff
>
> ==============================================================================
> --- cfe/trunk/include/clang/Driver/CC1Options.td (original)
> +++ cfe/trunk/include/clang/Driver/CC1Options.td Tue Jul  6 16:43:29 2010
> @@ -81,6 +81,8 @@
>   HelpText<"Emit verbose output about the analyzer's progress">;
>  def analyzer_experimental_checks : Flag<"-analyzer-experimental-checks">,
>   HelpText<"Use experimental path-sensitive checks">;
> +def analyzer_idempotent_operation :
> Flag<"-analyzer-idempotent-operation">,
> +  HelpText<"Use experimental path-sensitive idempotent operation
> checker">;
>  def analyzer_experimental_internal_checks :
>   Flag<"-analyzer-experimental-internal-checks">,
>   HelpText<"Use new default path-sensitive checks currently in testing">;
>
> Modified: cfe/trunk/include/clang/Frontend/AnalyzerOptions.h
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Frontend/AnalyzerOptions.h?rev=107706&r1=107705&r2=107706&view=diff
>
> ==============================================================================
> --- cfe/trunk/include/clang/Frontend/AnalyzerOptions.h (original)
> +++ cfe/trunk/include/clang/Frontend/AnalyzerOptions.h Tue Jul  6 16:43:29
> 2010
> @@ -72,6 +72,7 @@
>   unsigned VisualizeEGUbi : 1;
>   unsigned EnableExperimentalChecks : 1;
>   unsigned EnableExperimentalInternalChecks : 1;
> +  unsigned EnableIdempotentOperationChecker : 1;
>   unsigned InlineCall : 1;
>
>  public:
>
> Modified: cfe/trunk/lib/AST/Stmt.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Stmt.cpp?rev=107706&r1=107705&r2=107706&view=diff
>
> ==============================================================================
> --- cfe/trunk/lib/AST/Stmt.cpp (original)
> +++ cfe/trunk/lib/AST/Stmt.cpp Tue Jul  6 16:43:29 2010
> @@ -127,72 +127,6 @@
>   }
>  }
>
> -// Recursively find any substatements containing macros
> -bool Stmt::containsMacro(const Stmt *S) {
> -  if (S->getLocStart().isMacroID())
> -    return true;
> -
> -  if (S->getLocEnd().isMacroID())
> -    return true;
> -
> -  for (Stmt::const_child_iterator I = S->child_begin(); I !=
> S->child_end(); ++I)
> -    if (const Stmt *child = *I)
> -      if (containsMacro(child))
> -        return true;
> -
> -  return false;
> -}
> -
> -// Recursively find any substatements containing enum constants
> -bool Stmt::containsEnum(const Stmt *S) {
> -  const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(S);
> -
> -  if (DR && isa<EnumConstantDecl>(DR->getDecl()))
> -    return true;
> -
> -  for (Stmt::const_child_iterator I = S->child_begin(); I !=
> S->child_end(); ++I)
> -      if (const Stmt *child = *I)
> -        if (containsEnum(child))
> -          return true;
> -
> -  return false;
> -}
> -
> -bool Stmt::containsZeroConstant(const Stmt *S) {
> -  const IntegerLiteral *IL = dyn_cast<IntegerLiteral>(S);
> -  if (IL && IL->getValue() == 0)
> -    return true;
> -
> -  const FloatingLiteral *FL = dyn_cast<FloatingLiteral>(S);
> -  if (FL && FL->getValue().isZero())
> -    return true;
> -
> -  for (Stmt::const_child_iterator I = S->child_begin(); I !=
> S->child_end(); ++I)
> -    if (const Stmt *child = *I)
> -      if (containsZeroConstant(child))
> -        return true;
> -
> -  return false;
> -}
> -
> -bool Stmt::containsOneConstant(const Stmt *S) {
> -  const IntegerLiteral *IL = dyn_cast<IntegerLiteral>(S);
> -  if (IL && IL->getValue() == 1)
> -    return true;
> -
> -  const FloatingLiteral *FL = dyn_cast<FloatingLiteral>(S);
> -  const llvm::APFloat one(1.0);
> -  if (FL && FL->getValue().compare(one) == llvm::APFloat::cmpEqual)
> -    return true;
> -
> -  for (Stmt::const_child_iterator I = S->child_begin(); I !=
> S->child_end(); ++I)
> -    if (const Stmt *child = *I)
> -      if (containsOneConstant(child))
> -        return true;
> -
> -  return false;
> -}
> -
>  Expr *AsmStmt::getOutputExpr(unsigned i) {
>   return cast<Expr>(Exprs[i]);
>  }
>
> Modified: cfe/trunk/lib/Checker/AnalysisConsumer.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Checker/AnalysisConsumer.cpp?rev=107706&r1=107705&r2=107706&view=diff
>
> ==============================================================================
> --- cfe/trunk/lib/Checker/AnalysisConsumer.cpp (original)
> +++ cfe/trunk/lib/Checker/AnalysisConsumer.cpp Tue Jul  6 16:43:29 2010
> @@ -28,6 +28,7 @@
>  #include "clang/Checker/PathSensitive/GRExprEngine.h"
>  #include "clang/Checker/PathSensitive/GRTransferFuncs.h"
>  #include "clang/Checker/PathDiagnosticClients.h"
> +#include "GRExprEngineExperimentalChecks.h"
>  #include "clang/Basic/FileManager.h"
>  #include "clang/Basic/SourceManager.h"
>  #include "clang/Frontend/AnalyzerOptions.h"
> @@ -340,6 +341,9 @@
>   if (C.Opts.EnableExperimentalChecks)
>     RegisterExperimentalChecks(Eng);
>
> +  if (C.Opts.EnableIdempotentOperationChecker)
> +    RegisterIdempotentOperationChecker(Eng);
> +
>   // Set the graph auditor.
>   llvm::OwningPtr<ExplodedNode::Auditor> Auditor;
>   if (mgr.shouldVisualizeUbigraph()) {
>
> Modified: cfe/trunk/lib/Checker/CMakeLists.txt
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Checker/CMakeLists.txt?rev=107706&r1=107705&r2=107706&view=diff
>
> ==============================================================================
> --- cfe/trunk/lib/Checker/CMakeLists.txt (original)
> +++ cfe/trunk/lib/Checker/CMakeLists.txt Tue Jul  6 16:43:29 2010
> @@ -39,6 +39,7 @@
>   GRExprEngineExperimentalChecks.cpp
>   GRState.cpp
>   HTMLDiagnostics.cpp
> +  IdempotentOperationChecker.cpp
>   LLVMConventionsChecker.cpp
>   MacOSXAPIChecker.cpp
>   MallocChecker.cpp
>
> Modified: cfe/trunk/lib/Checker/GRExprEngineExperimentalChecks.h
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Checker/GRExprEngineExperimentalChecks.h?rev=107706&r1=107705&r2=107706&view=diff
>
> ==============================================================================
> --- cfe/trunk/lib/Checker/GRExprEngineExperimentalChecks.h (original)
> +++ cfe/trunk/lib/Checker/GRExprEngineExperimentalChecks.h Tue Jul  6
> 16:43:29 2010
> @@ -22,6 +22,7 @@
>  void RegisterPthreadLockChecker(GRExprEngine &Eng);
>  void RegisterMallocChecker(GRExprEngine &Eng);
>  void RegisterStreamChecker(GRExprEngine &Eng);
> +void RegisterIdempotentOperationChecker(GRExprEngine &Eng);
>
>  } // end clang namespace
>  #endif
>
> Added: cfe/trunk/lib/Checker/IdempotentOperationChecker.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Checker/IdempotentOperationChecker.cpp?rev=107706&view=auto
>
> ==============================================================================
> --- cfe/trunk/lib/Checker/IdempotentOperationChecker.cpp (added)
> +++ cfe/trunk/lib/Checker/IdempotentOperationChecker.cpp Tue Jul  6
> 16:43:29 2010
> @@ -0,0 +1,453 @@
> +//==- IdempotentOperationChecker.cpp - Idempotent Operations ----*- C++
> -*-==//
> +//
> +//                     The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
>
> +//===----------------------------------------------------------------------===//
> +//
> +// This file defines a set of path-sensitive checks for idempotent and/or
> +// tautological operations. Each potential operation is checked along all
> paths
> +// to see if every path results in a pointless operation.
> +//                 +-------------------------------------------+
> +//                 |Table of idempotent/tautological operations|
> +//                 +-------------------------------------------+
>
> +//+--------------------------------------------------------------------------+
> +//|Operator | x op x | x op 1 | 1 op x | x op 0 | 0 op x | x op ~0 | ~0 op
> x |
>
> +//+--------------------------------------------------------------------------+
> +//  +, +=   |        |        |        |   x    |   x    |         |
> +//  -, -=   |        |        |        |   x    |   -x   |         |
> +//  *, *=   |        |   x    |   x    |   0    |   0    |         |
> +//  /, /=   |   1    |   x    |        |  N/A   |   0    |         |
> +//  &, &=   |   x    |        |        |   0    |   0    |   x     |    x
> +//  |, |=   |   x    |        |        |   x    |   x    |   ~0    |    ~0
> +//  ^, ^=   |   0    |        |        |   x    |   x    |         |
> +//  <<, <<= |        |        |        |   x    |   0    |         |
> +//  >>, >>= |        |        |        |   x    |   0    |         |
> +//  ||      |   1    |   1    |   1    |   x    |   x    |   1     |    1
> +//  &&      |   1    |   x    |   x    |   0    |   0    |   x     |    x
> +//  =       |   x    |        |        |        |        |         |
> +//  ==      |   1    |        |        |        |        |         |
> +//  >=      |   1    |        |        |        |        |         |
> +//  <=      |   1    |        |        |        |        |         |
> +//  >       |   0    |        |        |        |        |         |
> +//  <       |   0    |        |        |        |        |         |
> +//  !=      |   0    |        |        |        |        |         |
>
> +//===----------------------------------------------------------------------===//
> +//
> +// Ways to reduce false positives (that need to be implemented):
> +// - Don't flag downsizing casts
> +// - Improved handling of static/global variables
> +// - Per-block marking of incomplete analysis
> +// - Handling ~0 values
> +// - False positives involving silencing unused variable warnings
> +//
> +// Other things TODO:
> +// - Improved error messages
> +// - Handle mixed assumptions (which assumptions can belong together?)
> +// - Finer grained false positive control (levels)
> +
> +#include "GRExprEngineExperimentalChecks.h"
> +#include "clang/Checker/BugReporter/BugType.h"
> +#include "clang/Checker/PathSensitive/CheckerVisitor.h"
> +#include "clang/Checker/PathSensitive/SVals.h"
> +#include "clang/AST/Stmt.h"
> +#include "llvm/ADT/DenseMap.h"
> +
> +using namespace clang;
> +
> +namespace {
> +class IdempotentOperationChecker
> +  : public CheckerVisitor<IdempotentOperationChecker> {
> +  public:
> +    static void *getTag();
> +    void PreVisitBinaryOperator(CheckerContext &C, const BinaryOperator
> *B);
> +    void VisitEndAnalysis(ExplodedGraph &G, BugReporter &B,
> +        bool hasWorkRemaining);
> +
> +  private:
> +    // Our assumption about a particular operation.
> +    enum Assumption { Possible, Impossible, Equal, LHSis1, RHSis1, LHSis0,
> +        RHSis0 };
> +
> +    void UpdateAssumption(Assumption &A, const Assumption &New);
> +
> +    /// contains* - Useful recursive methods to see if a statement
> contains an
> +    ///   element somewhere. Used in static analysis to reduce false
> positives.
> +    static bool containsMacro(const Stmt *S);
> +    static bool containsEnum(const Stmt *S);
> +    static bool containsBuiltinOffsetOf(const Stmt *S);
> +    static bool containsZeroConstant(const Stmt *S);
> +    static bool containsOneConstant(const Stmt *S);
> +    template <class T> static bool containsStmt(const Stmt *S) {
> +      if (isa<T>(S))
> +          return true;
> +
> +      for (Stmt::const_child_iterator I = S->child_begin(); I !=
> S->child_end();
> +          ++I)
> +        if (const Stmt *child = *I)
> +          if (containsStmt<T>(child))
> +            return true;
> +
> +        return false;
> +    }
> +
> +    // Hash table
> +    typedef llvm::DenseMap<const BinaryOperator *, Assumption>
> AssumptionMap;
> +    AssumptionMap hash;
> +};
> +}
> +
> +void *IdempotentOperationChecker::getTag() {
> +  static int x = 0;
> +  return &x;
> +}
> +
> +void clang::RegisterIdempotentOperationChecker(GRExprEngine &Eng) {
> +  Eng.registerCheck(new IdempotentOperationChecker());
> +}
> +
> +void IdempotentOperationChecker::PreVisitBinaryOperator(
> +                                                      CheckerContext &C,
> +                                                      const BinaryOperator
> *B) {
> +  // Find or create an entry in the hash for this BinaryOperator instance
> +  AssumptionMap::iterator i = hash.find(B);
> +  Assumption &A = i == hash.end() ? hash[B] : i->second;
> +
> +  // If we had to create an entry, initialise the value to Possible
> +  if (i == hash.end())
> +    A = Possible;
> +
> +  // If we already have visited this node on a path that does not contain
> an
> +  // idempotent operation, return immediately.
> +  if (A == Impossible)
> +    return;
> +
> +  // Skip binary operators containing common false positives
> +  if (containsMacro(B) || containsEnum(B) ||
> containsStmt<SizeOfAlignOfExpr>(B)
> +      || containsZeroConstant(B) || containsOneConstant(B)
> +      || containsBuiltinOffsetOf(B)) {
> +    A = Impossible;
> +    return;
> +  }
> +
> +  const Expr *LHS = B->getLHS();
> +  const Expr *RHS = B->getRHS();
> +
> +  const GRState *state = C.getState();
> +
> +  SVal LHSVal = state->getSVal(LHS);
> +  SVal RHSVal = state->getSVal(RHS);
> +
> +  // If either value is unknown, we can't be 100% sure of all paths.
> +  if (LHSVal.isUnknownOrUndef() || RHSVal.isUnknownOrUndef()) {
> +    A = Impossible;
> +    return;
> +  }
> +  BinaryOperator::Opcode Op = B->getOpcode();
> +
> +  // Dereference the LHS SVal if this is an assign operation
> +  switch (Op) {
> +  default:
> +    break;
> +
> +  // Fall through intentional
> +  case BinaryOperator::AddAssign:
> +  case BinaryOperator::SubAssign:
> +  case BinaryOperator::MulAssign:
> +  case BinaryOperator::DivAssign:
> +  case BinaryOperator::AndAssign:
> +  case BinaryOperator::OrAssign:
> +  case BinaryOperator::XorAssign:
> +  case BinaryOperator::ShlAssign:
> +  case BinaryOperator::ShrAssign:
> +  case BinaryOperator::Assign:
> +  // Assign statements have one extra level of indirection
> +    if (!isa<Loc>(LHSVal)) {
> +      A = Impossible;
> +      return;
> +    }
> +    LHSVal = state->getSVal(cast<Loc>(LHSVal));
> +  }
> +
> +
> +  // We now check for various cases which result in an idempotent
> operation.
> +
> +  // x op x
> +  switch (Op) {
> +  default:
> +    break; // We don't care about any other operators.
> +
> +  // Fall through intentional
> +  case BinaryOperator::SubAssign:
> +  case BinaryOperator::DivAssign:
> +  case BinaryOperator::AndAssign:
> +  case BinaryOperator::OrAssign:
> +  case BinaryOperator::XorAssign:
> +  case BinaryOperator::Assign:
> +  case BinaryOperator::Sub:
> +  case BinaryOperator::Div:
> +  case BinaryOperator::And:
> +  case BinaryOperator::Or:
> +  case BinaryOperator::Xor:
> +  case BinaryOperator::LOr:
> +  case BinaryOperator::LAnd:
> +    if (LHSVal != RHSVal)
> +      break;
> +    UpdateAssumption(A, Equal);
> +    return;
> +  }
> +
> +  // x op 1
> +  switch (Op) {
> +   default:
> +     break; // We don't care about any other operators.
> +
> +   // Fall through intentional
> +   case BinaryOperator::MulAssign:
> +   case BinaryOperator::DivAssign:
> +   case BinaryOperator::Mul:
> +   case BinaryOperator::Div:
> +   case BinaryOperator::LOr:
> +   case BinaryOperator::LAnd:
> +     if (!RHSVal.isConstant(1))
> +       break;
> +     UpdateAssumption(A, RHSis1);
> +     return;
> +  }
> +
> +  // 1 op x
> +  switch (Op) {
> +  default:
> +    break; // We don't care about any other operators.
> +
> +  // Fall through intentional
> +  case BinaryOperator::MulAssign:
> +  case BinaryOperator::Mul:
> +  case BinaryOperator::LOr:
> +  case BinaryOperator::LAnd:
> +    if (!LHSVal.isConstant(1))
> +      break;
> +    UpdateAssumption(A, LHSis1);
> +    return;
> +  }
> +
> +  // x op 0
> +  switch (Op) {
> +  default:
> +    break; // We don't care about any other operators.
> +
> +  // Fall through intentional
> +  case BinaryOperator::AddAssign:
> +  case BinaryOperator::SubAssign:
> +  case BinaryOperator::MulAssign:
> +  case BinaryOperator::AndAssign:
> +  case BinaryOperator::OrAssign:
> +  case BinaryOperator::XorAssign:
> +  case BinaryOperator::Add:
> +  case BinaryOperator::Sub:
> +  case BinaryOperator::Mul:
> +  case BinaryOperator::And:
> +  case BinaryOperator::Or:
> +  case BinaryOperator::Xor:
> +  case BinaryOperator::Shl:
> +  case BinaryOperator::Shr:
> +  case BinaryOperator::LOr:
> +  case BinaryOperator::LAnd:
> +    if (!RHSVal.isConstant(0))
> +      break;
> +    UpdateAssumption(A, RHSis0);
> +    return;
> +  }
> +
> +  // 0 op x
> +  switch (Op) {
> +  default:
> +    break; // We don't care about any other operators.
> +
> +  // Fall through intentional
> +  //case BinaryOperator::AddAssign: // Common false positive
> +  case BinaryOperator::SubAssign: // Check only if unsigned
> +  case BinaryOperator::MulAssign:
> +  case BinaryOperator::DivAssign:
> +  case BinaryOperator::AndAssign:
> +  //case BinaryOperator::OrAssign: // Common false positive
> +  //case BinaryOperator::XorAssign: // Common false positive
> +  case BinaryOperator::ShlAssign:
> +  case BinaryOperator::ShrAssign:
> +  case BinaryOperator::Add:
> +  case BinaryOperator::Sub:
> +  case BinaryOperator::Mul:
> +  case BinaryOperator::Div:
> +  case BinaryOperator::And:
> +  case BinaryOperator::Or:
> +  case BinaryOperator::Xor:
> +  case BinaryOperator::Shl:
> +  case BinaryOperator::Shr:
> +  case BinaryOperator::LOr:
> +  case BinaryOperator::LAnd:
> +    if (!LHSVal.isConstant(0))
> +      break;
> +    UpdateAssumption(A, LHSis0);
> +    return;
> +  }
> +
> +  // If we get to this point, there has been a valid use of this
> operation.
> +  A = Impossible;
> +}
> +
> +void IdempotentOperationChecker::VisitEndAnalysis(ExplodedGraph &G,
> +                                                  BugReporter &B,
> +                                                  bool hasWorkRemaining) {
> +  // If there is any work remaining we cannot be 100% sure about our
> warnings
> +  if (hasWorkRemaining)
> +    return;
> +
> +  // Iterate over the hash to see if we have any paths with definite
> +  // idempotent operations.
> +  for (AssumptionMap::const_iterator i =
> +      hash.begin(); i != hash.end(); ++i) {
> +    if (i->second != Impossible) {
>

Any reason not to just 'continue' in the case below? At the very least
'break' there seems strange, llvm_unreachable would make more sense.

Also, I tweaked this code a bit in r107741 and 107745 to make GCC happier
about the msg variable. Check and see if that looks OK?


> +      // Select the error message.
> +      const char *msg;
> +      switch (i->second) {
> +      case Equal:
> +        msg = "idempotent operation; both operands are always equal in
> value";
> +        break;
> +      case LHSis1:
> +        msg = "idempotent operation; the left operand is always 1";
> +        break;
> +      case RHSis1:
> +        msg = "idempotent operation; the right operand is always 1";
> +        break;
> +      case LHSis0:
> +        msg = "idempotent operation; the left operand is always 0";
> +        break;
> +      case RHSis0:
> +        msg = "idempotent operation; the right operand is always 0";
> +        break;
> +      case Impossible:
> +        break;
> +      case Possible:
> +        assert(0 && "Operation was never marked with an assumption");
> +      }
> +
> +      // Create the SourceRange Arrays
> +      SourceRange S[2] = { i->first->getLHS()->getSourceRange(),
> +                           i->first->getRHS()->getSourceRange() };
> +      B.EmitBasicReport("Idempotent operation", msg,
> i->first->getOperatorLoc(),
> +          S, 2);
> +    }
> +  }
> +}
> +
> +// Updates the current assumption given the new assumption
> +inline void IdempotentOperationChecker::UpdateAssumption(Assumption &A,
> +                                                        const Assumption
> &New) {
> +  switch (A) {
> +  // If we don't currently have an assumption, set it
> +  case Possible:
> +    A = New;
> +    return;
> +
> +  // If we have determined that a valid state happened, ignore the new
> +  // assumption.
> +  case Impossible:
> +    return;
> +
> +  // Any other case means that we had a different assumption last time. We
> don't
> +  // currently support mixing assumptions for diagnostic reasons, so we
> set
> +  // our assumption to be impossible.
> +  default:
> +    A = Impossible;
> +    return;
> +  }
> +}
> +
> +// Recursively find any substatements containing macros
> +bool IdempotentOperationChecker::containsMacro(const Stmt *S) {
> +  if (S->getLocStart().isMacroID())
> +    return true;
> +
> +  if (S->getLocEnd().isMacroID())
> +    return true;
> +
> +  for (Stmt::const_child_iterator I = S->child_begin(); I !=
> S->child_end();
> +      ++I)
> +    if (const Stmt *child = *I)
> +      if (containsMacro(child))
> +        return true;
> +
> +  return false;
> +}
> +
> +// Recursively find any substatements containing enum constants
> +bool IdempotentOperationChecker::containsEnum(const Stmt *S) {
> +  const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(S);
> +
> +  if (DR && isa<EnumConstantDecl>(DR->getDecl()))
> +    return true;
> +
> +  for (Stmt::const_child_iterator I = S->child_begin(); I !=
> S->child_end();
> +      ++I)
> +    if (const Stmt *child = *I)
> +      if (containsEnum(child))
> +        return true;
> +
> +  return false;
> +}
> +
> +// Recursively find any substatements containing __builtin_offset_of
> +bool IdempotentOperationChecker::containsBuiltinOffsetOf(const Stmt *S) {
> +  const UnaryOperator *UO = dyn_cast<UnaryOperator>(S);
> +
> +  if (UO && UO->getOpcode() == UnaryOperator::OffsetOf)
> +    return true;
> +
> +  for (Stmt::const_child_iterator I = S->child_begin(); I !=
> S->child_end();
> +      ++I)
> +    if (const Stmt *child = *I)
> +      if (containsBuiltinOffsetOf(child))
> +        return true;
> +
> +  return false;
> +}
> +
> +bool IdempotentOperationChecker::containsZeroConstant(const Stmt *S) {
> +  const IntegerLiteral *IL = dyn_cast<IntegerLiteral>(S);
> +  if (IL && IL->getValue() == 0)
> +    return true;
> +
> +  const FloatingLiteral *FL = dyn_cast<FloatingLiteral>(S);
> +  if (FL && FL->getValue().isZero())
> +    return true;
> +
> +  for (Stmt::const_child_iterator I = S->child_begin(); I !=
> S->child_end();
> +      ++I)
> +    if (const Stmt *child = *I)
> +      if (containsZeroConstant(child))
> +        return true;
> +
> +  return false;
> +}
> +
> +bool IdempotentOperationChecker::containsOneConstant(const Stmt *S) {
> +  const IntegerLiteral *IL = dyn_cast<IntegerLiteral>(S);
> +  if (IL && IL->getValue() == 1)
> +    return true;
> +
> +  const FloatingLiteral *FL = dyn_cast<FloatingLiteral>(S);
> +  const llvm::APFloat one(1.0);
> +  if (FL && FL->getValue().compare(one) == llvm::APFloat::cmpEqual)
> +    return true;
> +
> +  for (Stmt::const_child_iterator I = S->child_begin(); I !=
> S->child_end();
> +      ++I)
> +    if (const Stmt *child = *I)
> +      if (containsOneConstant(child))
> +        return true;
> +
> +  return false;
> +}
> +
>
> Modified: cfe/trunk/lib/Checker/SVals.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Checker/SVals.cpp?rev=107706&r1=107705&r2=107706&view=diff
>
> ==============================================================================
> --- cfe/trunk/lib/Checker/SVals.cpp (original)
> +++ cfe/trunk/lib/Checker/SVals.cpp Tue Jul  6 16:43:29 2010
> @@ -200,15 +200,19 @@
>   return isa<nonloc::ConcreteInt>(this) || isa<loc::ConcreteInt>(this);
>  }
>
> -bool SVal::isZeroConstant() const {
> +bool SVal::isConstant(int I) const {
>   if (isa<loc::ConcreteInt>(*this))
> -    return cast<loc::ConcreteInt>(*this).getValue() == 0;
> +    return cast<loc::ConcreteInt>(*this).getValue() == I;
>   else if (isa<nonloc::ConcreteInt>(*this))
> -    return cast<nonloc::ConcreteInt>(*this).getValue() == 0;
> +    return cast<nonloc::ConcreteInt>(*this).getValue() == I;
>   else
>     return false;
>  }
>
> +bool SVal::isZeroConstant() const {
> +  return isConstant(0);
> +}
> +
>
>
>  //===----------------------------------------------------------------------===//
>  // Transfer function dispatch for Non-Locs.
>
> Modified: cfe/trunk/lib/Frontend/CompilerInvocation.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/CompilerInvocation.cpp?rev=107706&r1=107705&r2=107706&view=diff
>
> ==============================================================================
> --- cfe/trunk/lib/Frontend/CompilerInvocation.cpp (original)
> +++ cfe/trunk/lib/Frontend/CompilerInvocation.cpp Tue Jul  6 16:43:29 2010
> @@ -112,6 +112,8 @@
>     Res.push_back("-analyzer-experimental-checks");
>   if (Opts.EnableExperimentalInternalChecks)
>     Res.push_back("-analyzer-experimental-internal-checks");
> +  if (Opts.EnableIdempotentOperationChecker)
> +    Res.push_back("-analyzer-idempotent-operation");
>  }
>
>  static void CodeGenOptsToArgs(const CodeGenOptions &Opts,
> @@ -788,6 +790,8 @@
>   Opts.EnableExperimentalChecks =
> Args.hasArg(OPT_analyzer_experimental_checks);
>   Opts.EnableExperimentalInternalChecks =
>     Args.hasArg(OPT_analyzer_experimental_internal_checks);
> +  Opts.EnableIdempotentOperationChecker =
> +    Args.hasArg(OPT_analyzer_idempotent_operation);
>   Opts.TrimGraph = Args.hasArg(OPT_trim_egraph);
>   Opts.MaxNodes = Args.getLastArgIntValue(OPT_analyzer_max_nodes,
> 150000,Diags);
>   Opts.MaxLoop = Args.getLastArgIntValue(OPT_analyzer_max_loop, 3, Diags);
>
> Added: cfe/trunk/test/Analysis/idempotent-operations.c
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/idempotent-operations.c?rev=107706&view=auto
>
> ==============================================================================
> --- cfe/trunk/test/Analysis/idempotent-operations.c (added)
> +++ cfe/trunk/test/Analysis/idempotent-operations.c Tue Jul  6 16:43:29
> 2010
> @@ -0,0 +1,52 @@
> +// RUN: %clang_cc1 -Wunused-variable -analyze
> -analyzer-idempotent-operation -analyzer-store=basic
> -analyzer-constraints=range
> +
> +// Basic tests
> +
> +extern void test(int i);
> +
> +void basic() {
> +  int x = 10, zero = 0, one = 1;
> +
> +  // x op x
> +  x = x;        // expected-warning {{idempotent operation; both operands
> are always equal in value}}
> +  test(x - x);  // expected-warning {{idempotent operation; both operands
> are always equal in value}}
> +  x -= x;       // expected-warning {{idempotent operation; both operands
> are always equal in value}}
> +  x = 10;       // no-warning
> +  test(x / x);  // expected-warning {{idempotent operation; both operands
> are always equal in value}}
> +  x /= x;       // expected-warning {{idempotent operation; both operands
> are always equal in value}}
> +  x = 10;       // no-warning
> +  test(x & x);  // expected-warning {{idempotent operation; both operands
> are always equal in value}}
> +  x &= x;       // expected-warning {{idempotent operation; both operands
> are always equal in value}}
> +  test(x | x);  // expected-warning {{idempotent operation; both operands
> are always equal in value}}
> +  x |= x;       // expected-warning {{idempotent operation; both operands
> are always equal in value}}
> +
> +  // x op 1
> +  test(x * one);  // expected-warning {{idempotent operation; the right
> operand is always 1}}
> +  x *= one;       // expected-warning {{idempotent operation; the right
> operand is always 1}}
> +  test(x / one);  // expected-warning {{idempotent operation; the right
> operand is always 1}}
> +  x /= one;       // expected-warning {{idempotent operation; the right
> operand is always 1}}
> +
> +  // 1 op x
> +  test(one * x);   // expected-warning {{idempotent operation; the left
> operand is always 1}}
> +
> +  // x op 0
> +  test(x + zero);  // expected-warning {{idempotent operation; the right
> operand is always 0}}
> +  test(x - zero);  // expected-warning {{idempotent operation; the right
> operand is always 0}}
> +  test(x * zero);  // expected-warning {{idempotent operation; the right
> operand is always 0}}
> +  test(x & zero);  // expected-warning {{idempotent operation; the right
> operand is always 0}}
> +  test(x | zero);  // expected-warning {{idempotent operation; the right
> operand is always 0}}
> +  test(x ^ zero);  // expected-warning {{idempotent operation; the right
> operand is always 0}}
> +  test(x << zero); // expected-warning {{idempotent operation; the right
> operand is always 0}}
> +  test(x >> zero); // expected-warning {{idempotent operation; the right
> operand is always 0}}
> +
> +  // 0 op x
> +  test(zero + x);  // expected-warning {{idempotent operation; the left
> operand is always 0}}
> +  test(zero - x);  // expected-warning {{idempotent operation; the left
> operand is always 0}}
> +  test(zero / x);  // expected-warning {{idempotent operation; the left
> operand is always 0}}
> +  test(zero * x);  // expected-warning {{idempotent operation; the left
> operand is always 0}}
> +  test(zero & x);  // expected-warning {{idempotent operation; the left
> operand is always 0}}
> +  test(zero | x);  // expected-warning {{idempotent operation; the left
> operand is always 0}}
> +  test(zero ^ x);  // expected-warning {{idempotent operation; the left
> operand is always 0}}
> +  test(zero << x); // expected-warning {{idempotent operation; the left
> operand is always 0}}
> +  test(zero >> x); // expected-warning {{idempotent operation; the left
> operand is always 0}}
> +}
>
>
> _______________________________________________
> cfe-commits mailing list
> cfe-commits at cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20100706/b18d80e2/attachment.html>


More information about the cfe-commits mailing list