a.sidorin updated this revision to Diff 142226.
a.sidorin added a comment.

Rewrite the GCCAsmStmt in the CFG.


Repository:
  rC Clang

https://reviews.llvm.org/D45416

Files:
  include/clang/Analysis/CFG.h
  lib/Analysis/CFG.cpp
  lib/StaticAnalyzer/Core/ExprEngine.cpp
  test/Analysis/asm.cpp

Index: test/Analysis/asm.cpp
===================================================================
--- /dev/null
+++ test/Analysis/asm.cpp
@@ -0,0 +1,12 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker debug.ExprInspection -fheinous-gnu-extensions -w %s -verify
+
+int clang_analyzer_eval(int);
+
+int global;
+void testRValueOutput() {
+  int &ref = global;
+  ref = 1;
+  __asm__("" : "=r"((int)global));  // don't crash on rvalue output operand
+  clang_analyzer_eval(global == 1); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(ref == 1);    // expected-warning{{UNKNOWN}}
+}
Index: lib/StaticAnalyzer/Core/ExprEngine.cpp
===================================================================
--- lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -3071,13 +3071,14 @@
   // outputs.
 
   ProgramStateRef state = Pred->getState();
+  const auto *LCtx = Pred->getLocationContext();
 
   for (const Expr *O : A->outputs()) {
-    SVal X = state->getSVal(O, Pred->getLocationContext());
+    SVal X = state->getSVal(O, LCtx);
     assert(!X.getAs<NonLoc>());  // Should be an Lval, or unknown, undef.
 
     if (Optional<Loc> LV = X.getAs<Loc>())
-      state = state->bindLoc(*LV, UnknownVal(), Pred->getLocationContext());
+      state = state->bindLoc(*LV, UnknownVal(), LCtx);
   }
 
   Bldr.generateNode(A, Pred, state);
Index: lib/Analysis/CFG.cpp
===================================================================
--- lib/Analysis/CFG.cpp
+++ lib/Analysis/CFG.cpp
@@ -548,6 +548,7 @@
   CFGBlock *VisitDoStmt(DoStmt *D);
   CFGBlock *VisitExprWithCleanups(ExprWithCleanups *E, AddStmtChoice asc);
   CFGBlock *VisitForStmt(ForStmt *F);
+  CFGBlock *VisitGCCAsmStmt(GCCAsmStmt *GCCAsmS, AddStmtChoice asc);
   CFGBlock *VisitGotoStmt(GotoStmt *G);
   CFGBlock *VisitIfStmt(IfStmt *I);
   CFGBlock *VisitImplicitCastExpr(ImplicitCastExpr *E, AddStmtChoice asc);
@@ -587,6 +588,16 @@
   CFGBlock *VisitChildren(Stmt *S);
   CFGBlock *VisitNoRecurse(Expr *E, AddStmtChoice asc);
 
+  GCCAsmStmt *getOrCreateGCCAsmStmtWithoutGnuExtensions(GCCAsmStmt *GCCAsmS);
+
+  template <typename StmtTy> void *allocateMemForStmt() {
+    // Get the alignment of the new Stmt, padding out to >=8 bytes.
+    unsigned Align = std::max(alignof(StmtTy), static_cast<size_t>(8));
+    // Allocate a new Stmt using the BumpPtrAllocator. It will get
+    // automatically freed with the CFG.
+    return cfg->getAllocator().Allocate(sizeof(StmtTy), Align);
+  }
+
   void maybeAddScopeBeginForVarDecl(CFGBlock *B, const VarDecl *VD,
                                     const Stmt *S) {
     if (ScopePos && (VD == ScopePos.getFirstVarInScope()))
@@ -2039,6 +2050,9 @@
     case Stmt::ForStmtClass:
       return VisitForStmt(cast<ForStmt>(S));
 
+    case Stmt::GCCAsmStmtClass:
+      return VisitGCCAsmStmt(cast<GCCAsmStmt>(S), asc);
+
     case Stmt::GotoStmtClass:
       return VisitGotoStmt(cast<GotoStmt>(S));
 
@@ -2571,14 +2585,9 @@
   for (DeclStmt::reverse_decl_iterator I = DS->decl_rbegin(),
                                        E = DS->decl_rend();
        I != E; ++I) {
-    // Get the alignment of the new DeclStmt, padding out to >=8 bytes.
-    unsigned A = alignof(DeclStmt) < 8 ? 8 : alignof(DeclStmt);
-
-    // Allocate the DeclStmt using the BumpPtrAllocator.  It will get
-    // automatically freed with the CFG.
     DeclGroupRef DG(*I);
     Decl *D = *I;
-    void *Mem = cfg->getAllocator().Allocate(sizeof(DeclStmt), A);
+    void *Mem = allocateMemForStmt<DeclStmt>();
     DeclStmt *DSNew = new (Mem) DeclStmt(DG, D->getLocation(), GetEndLoc(D));
     cfg->addSyntheticDeclStmt(DSNew, DS);
 
@@ -3009,7 +3018,58 @@
   }
   return LastBlock;
 }
