This is an archive of the discontinued LLVM Phabricator instance.

[clang][dataflow] Add support for nested composite bool expressions
ClosedPublic

Authored by sgatev on Mar 11 2022, 3:54 AM.

Details

Summary

This is part of the implementation of the dataflow analysis framework.
See "[RFC] A dataflow analysis framework for Clang AST" on cfe-dev.

Diff Detail

Event Timeline

sgatev created this revision.Mar 11 2022, 3:54 AM
Herald added a project: Restricted Project. · View Herald TranscriptMar 11 2022, 3:54 AM
sgatev requested review of this revision.Mar 11 2022, 3:54 AM
Herald added a project: Restricted Project. · View Herald TranscriptMar 11 2022, 3:54 AM
ymandel accepted this revision.Mar 11 2022, 5:24 AM
This revision is now accepted and ready to land.Mar 11 2022, 5:24 AM
xazax.hun added inline comments.Mar 11 2022, 9:21 AM
clang/lib/Analysis/FlowSensitive/Transfer.cpp
516

Could you elaborate on when would this happen? I'd expect the traversal to always visit the predecessor basic blocks first and within a basic block always visit subexpressions first. So I'd be quite surprised if there is a subexpression we did not visit.

sgatev updated this revision to Diff 415021.Mar 14 2022, 12:56 AM
sgatev marked an inline comment as done.

Address reviewers' comments.

sgatev added inline comments.Mar 14 2022, 12:58 AM
clang/lib/Analysis/FlowSensitive/Transfer.cpp
516

From what I've seen, logic operators influence the structure of the CFG through additional basic blocks and terminators, but their sub-expression operators are not added directly in the basic blocks.

For example:

void test(bool a, bool b, bool c) {
    bool d = a && (b || c);
}

results in:

void test(bool a, bool b, bool c)
 [B5 (ENTRY)]
   Succs (1): B4

 [B1]
   1: [B4.2] && ([B3.2] || [B2.2])
   2: bool d = a && (b || c);
   Preds (3): B2 B3 B4
   Succs (1): B0

 [B2]
   1: c
   2: [B2.1] (ImplicitCastExpr, LValueToRValue, _Bool)
   Preds (1): B3
   Succs (1): B1

 [B3]
   1: b
   2: [B3.1] (ImplicitCastExpr, LValueToRValue, _Bool)
   T: [B3.2] || ...
   Preds (1): B4
   Succs (2): B1 B2

 [B4]
   1: a
   2: [B4.1] (ImplicitCastExpr, LValueToRValue, _Bool)
   T: [B4.2] && ...
   Preds (1): B5
   Succs (2): B3 B1

 [B0 (EXIT)]
   Preds (1): B1

So, when we evaluate a && (b || c) in B1, the sub-expression b || c has not been evaluated yet. I updated the comment in the code to make that more clear.

xazax.hun added inline comments.Mar 14 2022, 8:19 AM
clang/lib/Analysis/FlowSensitive/Transfer.cpp
516

I understand the structure of the CFG and also understand that certain subexpressions are in different basic blocks. But I still don't understand why would b || c be not evaluated when we evaluate a && (b || c).

The operator && in your example is evaluated in B1. The operator || is evaluated in B3. B3 is a predecessor of B1, so if we process the CFG in reverse-post order, we should visit B3 before B1.

I am pretty sure if the traversal is well-written we should have the || evaluated before we visit B1.

I suspect that something different might be going on. Is it possible that you want to evaluate the && in B4?

Note that && is a terminator there because of the short-circuiting. So at that point we should NOT ask for the value of ||.

sgatev added inline comments.Mar 14 2022, 8:35 AM
clang/lib/Analysis/FlowSensitive/Transfer.cpp
516

The operator || is evaluated in B3.

I don't think that's the case. Similar to your observation about && being a terminator in B4, I believe that || is a terminator in B3.

I interpret B3 as the "a is true" branch. It still doesn't contain information about b and c which might be subject to short-circuiting.

Does that make sense?

xazax.hun accepted this revision.Mar 14 2022, 8:54 AM
xazax.hun added inline comments.
clang/lib/Analysis/FlowSensitive/Transfer.cpp
516

Oh, I see now! Sorry, I somehow assumed Visit is recursive. But you only visit the top level operator not the whole subexpression. Yeah, I understand now, thanks! This looks good to me.

sgatev marked 2 inline comments as done.Mar 14 2022, 9:59 AM