TC-S Dead Code Elimination Samples
Note
The goal of dead code elimination is to remove unused temporaries from the code.
For those examples, we will compare the result of the conversion back to LIR with and without dead code elimination performed during the SSA stage.
let
var used := "toto"
var unused := "tata"
in
print(used)
end
$ tc -e --ssa -L unused_variable.tig
/* == Low Level Intermediate representation. == */
label l0
"toto"
label l1
"tata"
# Routine: _main
label main
# Prologue
# Body
seq
label l6
move
temp t1.0
const 0
move
temp t0.0
const 0
move
temp t0.1
name l0
move
temp t1.1
name l1
sxp
call
name print
temp t0.1
call end
label l2
seq end
# Epilogue
label end
$ echo $?
0
$ tc -e --dead-code-elimination --ssa -L unused_variable.tig
/* == Low Level Intermediate representation. == */
label l0
"toto"
label l1
"tata"
# Routine: _main
label main
# Prologue
# Body
seq
label l6
move
temp t1.0
const 0
move
temp t0.0
const 0
move
temp t0.1
name l0
sxp
call
name print
temp t0.1
call end
label l2
seq end
# Epilogue
label end
$ echo $?
0
let
var b := 2
var a := if 1 then (b := 3; b) else (b := 4; 0)
in
print_int(a)
end
If a temporary is never used, it is simply removed from the final output, along with its definition.
Note
Note that we keep the definition of tx.0
as a fallback after
deletion. An example of this behavior is presented below.
$ tc -e --rename-variables --bbflowgraph-dump double_branch.tig
$ echo $?
0
![/* Graph Visualization */
digraph "double_branch.main._main.basic-block-flow.gv" {
node [shape=box];
"0" [label="label l5
move
temp t0.1
const 2
cjump ne
const 1
const 0
name l0
name l1
"]
"1" [label="label l0
move
temp t0.2
const 3
move
temp t2.1
temp t0.2
jump
name l2
"]
"2" [label="label l2
move
temp t0.3
phi(t0.2, t0.4)
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 t0.4
const 4
move
temp t2.3
const 0
jump
name l2
"]
"1" -> "2"
"0" -> "1"
"3" -> "2"
"0" -> "3"
}](../../_images/graphviz-6a3eaa26ec2982592a530023c21ca75dfd3ec61e.png)
double_branch.main._main.basic-block-flow.gv
$ tc -e --dead-code-elimination --rename-variables --bbflowgraph-dump double_branch_dce.tig
$ echo $?
0
![/* Graph Visualization */
digraph "double_branch_dce.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.2
const 3
move
temp t2.1
temp t0.2
jump
name l2
"]
"2" [label="label l2
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 0
jump
name l2
"]
"1" -> "2"
"0" -> "1"
"3" -> "2"
"0" -> "3"
}](../../_images/graphviz-cf285a035eb4c6c8c150b628f5fdf65560e5a925.png)
double_branch_dce.main._main.basic-block-flow.gv
Warning
Before removing a function call, we need to make sure that the method is pure.
A pure method is one without side effects, meaning that they do not write in memory outside the function’s scope.
For exemple, functions that perform I/O operations are ones that produce side
effects, as they write somewhere in memory. In Tiger, IO builtins such as
print
, print_int
, getchar
, etc. produce side effects
and have been dealt with (reordered/extracted) during canonicalization
(see TC-6, Translating to the Low Level Intermediate Representation).
In the following example, we declare a unused variable val
that is
initialized to an array, with some IO call in its initialization.
let
type arr = array of int
var val := (
print("Where DCE?"); /* Handled during canonicalization. */
arr[10] of 42 /* Calls `init_array', side effects procedure! */
)
in
end
- What can be seen after canonicalization is that:
The IO call (
print
, returning nothing), has been correctly handled and extracted, so we have nothing to deal with IO calls during dead code elimination,However, the variable initialization value, implicitly calling init_array, will be handled during dead code elimination.
$ tc -L io_side_effects.tig
/* == Low Level Intermediate representation. == */
label l0
"Where DCE?"
# Routine: _main
label main
# Prologue
move
temp t1
temp fp
move
temp fp
temp sp
move
temp sp
binop sub
temp sp
const 4
# Body
seq
label l4
move
temp t2
temp fp
sxp
call
name print
name l0
call end
move
temp t0
call
name init_array
const 10
const 42
call end
move
mem
temp t2
temp t0
label l1
seq end
# Epilogue
move
temp sp
temp fp
move
temp fp
temp t1
label end
$ echo $?
0
let
type arr = array of int
var my_array := arr[10] of 0
function f(arr : arr, i : int) : int = (arr[i] := 42; arr[i])
var a := 0
in
a := f(my_array, 0)
end
Since the function shown above writes in the array that is defined outside the function’s scope, we consider it to be impure. Therefore, the function call and its associated variable are not removed even if they are not used.
$ tc -e --dead-code-elimination --ssa -L impure_method.tig
/* == Low Level Intermediate representation. == */
# Routine: f
label l0
# Prologue
move
temp t4
temp fp
move
temp fp
temp sp
move
temp sp
binop sub
temp sp
const 4
move
mem
temp fp
temp i0
move
temp t2.0
temp i1
move
temp t3.0
temp i2
# Body
seq
label l5
move
temp rv
const 0
move
mem
binop add
temp t2.0
binop mul
temp t3.0
const 4
const 42
move
temp rv
mem
binop add
temp t2.0
binop mul
temp t3.0
const 4
label l1
seq end
# Epilogue
move
temp sp
temp fp
move
temp fp
temp t4
label end
# Routine: _main
label main
# Prologue
# Body
seq
label l6
move
temp t5.0
const 0
move
temp t1.0
const 0
move
temp t0.0
const 0
move
temp t0.1
call
name init_array
const 10
const 0
call end
move
temp t1.1
temp t0.1
move
temp t5.2
call
name l0
temp fp
temp t1.1
const 0
call end
label l2
seq end
# Epilogue
label end
$ echo $?
0
In case of a pure method like the one below, the function call is removed from the final output.
let
function f() : int = 42
var a := f()
in
end
$ tc -e --dead-code-elimination --ssa -L pure_method.tig
/* == Low Level Intermediate representation. == */
# Routine: f
label l0
# Prologue
move
temp t0
temp fp
move
temp fp
temp sp
move
temp sp
binop sub
temp sp
const 4
move
mem
temp fp
temp i0
# Body
seq
label l5
move
temp rv
const 0
move
temp rv
const 42
label l1
seq end
# Epilogue
move
temp sp
temp fp
move
temp fp
temp t0
label end
# Routine: _main
label main
# Prologue
# Body
seq
label l6
move
temp t1.0
const 0
label l2
seq end
# Epilogue
label end
$ echo $?
0