-  
+
+GCCAsmStmt *
+CFGBuilder::getOrCreateGCCAsmStmtWithoutGnuExtensions(GCCAsmStmt *S) {
+  // GCC asm syntax allows using no-op-like casts as outputs. While GCC treats
+  // them as lvalues, clang builds an LValueToRValue cast.
+  // We are going to re-create the GCCAsmStmt if this happens.
+  if (std::find_if(S->begin_outputs(), S->end_outputs(), [this](const Expr *E) {
+        return E != E->IgnoreParenNoopCasts(*Context);
+      }) == S->end_outputs())
+    return S;
+
+  SmallVector<IdentifierInfo *, 4> Names;
+  for (unsigned I = 0, E = S->getNumOutputs(); I != E; I++)
+    Names.push_back(S->getOutputIdentifier(I));
+
+  for (unsigned I = 0, E = S->getNumInputs(); I != E; I++)
+    Names.push_back(S->getInputIdentifier(I));
+
+  SmallVector<StringLiteral *, 4> Clobbers;
+  for (unsigned I = 0, E = S->getNumClobbers(); I != E; I++)
+    Clobbers.push_back(S->getClobberStringLiteral(I));
+
+  SmallVector<StringLiteral *, 4> Constraints;
+  for (unsigned I = 0, E = S->getNumOutputs(); I != E; I++)
+    Constraints.push_back(S->getOutputConstraintLiteral(I));
+
+  for (unsigned I = 0, E = S->getNumInputs(); I != E; I++)
+    Constraints.push_back(S->getInputConstraintLiteral(I));
+
+  SmallVector<Expr *, 4> Exprs(S->getNumOutputs() + S->getNumInputs());
+  std::transform(
+      S->begin_outputs(), S->end_outputs(), Exprs.begin(),
+      [this](Expr *E) -> Expr * { return E->IgnoreParenNoopCasts(*Context); });
+
+  std::copy(S->begin_inputs(), S->end_inputs(),
+            Exprs.begin() + S->getNumOutputs());
+
+  void *Mem = allocateMemForStmt<GCCAsmStmt>();
+  GCCAsmStmt *CleanGCCAsmStmt = new (Mem) GCCAsmStmt(
+      *Context, S->getAsmLoc(), S->isSimple(), S->isVolatile(),
+      S->getNumOutputs(), S->getNumInputs(), Names.data(), Constraints.data(),
+      Exprs.data(), S->getAsmString(), S->getNumClobbers(), Clobbers.data(),
+      S->getRParenLoc());
+  cfg->addSyntheticStmt(CleanGCCAsmStmt, S);
+  return CleanGCCAsmStmt;
+}
+
+CFGBlock *CFGBuilder::VisitGCCAsmStmt(GCCAsmStmt *GCCAsmS, AddStmtChoice asc) {
+  GCCAsmS = getOrCreateGCCAsmStmtWithoutGnuExtensions(GCCAsmS);
+  return VisitStmt(GCCAsmS, asc);
+}
+
 CFGBlock *CFGBuilder::VisitGotoStmt(GotoStmt *G) {
   // Goto is a control-flow statement.  Thus we stop processing the current
   // block and create a new one.
Index: include/clang/Analysis/CFG.h
===================================================================
--- include/clang/Analysis/CFG.h
+++ include/clang/Analysis/CFG.h
@@ -1117,27 +1117,32 @@
   void addSyntheticDeclStmt(const DeclStmt *Synthetic,
                             const DeclStmt *Source) {
     assert(Synthetic->isSingleDecl() && "Can handle single declarations only");
-    assert(Synthetic != Source && "Don't include original DeclStmts in map");
-    assert(!SyntheticDeclStmts.count(Synthetic) && "Already in map");
-    SyntheticDeclStmts[Synthetic] = Source;
+    addSyntheticStmt(Synthetic, Source);
+  }
+
+  /// Records a synthetic Stmt and the Stmt it was constructed from.
+  void addSyntheticStmt(const Stmt *Synthetic, const Stmt *Source) {
+    assert(Synthetic != Source && "Don't include original Stmts in map");
+    assert(!SyntheticStmts.count(Synthetic) && "Already in map");
+    SyntheticStmts[Synthetic] = Source;
   }
 
   using synthetic_stmt_iterator =
-      llvm::DenseMap<const DeclStmt *, const DeclStmt *>::const_iterator;
+      llvm::DenseMap<const Stmt *, const Stmt *>::const_iterator;
   using synthetic_stmt_range = llvm::iterator_range<synthetic_stmt_iterator>;
 
   /// Iterates over synthetic DeclStmts in the CFG.
   ///
   /// Each element is a (synthetic statement, source statement) pair.
   ///
   /// \sa addSyntheticDeclStmt
   synthetic_stmt_iterator synthetic_stmt_begin() const {
-    return SyntheticDeclStmts.begin();
+    return SyntheticStmts.begin();
   }
 
   /// \sa synthetic_stmt_begin
   synthetic_stmt_iterator synthetic_stmt_end() const {
-    return SyntheticDeclStmts.end();
+    return SyntheticStmts.end();
   }
 
   /// \sa synthetic_stmt_begin
@@ -1213,7 +1218,7 @@
 
   /// Collects DeclStmts synthesized for this CFG and maps each one back to its
   /// source DeclStmt.
-  llvm::DenseMap<const DeclStmt *, const DeclStmt *> SyntheticDeclStmts;
+  llvm::DenseMap<const Stmt *, const Stmt *> SyntheticStmts;
 };
 
 } // namespace clang
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to