Index: include/clang/Analysis/CFG.h =================================================================== --- include/clang/Analysis/CFG.h +++ include/clang/Analysis/CFG.h @@ -1117,13 +1117,18 @@ 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_iterator; + llvm::DenseMap::const_iterator; using synthetic_stmt_range = llvm::iterator_range; /// Iterates over synthetic DeclStmts in the CFG. @@ -1132,12 +1137,12 @@ /// /// \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 SyntheticDeclStmts; + llvm::DenseMap SyntheticStmts; }; } // namespace clang 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 void *allocateMemForStmt() { + // Get the alignment of the new Stmt, padding out to >=8 bytes. + unsigned Align = std::max(alignof(StmtTy), static_cast(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(S)); + case Stmt::GCCAsmStmtClass: + return VisitGCCAsmStmt(cast(S), asc); + case Stmt::GotoStmtClass: return VisitGotoStmt(cast(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 *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 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 Clobbers; + for (unsigned I = 0, E = S->getNumClobbers(); I != E; I++) + Clobbers.push_back(S->getClobberStringLiteral(I)); + + SmallVector 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 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 *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: 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()); // Should be an Lval, or unknown, undef. if (Optional LV = X.getAs()) - state = state->bindLoc(*LV, UnknownVal(), Pred->getLocationContext()); + state = state->bindLoc(*LV, UnknownVal(), LCtx); } Bldr.generateNode(A, Pred, state); 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}} +}