-
Notifications
You must be signed in to change notification settings - Fork 112
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Irreducible control flow graphs #921
Comments
Thanks for bringing this issue to our attention. I do not remember now why support for them was dropped. Irreducible graphs have not surfaced before in Boogie applications so we never considered adding support for them. Do you have any concrete application where you need them? |
Yes. See "Real-world example" from above. I'm using boogie to prove properties of libvsync atomics, which are implemented in ARM assembly |
ok, I will look into it. But it will take me some time. Do you think you can make progress by transforming to irreducible CFGs on your side? I assume you have a toolchain for generating Boogie and you can do the transformation in that toolchain. |
I don't actually have any toolchain. The project is small. Part of it is hand-written Boogie model for ARM and part is a parser from assembly. I use Boogie precisely because I need to deal with control flow. I was first doing everything in Coq and then SMT-LIB directly, but there dealing with control flow is too cumbersome. Your "Weakest-precondition of unstructured programs" paper looked promising, so I switched to Boogie. Irreducibility shouldn't be a problem really. The transformations Boogie makes are:
Both of these don't require irreducibility. Irreducibility just ensures that there is only one loop header, so you can find where to apply 1 and 2. In irreducible graph - with more than one loop entry - you can just use the block that comes first in code as loop header. |
Reducibility is a requirement for the back-edge elimination to be sound (that is, each loop must have at most one entry point). This example shows why irreducibility leads to unsoundness in general:
Here, B2 and B4 are two entry points of the same loop and thus the resulting CFG is irreducible. This program is not correct and thus should not be verified by Boogie, because the execution path B4 -> B2 -> B3 leads to failure if Suppose we choose to eliminate the back edge from B4 to B2 (instead of B3 to B4). Then, the resulting program is correct (and would be verified by Boogie), which shows the unsoundness. One correct way of verifying this program would be to first turn the CFG into a reducible CFG with the same executions and to then do the back-edge elimination.
Interesting. We made a subset of Boogie proof-producing using Isabelle a while ago (see https://link.springer.com/chapter/10.1007/978-3-030-81688-9_33), which included formally justifying the back-edge elimination (in our approach, a proof is produced on every run instead of proving the approach once and for all, which simplies things). It would be interesting to know how your proof compares to ours. |
I studied this issue more thoroughly. It appears to me that there are two separate issues going on:
|
@gauravpartha You are absolutely correct. I was wrong about cutting back edge in irreducible graph, because it also cuts a forward edge in corresponding reducible graph. graph LR
A["`*A:*`"]
B["`*B:* x := 1`"]
C["`*C:*`"]
D["`*D:* **assert** x == 1`"]
E["`*E:*`"]
F["`*F:*`"]
A --> B & E
B --> C
C --> D
D --> E
%%E -. blocks between E to C .-> C
E --> F
F -.-> C
graph LR
A["`*A:*`"]
B["`*B:* x := 1`"]
C["`*C:*`"]
D["`*D:* **assert** x == 1`"]
E["`*E:*`"]; E'["`*E':*`"]
F["`*F:*`"]; F'["`*F':*`"]
A --> B & E'
B --> C
C --> D
D --> E
%%E -.blocks between E to C.-> C
%%E' -.blocks between E to C.-> C
E --> F
F -.-> C
E' --> F'
F' -.-> C
I still think - not sure if it's advantageous though - that this issue can be worked around without copying any blocks by:
graph LR
A["`*A:*`"]
B["`*B:* x := 1`"]
C["`*C:*`"]
D["`*D:* **assert** x == 1`"]
E["`*E:*`"]
F["`*F:*`"]
A --> B & E
B -.-> C
C --> D
D --> E
%%E -.blocks between E to C.-> C
%%E' -.blocks between E to C.-> C
E --> F
F -.-> C
|
Even if an automatic method for converting an irreducible CFG to a reducible CFG could be implemented, I don't understand how it would address the problem of specifying the appropriate loop invariant needed for verifying the loop. For well-structured (or reducible) loops, it is clear to the programmer where to put the loop invariant and this information is exploited by Boogie to generate the appropriate verification conditions. Any suggestions? |
Invariant always comes in loop header. Here are a couple of suggestions for which one of two loop headers to use:
|
History of commits
added code to handle irreducible graphs
Support for irreducible graphs (with extractLoops)
So now the only surviving support for irreducible flow graphs I found is undocumented
/extractLoops
optionProblem with
/extractLoops
Let's say, we have the canonical nonreducible flow graph
According to the Dragon Book (9.7.6 Handling Nonreducible Flow Graphs) this flow graph should be transformed into one of
Meanwhile
/extractLoops
just unrolls loop and produces control flow which is not equivalent to originalReal-world example
https://github.com/open-s4c/libvsync/blob/main/include/vsync/atomic/internal/arm64.h#L551-L564
Conclusions
Are there any unsolvable problems with properly handling irreducible flow graphs, that it was removed by @shazqadeer 28d46f8? Can it be brought back or are there other ways of dealing with this issue in Boogie?
Quotes
The text was updated successfully, but these errors were encountered: