This is an archive of the discontinued LLVM Phabricator instance.

[analyzer] Do not cache out on some shared implicit AST nodes.
ClosedPublic

Authored by xazax.hun on Dec 11 2019, 11:23 AM.

Details

Summary

Some AST nodes that stands for implicit initialization is shared. The analyzer will do the same evaluation on the same nodes resulting in the same state. The analyzer will "cache out", i.e. it thinks that it visited an already existing node in the exploded graph. This is not true in this case and we lose coverage.

Diff Detail

Event Timeline

xazax.hun created this revision.Dec 11 2019, 11:23 AM
NoQ added a comment.Dec 11 2019, 11:41 AM

Ahaa. Ahaa. Okay.

So the AST is like

|   |-DeclStmt 0x7f9f3b8932e0 <line:6:3, col:43>
|   | `-VarDecl 0x7f9f3b892f50 <col:3, col:42> col:7 used a 'int [5]' cinit
|   |   `-InitListExpr 0x7f9f3b893268 <col:14, col:42> 'int [5]'
|   |     |-array_filler: ImplicitValueInitExpr 0x7f9f3b8932d0 <<invalid sloc>> 'int'
|   |     |-IntegerLiteral 0x7f9f3b893118 <col:41> 'int' 4
|   |     |-ImplicitValueInitExpr 0x7f9f3b8932d0 <<invalid sloc>> 'int'
|   |     |-IntegerLiteral 0x7f9f3b893078 <col:31> 'int' 15
|   |     |-ImplicitValueInitExpr 0x7f9f3b8932d0 <<invalid sloc>> 'int'
|   |     `-IntegerLiteral 0x7f9f3b892fd8 <col:21> 'int' 29

And the CFG is like

[B1]
   1: 4
   2: [B1.4]
   3: 15
   4: /*implicit*/(int)0
   5: 29
   6: {[4] = [B1.5], [2] = [B1.3], [0] = [B1.1]}
   7: int a[5] = {[4] = 29, [2] = 15, [0] = 4};

And you're trying to avoid agglutinating program points for [B1.2] and [B1.4].

Can we simply erase the implicit initializers from the CFG? Under an option, of course. We ignore them in the static analyzer anyway (like all constants).

We could do a custom program point just for this problem (which includes the index as part of its identity), but it's kinda underwhelming to build a whole new program point that always does nothing.

NoQ added a comment.Dec 11 2019, 12:37 PM

One way to criticize the current solution would be to put an InitListExpr in a loop and immediately discard it and see how states after different numbers of iterations no longer join together only because they have different NoCachingOutForConsts values.

Hmm loops. Right. So this solution is insufficient because it can prevent legitimate caching out in loops.

If we remove those initializers from the CFG wouldn't we loose the Pre/PostStmt callbacks? Is that acceptable? If not, we might need to create a new program point :(

NoQ added a comment.Dec 11 2019, 1:32 PM

I doubt we'll ever need these callbacks. I can't imagine any kind of analysis that would need them. Like, these statements have absolutely no effect on the runtime program state.

xazax.hun updated this revision to Diff 233467.Dec 11 2019, 4:04 PM
xazax.hun edited the summary of this revision. (Show Details)
  • Omit nodes from CFG instead of trying to make the states distinct.
xazax.hun updated this revision to Diff 233468.Dec 11 2019, 4:15 PM
  • Removed leftover code from previous approach.
NoQ accepted this revision.Dec 11 2019, 5:00 PM

Thanks!!

clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
1323–1328

We already have a similar unreachable here :p

This revision is now accepted and ready to land.Dec 11 2019, 5:00 PM
This revision was automatically updated to reflect the committed changes.