a.sidorin updated this revision to Diff 143959.
a.sidorin added a comment.
Add a test for CFG dump; replace static_cast with an initialization.
No test failures on check-all were observed.
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
test/Analysis/cfg.cpp
Index: test/Analysis/cfg.cpp
===================================================================
--- test/Analysis/cfg.cpp
+++ test/Analysis/cfg.cpp
@@ -1,6 +1,6 @@
-// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -triple x86_64-apple-darwin12 -analyzer-config cfg-temporary-dtors=true -std=c++11 -analyzer-config cfg-rich-constructors=false %s > %t 2>&1
+// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -triple x86_64-apple-darwin12 -fheinous-gnu-extensions -analyzer-config cfg-temporary-dtors=true -std=c++11 -analyzer-config cfg-rich-constructors=false %s > %t 2>&1
// RUN: FileCheck --input-file=%t -check-prefixes=CHECK,WARNINGS %s
-// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -triple x86_64-apple-darwin12 -analyzer-config cfg-temporary-dtors=true -std=c++11 -analyzer-config cfg-rich-constructors=true %s > %t 2>&1
+// RUN: %clang_analyze_cc1 -analyzer-checker=debug.DumpCFG -triple x86_64-apple-darwin12 -fheinous-gnu-extensions -analyzer-config cfg-temporary-dtors=true -std=c++11 -analyzer-config cfg-rich-constructors=true %s > %t 2>&1
// RUN: FileCheck --input-file=%t -check-prefixes=CHECK,ANALYZER %s
// This file tests how we construct two different flavors of the Clang CFG -
@@ -84,6 +84,23 @@
static_assert(1, "abc");
}
+
+// CHECK-LABEL: void checkGCCAsmRValueOutput()
+// CHECK: [B2 (ENTRY)]
+// CHECK-NEXT: Succs (1): B1
+// CHECK: [B1]
+// CHECK-NEXT: 1: int arg
+// CHECK-NEXT: 2: arg
+// CHECK-NEXT: 3: asm ("" : "=r" ([B1.2]));
+// CHECK-NEXT: 4: arg
+// CHECK-NEXT: 5: asm ("" : "=r" ([B1.4]));
+void checkGCCAsmRValueOutput() {
+ int arg;
+ __asm__("" : "=r"((int)arg)); // rvalue output operand
+ __asm__("" : "=r"(arg)); // lvalue output operand
+}
+
+
// CHECK-LABEL: void F(EmptyE e)
// CHECK: ENTRY
// CHECK-NEXT: Succs (1): B1
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
@@ -3058,13 +3058,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), 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));
@@ -2578,14 +2592,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);
@@ -3016,7 +3025,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
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits