Skip to content

Irreducible control flow graphs #921

@petemud

Description

@petemud
procedure unsound() {
    var x: int;
    assume(x == 0);
    if (true) {
        goto Inside;
    }
    while (x < 10000) {
        Inside: x := x + 1;
    }
    assert(x == -1);
}
$ boogie unsound.bpl /extractLoops
Boogie program verifier finished with 1 verified, 0 errors

History of commits

  1. d8b239c @shazqadeer added code to handle irreducible graphs
  2. 28d46f8 @shazqadeer reverted it on the same day
  3. 64b57a0 @akashlal Support for irreducible graphs (with extractLoops)
  4. b6796ce @shazqadeer finally purged his initial code away from repo

So now the only surviving support for irreducible flow graphs I found is undocumented /extractLoops option

Problem with /extractLoops

Let's say, we have the canonical nonreducible flow graph

graph LR
    A --> B & C
    B --> C
    C --> B
Loading
procedure nonreducible() {
    A: goto B, C;
    B: goto C;
    C: goto B;
}

According to the Dragon Book (9.7.6 Handling Nonreducible Flow Graphs) this flow graph should be transformed into one of

graph LR
    A --> B' & C
    B & B' --> C
    C --> B
Loading
graph LR
    A --> B & C'
    B --> C
    C & C' --> B
Loading
procedure reducible1() {
    A: goto B', C;
    B': goto C;
    B: goto C;
    C: goto B;
}

procedure reducible2() {
    A: goto B, C';
    C': goto B;
    B: goto C;
    C: goto B;
}

Meanwhile /extractLoops just unrolls loop and produces control flow which is not equivalent to original

graph LR
    A --> B & C
    B --> C
    C --> B'1
    B'1 --> C'1
    C'1 --> B'2
    B'2 --> ...
Loading
procedure reducible() {
    A: goto B, C;
    B: goto C;
    C: goto B'1;
    B'1: goto C'1;
    C'1: goto B'2;
    B'2: goto C'2;
    ...
}

Real-world example

https://github.com/open-s4c/libvsync/blob/main/include/vsync/atomic/internal/arm64.h#L551-L564

graph TB
    A[ldx oldv, a
cmp oldv, cmpv]
    B[b.eq oldv]
    W[wfe]
    C[ldx oldv, a
cmp oldv, cmpv]
    D[b.neq oldv]
    E[add new, oldv, v
stx tmp, new, a]
    F[cbnz tmp]
    A --> B
    B --> W & E
    W --> C
    C --> D
    D --> W & E
    E --> F
    F --> C
Loading
procedure await_eq_add() {
    A: goto B;
    B: goto W, E;
    W: goto C;
    C: goto D;
    D: goto W, E;
    E: goto F;
    F: goto C, G;
    G: return;
}

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

We use the standard techniques for converting an irreducible graph into an equivalent, although possibly far larger, reducible graph. We are looking into ways to deal with irreducible graphs that avoid this problem, but so far it has not been an issue.
Barnett, M., Leino, K.R.M. (2005). Weakest-precondition of unstructured programs.

if (!g.Reducible)
{
    throw new VCGenException("Irreducible flow graphs are unsupported.");
}

Barnett, M. (2009). Initial set of files.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions