Skip to content

Commit

Permalink
Expand control-flow graph rendering
Browse files Browse the repository at this point in the history
  • Loading branch information
rocky committed Nov 24, 2024
1 parent 8f072d6 commit e54f172
Show file tree
Hide file tree
Showing 12 changed files with 98 additions and 48 deletions.
39 changes: 34 additions & 5 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ We can get control flow information for this program using::

After running, in ``/tmp`` you'll find some ``.dot`` files and some ``.png`` images generated for the main routine.

``flow-3.8--count-bits.cpython-38-module.png`` is a PNG image for the control flow.
``flow-3.8--count-bits.cpython-38--module.png`` is a PNG image for the control flow.

.. image:: doc-example/flow-3.8--count-bits.cpython-38-module.png
.. image:: doc-example/flow-3.8--count-bits.cpython-38--module.png

Here is what the colors on the arrows indicate:

Expand Down Expand Up @@ -92,14 +92,30 @@ Control-Flow with Dominator Regions

In addition to the basic control flow, we also mark and color boxes with dominator regions.

.. image:: doc-example/flow+dom-3.8--count-bits.cpython-38-module.png
.. image:: doc-example/flow+dom-3.8--count-bits.cpython--38-module.png


Regions with the same nesting level have the same color. So Basic blocks 3 and 7 are at the same nesting level. Blocks 4 and 5 are at the same nesting level and are the same color. However, even though Block 6 is the same color it is not at the same nesting level, although it *is* inside the same dominator region.
Regions with the same nesting level have the same color. So Basic blocks 3 and 7 are at the same nesting level. Blocks 4 and 5 are at the same nesting level and are the same color.

Block 6 has two jumps into it, so it is neither "inside" either blocks 4 or 5. Block 6 is the "join point" block after an if/else::

# block 3
if i % 0:
# block 4
one_bits += 1
else:
# block 5
zero_bits += 1
# join point
i << 1 # This is block 6

The collection of blocks 4, 5, and 6 are all dominated by the block region head Block 3 which has a border around it to show it is the head of a block region.

A border is put around a block _only_ if it dominates some _other_ block. So while technicaly block 4 dominates, itself, and block 5 dominates itself, that fact is not interesting.


Colors get darker as the region is more nested.

Here the additional border indicates that a block is part of some non-trivial dominator region. (A "trivial" dominator region is where the block just dominates itself.)

In addition, if a jump or fallthrough jumps out of its dominator region
the arrowhead of the jump is shown in brown. Note that a jump arrow
Expand All @@ -112,3 +128,16 @@ If any basic block is jumped to using a jump-out (or end scope) kind of edge, th
Inside the block text, we now add the dominator region number for a block in parenthesis. For example, Basic blocks, 4 and 5 are in dominator region 3 and so are marked "(3)" after their basic block number. The dominator number for a basic block is the same as its basic block number. So Basic Block 3 is also Dominator Region 3.

Note that even though basic blocks 4 and 5 are at the same indentation level, they are in different *scopes* under basic block 3.

In this example, all conditional jumps were taken if the condition was false. When the condition is true, we bold the dotted blue arrow. By doing this and by showing the whether the jump condition is true or false, you can see in the control flow whether the source text contains and "and" type of condition or an "or" type of condition.

Here is the graph for ``a and b``::

.. image:: doc-example/flow+dom-3.9-and-lambda:x-y.png


Note the same graph would be the same as ``if a: if b: ...```.

The graph for ``a or b`` is almost the same with the exception of the style of the blue dotted arrow::

.. image:: doc-example/flow+dom-3.9-and-lambda:x-y.png
3 changes: 3 additions & 0 deletions doc-example/and-or.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Example to show the difference in control flow between "and" and "or"
lambda a, b: a or b
lambda x, y: x and y
14 changes: 14 additions & 0 deletions doc-example/count-bits.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
i: int = 6
zero_bits = 0
one_bits = 0
while i > 0: # loop point
# loop alternative
if i % 0:
# first alternative
one_bits += 1
else:
# second alternative
zero_bits += 1
# join point
i << 1
# loop-end join point
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,20 @@ digraph G {

mclimit=1.5;
rankdir=TD; ordering=out;
color="#efefef";
color="#ededed";

node[shape=box style=filled fontsize=10 fontname="DejaVu Sans Mono"
fillcolor="#efefef", width=2];
fillcolor="#ededed", width=2];
edge[fontsize=10 fontname="Verdana"];

# basic blocks:
block_1 [shape = "box", peripheries=2][fontcolor = "black", fillcolor = "#cae1ff"][label="Basic Block 1 (0)\loffset: 0..20 \lflags=entry \lreach_offset=70\l"];
block_2 [shape = "box", peripheries=2][fontcolor = "black", fillcolor = "#bcd2ee"][label="Basic Block 2 (1)\loffset: 22..28 \lflags=loop, conditional jump \ljumps=[66]\lreach_offset=70\l"];
block_3 [shape = "box", peripheries=2][fontcolor = "black", fillcolor = "#a2b5cd"][label="Basic Block 3 (2)\loffset: 30..36 \lflags=conditional jump \ljumps=[48]\lreach_offset=64\l"];
block_4 [fontcolor = "black", fillcolor = "#63b8ff"][label="Basic Block 4 (3)\loffset: 38..46 \lflags=no fallthrough,\l unconditional\ljumps=[56]\lreach_offset=46\l"];
block_5 [fontcolor = "black", fillcolor = "#63b8ff"][label="Basic Block 5 (3)\loffset: 48..54 \lreach_offset=54\l"];
block_6 [color=brown, fontcolor = "black", fillcolor = "#63b8ff"][label="Basic Block 6 (3)\loffset: 56..64 \lflags=no fallthrough, except,\l join block,\l unconditional\ljumps=[22]\lreach_offset=64\l"];
block_7 [fontcolor = "black", fillcolor = "#a2b5cd"][label="Basic Block 7 (2)\loffset: 66..68 \lflags=no fallthrough, return \lreach_offset=68\l"];
block_1 [shape = "box", peripheries=2][fontcolor = "black", fillcolor = "#cae1ff"][label="Basic Block 1 (0), Line 3 \loffset: 0..20 \lflags={entry} \lreach_offset=70\l"];
block_2 [shape = "box", peripheries=2][fontcolor = "black", fillcolor = "#bcd2ee"][label="Basic Block 2 (1), Line 4 \loffset: 22..28 \lflags={loop,\l jump forward if false}\ljumps=[66]\lreach_offset=70\l"];
block_3 [shape = "box", peripheries=2][fontcolor = "black", fillcolor = "#a2b5cd"][label="Basic Block 3 (2), Line 6 \loffset: 30..36 \lflags={jump forward if false} \ljumps=[48]\lreach_offset=64\l"];
block_4 [fontcolor = "black", fillcolor = "#63b8ff"][label="Basic Block 4 (3), Line 8 \loffset: 38..46 \lflags={no fallthrough,\l unconditional}\ljumps=[56]\lreach_offset=46\l"];
block_5 [fontcolor = "black", fillcolor = "#63b8ff"][label="Basic Block 5 (3), Line 11 \loffset: 48..54 \lreach_offset=54\l"];
block_6 [color=brown, fontcolor = "black", fillcolor = "#63b8ff"][label="Basic Block 6 (3), Line 13 \loffset: 56..64 \lflags={no fallthrough, except,\l join block,\l unconditional}\ljumps=[22]\lreach_offset=64\l"];
block_7 [fontcolor = "black", fillcolor = "#a2b5cd"][label="Basic Block 7 (2), Line 13 \loffset: 66..68 \lflags={no fallthrough, return} \lreach_offset=68\l"];

# Edges should be ordered from innermost block edges to outmost.
# If layout gives ugly edge crossing, change the order or the edges
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc-example/flow+dom-3.9-and-lambda:x-y.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc-example/flow+dom-3.9-or-lambda:a-b.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
38 changes: 38 additions & 0 deletions doc-example/flow-3.8-count-bits.cpython-38--module.dot
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
digraph G {
graph[fontsize=10 fontname="DejaVu Sans Mono"];

mclimit=1.5;
rankdir=TD; ordering=out;
color="#ededed";

node[shape=box style=filled fontsize=10 fontname="DejaVu Sans Mono"
fillcolor="#ededed", width=2];
edge[fontsize=10 fontname="Verdana"];

# basic blocks:
block_1 [shape = "box", peripheries=2][label="Basic Block 1, Line 3 \loffset: 0..20 \lflags={entry} \l"];
block_2 [label="Basic Block 2, Line 4 \loffset: 22..28 \lflags={loop,\l jump forward if false}\ljumps=[66]\l"];
block_3 [label="Basic Block 3, Line 6 \loffset: 30..36 \lflags={jump forward if false} \ljumps=[48]\l"];
block_4 [label="Basic Block 4, Line 8 \loffset: 38..46 \lflags={no fallthrough,\l unconditional}\ljumps=[56]\l"];
block_5 [label="Basic Block 5, Line 11 \loffset: 48..54 \l"];
block_6 [label="Basic Block 6, Line 13 \loffset: 56..64 \lflags={no fallthrough, except,\l unconditional}\ljumps=[22]\l"];
block_7 [label="Basic Block 7, Line 13 \loffset: 66..68 \lflags={no fallthrough, return} \l"];

# Edges should be ordered from innermost block edges to outmost.
# If layout gives ugly edge crossing, change the order or the edges
# and/or add port directions on nodes For example:
# block_1:sw -> block_4:nw or
# block_0 -> block_3:ne
# See https://stackoverflow.com/questions/53468814/how-can-i-influence-graphviz-dot-to-prefer-which-edges-can-cross/53472852#53472852

block_6:nw -> block_2:sw [weight=1][color="#006400"];
block_6 -> block_7 [weight=10][style="dashed"] [arrowhead="none"];
block_5 -> block_6 [weight=10][color="red"][style="dashed"];
block_4 -> block_5 [weight=10][style="dashed"] [arrowhead="none"];
block_4 -> block_6 [weight=1];
block_3 -> block_4 [weight=10][color="red"][style="dashed"];
block_3 -> block_5 [weight=1][color="blue"][style="dotted"];
block_2 -> block_3 [weight=10][color="red"][style="dashed"];
block_2:se -> block_7:ne [weight=1][color="MediumBlue"][style="dotted"];
block_1 -> block_2 [weight=10][color="red"][style="dashed"];
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 0 additions & 34 deletions doc-example/flow-dom-3.8--count-bits.cpython-38-module.dot

This file was deleted.

0 comments on commit e54f172

Please sign in to comment.