[PATCH] D45416: [analyzer] ExprEngine: model GCC inline asm rvalue cast outputs

2018-04-27 Thread Richard Smith - zygoloid via Phabricator via cfe-commits
rsmith added inline comments.



Comment at: test/Analysis/asm.cpp:9
+  ref = 1;
+  __asm__("" : "=r"((int)global));  // don't crash on rvalue output operand
+  clang_analyzer_eval(global == 1); // expected-warning{{UNKNOWN}}

Ugh, do we really need to support this nonsense? :(

If so, it'd seem reasonable to me to do the cleanup when building the AST 
rather than in the CFG builder (that is, convert the LValueToRValue cast here 
to a NoOp lvalue cast node, since that matches its actual semantics).


Repository:
  rC Clang

https://reviews.llvm.org/D45416



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D45416: [analyzer] ExprEngine: model GCC inline asm rvalue cast outputs

2018-04-27 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ accepted this revision.
NoQ added a subscriber: rsmith.
NoQ added a comment.

Woohoo LGTM.

Heads up to @rsmith because we're about to break the CFG again, and also yay 
we've found another use case for rewriting AST in our CFG.


Repository:
  rC Clang

https://reviews.llvm.org/D45416



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D45416: [analyzer] ExprEngine: model GCC inline asm rvalue cast outputs

2018-04-25 Thread Aleksei Sidorin via Phabricator via cfe-commits
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  = 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());  // 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: 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), 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(S));
 
+case Stmt::GCCAsmStmtClass:
+  return 

[PATCH] D45416: [analyzer] ExprEngine: model GCC inline asm rvalue cast outputs

2018-04-16 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added a comment.

(also we'll need CFG dump tests)


Repository:
  rC Clang

https://reviews.llvm.org/D45416



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D45416: [analyzer] ExprEngine: model GCC inline asm rvalue cast outputs

2018-04-13 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added a comment.

(i'd much rather do the latter)


Repository:
  rC Clang

https://reviews.llvm.org/D45416



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D45416: [analyzer] ExprEngine: model GCC inline asm rvalue cast outputs

2018-04-13 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ accepted this revision.
NoQ added a comment.

Wow, you actually did that.

Ok, now we can decide if we want this to be analyzer-only (with a 
`CFG::BuildOptions` flag) or get someone else to have a look at that as a 
global CFG change (i.e. it may potentially affect compiler warnings). Or at 
least make sure to run all clang tests :)


Repository:
  rC Clang

https://reviews.llvm.org/D45416



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D45416: [analyzer] ExprEngine: model GCC inline asm rvalue cast outputs

2018-04-12 Thread Aleksei Sidorin via Phabricator via cfe-commits
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  = 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());  // 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: 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++)
+  

[PATCH] D45416: [analyzer] ExprEngine: model GCC inline asm rvalue cast outputs

2018-04-10 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added a comment.

I mean, like, if we try to work with the existing AST then we're stuck with a 
prvalue expression that represents an lvalue and will be assigned a `Loc` 
value, which is pretty weird anyway. Getting rid of the ParentMap in favor of 
providing enough context (eg. in the CFG or in checker callbacks) whenever we 
might want to use it sounds like a much saner solution. There might be other 
"unknown unknowns" about rewriting the AST, but our current problem looks as 
safe as we'll ever get.


Repository:
  rC Clang

https://reviews.llvm.org/D45416



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D45416: [analyzer] ExprEngine: model GCC inline asm rvalue cast outputs

2018-04-10 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ added a comment.

In https://reviews.llvm.org/D45416#1062901, @a.sidorin wrote:

> > The ultimate solution would probably be to add a fake cloned asm statement 
> > to the CFG (instead of the real asm statement) that would point to the 
> > correct output child-expression(s) that are untouched themselves but simply 
> > have their noop casts removed.
>
> I have some concerns about this solution. It will result in difference 
> between AST nodes and nodes that user will receive during analysis and I'm 
> not sure that it is good. What is even worse here is that a lot of AST stuff 
> won't work properly - ParentMap, for example.


Yep. But i'd rather avoid using the `ParentMap`. Also we already do this for 
`DeclStmt`s that declare more than one `VarDecl` (split them up into 
single-decl statements), and the practical effect of such simplification is 
barely noticeable even though `DeclStmt`s are so much more common.

>> Or we could try
> 
> Looks like something is missed here :)

Whoops sry nvm. And and and mmm you didn't include the diff context :p

In https://reviews.llvm.org/D45416#1063366, @a.sidorin wrote:

> Maybe we should just remove the condition and leave a FIXME?


The fix is already there and it's valid and it makes things better overall, why 
not keep it around. We still need a FIXME though.


Repository:
  rC Clang

https://reviews.llvm.org/D45416



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D45416: [analyzer] ExprEngine: model GCC inline asm rvalue cast outputs

2018-04-10 Thread Aleksei Sidorin via Phabricator via cfe-commits
a.sidorin added a comment.

Maybe we should just remove the condition and leave a FIXME?


Repository:
  rC Clang

https://reviews.llvm.org/D45416



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D45416: [analyzer] ExprEngine: model GCC inline asm rvalue cast outputs

2018-04-10 Thread Aleksei Sidorin via Phabricator via cfe-commits
a.sidorin added a comment.

> The ultimate solution would probably be to add a fake cloned asm statement to 
> the CFG (instead of the real asm statement) that would point to the correct 
> output child-expression(s) that are untouched themselves but simply have 
> their noop casts removed.

I have some concerns about this solution. It will result in difference between 
AST nodes and nodes that user will receive during analysis and I'm not sure 
that it is good. What is even worse here is that a lot of AST stuff won't work 
properly - ParentMap, for example.

> Or we could try

Looks like something is missed here :)


Repository:
  rC Clang

https://reviews.llvm.org/D45416



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D45416: [analyzer] ExprEngine: model GCC inline asm rvalue cast outputs

2018-04-09 Thread Artem Dergachev via Phabricator via cfe-commits
NoQ accepted this revision.
NoQ added a comment.

Thanks!

Eww. Weird AST.

I wonder how this should work:

  // RUN: %clang_analyze_cc1 -analyzer-checker debug.ExprInspection 
-fheinous-gnu-extensions -w %s -verify
  
  int clang_analyzer_eval(int);
  
  int global;
  void testRValueOutput() {
int  = global;
ref = 1;
__asm__("" : "=r"((int)ref));
clang_analyzer_eval(ref == 1); // currently says UNKNOWN
clang_analyzer_eval(global == 1); // currently says TRUE
  }

The ultimate solution would probably be to add a fake cloned asm statement to 
the CFG (instead of the real asm statement) that would point to the correct 
output child-expression(s) that are untouched themselves but simply have their 
noop casts removed.

Or we could try


Repository:
  rC Clang

https://reviews.llvm.org/D45416



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D45416: [analyzer] ExprEngine: model GCC inline asm rvalue cast outputs

2018-04-09 Thread George Karpenkov via Phabricator via cfe-commits
george.karpenkov accepted this revision.
george.karpenkov added a comment.
This revision is now accepted and ready to land.

Right, sorry. LGTM, but maybe Artem has something to add as well.
I don't have any suggestions, and trying to modifying lifetimes of expressions 
in environment for the sake of a GCC extensions seems suboptimal as well.


Repository:
  rC Clang

https://reviews.llvm.org/D45416



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D45416: [analyzer] ExprEngine: model GCC inline asm rvalue cast outputs

2018-04-09 Thread Aleksei Sidorin via Phabricator via cfe-commits
a.sidorin added inline comments.



Comment at: lib/StaticAnalyzer/Core/ExprEngine.cpp:3082
+if (X.isUnknown()) {
+  // The value being casted to rvalue can be garbage-collected after
+  // the cast is modeled. Try to recover the memory region being casted

george.karpenkov wrote:
> From my understanding, the code inside the if-block is not tested below
> 
It is tested: without this code, "TRUE" will be printed. The reason is that the 
lvalue of 'global' is removed from Environment after the cast happens so `X` 
becomes Unknown.  But the way of retrieving the value is definitely not the 
best so if you have any suggestions on improvement - they are welcome.


Repository:
  rC Clang

https://reviews.llvm.org/D45416



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D45416: [analyzer] ExprEngine: model GCC inline asm rvalue cast outputs

2018-04-09 Thread George Karpenkov via Phabricator via cfe-commits
george.karpenkov requested changes to this revision.
george.karpenkov added inline comments.
This revision now requires changes to proceed.



Comment at: lib/StaticAnalyzer/Core/ExprEngine.cpp:3082
+if (X.isUnknown()) {
+  // The value being casted to rvalue can be garbage-collected after
+  // the cast is modeled. Try to recover the memory region being casted

From my understanding, the code inside the if-block is not tested below



Repository:
  rC Clang

https://reviews.llvm.org/D45416



___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[PATCH] D45416: [analyzer] ExprEngine: model GCC inline asm rvalue cast outputs

2018-04-08 Thread Aleksei Sidorin via Phabricator via cfe-commits
a.sidorin created this revision.
a.sidorin added reviewers: NoQ, dcoughlin, xazax.hun.
Herald added subscribers: cfe-commits, rnkovacs, szepet.
Herald added a reviewer: george.karpenkov.

Despite the fact that cast expressions return rvalues, GCC still handles  such 
outputs as lvalues when compiling inline assembler. Such code causes assertion 
failure in ExprEngine::VisitGCCAsmStmt. In this commit, we treat such rvalue 
outputs in the way similar to how CGStmt does it.


Repository:
  rC Clang

https://reviews.llvm.org/D45416

Files:
  lib/StaticAnalyzer/Core/ExprEngine.cpp
  test/Analysis/asm.c


Index: test/Analysis/asm.c
===
--- /dev/null
+++ test/Analysis/asm.c
@@ -0,0 +1,10 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker debug.ExprInspection 
-fheinous-gnu-extensions -w %s -verify
+
+int clang_analyzer_eval(int);
+
+int global;
+void testRValueOutput() {
+  global = 1;
+  __asm__("" : "=r"((int)global));  // don't crash on rvalue output operand
+  clang_analyzer_eval(global == 1); // expected-warning{{UNKNOWN}}
+}
Index: lib/StaticAnalyzer/Core/ExprEngine.cpp
===
--- lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -3071,9 +3071,25 @@
   // outputs.
 
   ProgramStateRef state = Pred->getState();
+  const auto *LCtx = Pred->getLocationContext();
 
   for (const Expr *O : A->outputs()) {
-SVal X = state->getSVal(O, Pred->getLocationContext());
+// NOTE: GCC inline asm supports rvalue no-op-like casts as output
+// arguments (-fheinous-gnu-extensions).
+const Expr *LValueOutputE = O->IgnoreParenNoopCasts(getContext());
+SVal X = state->getSVal(LValueOutputE, LCtx);
+if (X.isUnknown()) {
+  // The value being casted to rvalue can be garbage-collected after
+  // the cast is modeled. Try to recover the memory region being casted
+  // if possible.
+  if (const auto *DRE = dyn_cast(LValueOutputE)) {
+if (const auto *VD = dyn_cast(DRE->getDecl())) {
+  const VarRegion *VR =
+  getSValBuilder().getRegionManager().getVarRegion(VD, LCtx);
+  X = loc::MemRegionVal(VR);
+}
+  }
+}
 assert(!X.getAs());  // Should be an Lval, or unknown, undef.
 
 if (Optional LV = X.getAs())


Index: test/Analysis/asm.c
===
--- /dev/null
+++ test/Analysis/asm.c
@@ -0,0 +1,10 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker debug.ExprInspection -fheinous-gnu-extensions -w %s -verify
+
+int clang_analyzer_eval(int);
+
+int global;
+void testRValueOutput() {
+  global = 1;
+  __asm__("" : "=r"((int)global));  // don't crash on rvalue output operand
+  clang_analyzer_eval(global == 1); // expected-warning{{UNKNOWN}}
+}
Index: lib/StaticAnalyzer/Core/ExprEngine.cpp
===
--- lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -3071,9 +3071,25 @@
   // outputs.
 
   ProgramStateRef state = Pred->getState();
+  const auto *LCtx = Pred->getLocationContext();
 
   for (const Expr *O : A->outputs()) {
-SVal X = state->getSVal(O, Pred->getLocationContext());
+// NOTE: GCC inline asm supports rvalue no-op-like casts as output
+// arguments (-fheinous-gnu-extensions).
+const Expr *LValueOutputE = O->IgnoreParenNoopCasts(getContext());
+SVal X = state->getSVal(LValueOutputE, LCtx);
+if (X.isUnknown()) {
+  // The value being casted to rvalue can be garbage-collected after
+  // the cast is modeled. Try to recover the memory region being casted
+  // if possible.
+  if (const auto *DRE = dyn_cast(LValueOutputE)) {
+if (const auto *VD = dyn_cast(DRE->getDecl())) {
+  const VarRegion *VR =
+  getSValBuilder().getRegionManager().getVarRegion(VD, LCtx);
+  X = loc::MemRegionVal(VR);
+}
+  }
+}
 assert(!X.getAs());  // Should be an Lval, or unknown, undef.
 
 if (Optional LV = X.getAs())
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits