TC-S Static Single Assignment Samples
Note
SSA form requires all temporaries to be statically defined once. After renaming, a number is added at the end of each temporary. When there is no branching path, this renaming method is analogous to the one described in TC-R, Unique Identifiers.
let
var a := 1
var b := 2
in
a + b
end
$ tc -eL two_variables.tig
/* == Low Level Intermediate representation. == */
# Routine: _main
label main
# Prologue
# Body
seq
label l2
move
temp t0
const 1
move
temp t1
const 2
sxp
binop add
temp t0
temp t1
label l0
seq end
# Epilogue
label end
$ echo $?
0
$ tc -e --ssa -L two_variables.tig
/* == Low Level Intermediate representation. == */
# Routine: _main
label main
# Prologue
# Body
seq
label l2
move
temp t1.0
const 0
move
temp t0.0
const 0
move
temp t0.1
const 1
move
temp t1.1
const 2
sxp
binop add
temp t0.1
temp t1.1
label l0
seq end
# Epilogue
label end
$ echo $?
0
Note
One of the important things to notice is the apparition of a new definition
for each temporary: tx.0
.
These new definitions are here to account for variables defined on branching
paths, where they might be used before their assignment.
let
var a := 1
in
a := 42
end
$ tc -eL redefinition.tig
/* == Low Level Intermediate representation. == */
# Routine: _main
label main
# Prologue
# Body
seq
label l2
move
temp t0
const 1
move
temp t0
const 42
label l0
seq end
# Epilogue
label end
$ echo $?
0
$ tc -e --ssa -L redefinition.tig
/* == Low Level Intermediate representation. == */
# Routine: _main
label main
# Prologue
# Body
seq
label l2
move
temp t0.0
const 0
move
temp t0.1
const 1
move
temp t0.2
const 42
label l0
seq end
# Epilogue
label end
$ echo $?
0
Now we can check programs with branching paths.
Warning
As SSA form is an abstract form, it cannot be visualized directly in the LIR output. This is why we have 4 steps to output:
LIR before SSA,
BasicBlock Flow Graph in SSA form,
abstract Phi-Nodes visualization in SSA form,
from SSA form back to LIR.
let
var a := 1
in
print_int(if 1 then a else a + 1)
end
$ tc -eL if.tig
/* == Low Level Intermediate representation. == */
# Routine: _main
label main
# Prologue
# Body
seq
label l5
move
temp t0
const 1
cjump ne
const 1
const 0
name l0
name l1
label l1
move
temp t1
binop add
temp t0
const 1
label l2
sxp
call
name print_int
temp t1
call end
jump
name l3
label l0
move
temp t1
temp t0
jump
name l2
label l3
seq end
# Epilogue
label end
$ echo $?
0
$ tc -e --rename-variables --bbflowgraph-dump if.tig
$ echo $?
0
![/* Graph Visualization */
digraph "if.main._main.basic-block-flow.gv" {
node [shape=box];
"0" [label="label l5
move
temp t0.1
const 1
cjump ne
const 1
const 0
name l0
name l1
"]
"1" [label="label l0
move
temp t1.1
temp t0.1
jump
name l2
"]
"2" [label="label l2
move
temp t1.2
phi(t1.1, t1.3)
sxp
call
name print_int
temp t1.2
call end
jump
name l3
"]
"3" [label="label l1
move
temp t1.3
binop add
temp t0.1
const 1
jump
name l2
"]
"1" -> "2"
"0" -> "1"
"3" -> "2"
"0" -> "3"
}](../../_images/graphviz-4e600852e51741e1593eff668e9aa8e55ef41fae.png)
if.main._main.basic-block-flow.gv
$ tc -e --ssa-debug if.tig
label main
seq
label l5
move
temp t0.1
const 1
cjump ne
const 1
const 0
name l0
name l1
label l1
move
temp t1.3
binop add
temp t0.1
const 1
jump
name l2
label l2
move
temp t1.2
phi(t1.1, t1.3)
sxp
call
name print_int
temp t1.2
call end
jump
name l3
label l0
move
temp t1.1
temp t0.1
jump
name l2
label l3
seq end
$ echo $?
0
As we can see, a Phi-Node was inserted at the end of the two branching paths
of the condition. The temporary t1.2
, which contains the result of the
Phi-Node, is then used in the print_int
call.
$ tc -e --ssa -L if.tig
/* == Low Level Intermediate representation. == */
# Routine: _main
label main
# Prologue
# Body
seq
label l5
move
temp t1.0
const 0
move
temp t0.0
const 0
move
temp t0.1
const 1
cjump ne
const 1
const 0
name l0
name l1
label l1
move
temp t1.3
binop add
temp t0.1
const 1
move
temp t1.2
temp t1.3
label l2
sxp
call
name print_int
temp t1.2
call end
jump
name l3
label l0
move
temp t1.1
temp t0.1
move
temp t1.2
temp t1.1
jump
name l2
label l3
seq end
# Epilogue
label end
$ echo $?
0
When translating back to LIR, the Phi-Node is replaced by a series of assignments.
Let’s see another example.
let
var a := 1
in
while a = 1 do
a := 2;
print_int(a)
end
$ tc -eL while.tig
/* == Low Level Intermediate representation. == */
# Routine: _main
label main
# Prologue
# Body
seq
label l5
move
temp t0
const 1
label l1
cjump eq
temp t0
const 1
name l2
name l0
label l0
sxp
call
name print_int
temp t0
call end
jump
name l3
label l2
move
temp t0
const 2
jump
name l1
label l3
seq end
# Epilogue
label end
$ echo $?
0
$ tc -e --rename-variables --bbflowgraph-dump while.tig
$ echo $?
0
![/* Graph Visualization */
digraph "while.main._main.basic-block-flow.gv" {
node [shape=box];
"0" [label="label l5
move
temp t0.1
const 1
jump
name l1
"]
"1" [label="label l1
move
temp t0.2
phi(t0.1, t0.3)
cjump eq
temp t0.2
const 1
name l2
name l0
"]
"2" [label="label l2
move
temp t0.3
const 2
jump
name l1
"]
"3" [label="label l0
sxp
call
name print_int
temp t0.2
call end
jump
name l3
"]
"2" -> "1"
"1" -> "2"
"1" -> "3"
"0" -> "1"
}](../../_images/graphviz-b9a5f881b9f020108f862f1d9d1776fbc58415de.png)
while.main._main.basic-block-flow.gv
$ tc -e --ssa-debug while.tig
label main
seq
label l5
move
temp t0.1
const 1
jump
name l1
label l1
move
temp t0.2
phi(t0.1, t0.3)
cjump eq
temp t0.2
const 1
name l2
name l0
label l0
sxp
call
name print_int
temp t0.2
call end
jump
name l3
label l2
move
temp t0.3
const 2
jump
name l1
label l3
seq end
$ echo $?
0
Note
In the case of a loop, the Phi-Node is inserted before the condition.
This makes t0.2
use the value of a temporary not yet defined.
This behavior makes sense when translating back to LIR.
$ tc -e --ssa -L while.tig
/* == Low Level Intermediate representation. == */
# Routine: _main
label main
# Prologue
# Body
seq
label l5
move
temp t0.0
const 0
move
temp t0.1
const 1
move
temp t0.2
temp t0.1
label l1
cjump eq
temp t0.2
const 1
name l2
name l0
label l0
sxp
call
name print_int
temp t0.2
call end
jump
name l3
label l2
move
temp t0.3
const 2
move
temp t0.2
temp t0.3
jump
name l1
label l3
seq end
# Epilogue
label end
$ echo $?
0
Since a Phi-Node is replaced by a series of assignments, the first one is made at its location. In case of a loop, assigning the value before the condition and reassigning it in the body is the expected behavior.
In case of definitions in only one branch, the Phi-Node uses the definition of
tx.0
.
let
var a := if 1 then (
let var b := 42 in b end
) else 10
in
print_int(a)
end
$ tc -e --rename-variables --bbflowgraph-dump branching_definition.tig
$ echo $?
0
![/* Graph Visualization */
digraph "branching_definition.main._main.basic-block-flow.gv" {
node [shape=box];
"0" [label="label l5
cjump ne
const 1
const 0
name l0
name l1
"]
"1" [label="label l0
move
temp t0.1
const 42
move
temp t2.1
temp t0.1
jump
name l2
"]
"2" [label="label l2
move
temp t0.2
phi(t0.1, t0.0)
move
temp t2.2
phi(t2.1, t2.3)
move
temp t1.1
temp t2.2
sxp
call
name print_int
temp t1.1
call end
jump
name l3
"]
"3" [label="label l1
move
temp t2.3
const 10
jump
name l2
"]
"1" -> "2"
"0" -> "1"
"3" -> "2"
"0" -> "3"
}](../../_images/graphviz-b2e74a48ea8b77f40f25ae956dd29d4c48c730fc.png)
branching_definition.main._main.basic-block-flow.gv