From Coq Require Import Arith ZArith Psatz Bool String List FSets Program.Equality.
Require Import Sequences IMP Constprop.
Local Open Scope string_scope.
Local Open Scope Z_scope.
Local Open Scope list_scope.
10. Liveness analysis and dead code elimination
Module IdentSet :=
FSetWeakList.Make(
Ident_Decidable).
Module ISFact :=
FSetFacts.WFacts(
IdentSet).
Module ISProp :=
FSetProperties.WProperties(
IdentSet).
Module ISDecide :=
FSetDecide.Decide(
IdentSet).
Import ISDecide.
10.1 Liveness analysis
Computation of the set of variables appearing in an expression or command.
Fixpoint fv_aexp (
a:
aexp) :
IdentSet.t :=
match a with
|
CONST n =>
IdentSet.empty
|
VAR v =>
IdentSet.singleton v
|
PLUS a1 a2 =>
IdentSet.union (
fv_aexp a1) (
fv_aexp a2)
|
MINUS a1 a2 =>
IdentSet.union (
fv_aexp a1) (
fv_aexp a2)
end.
Fixpoint fv_bexp (
b:
bexp) :
IdentSet.t :=
match b with
|
TRUE =>
IdentSet.empty
|
FALSE =>
IdentSet.empty
|
EQUAL a1 a2 =>
IdentSet.union (
fv_aexp a1) (
fv_aexp a2)
|
LESSEQUAL a1 a2 =>
IdentSet.union (
fv_aexp a1) (
fv_aexp a2)
|
NOT b1 =>
fv_bexp b1
|
AND b1 b2 =>
IdentSet.union (
fv_bexp b1) (
fv_bexp b2)
end.
Fixpoint fv_com (
c:
com) :
IdentSet.t :=
match c with
|
SKIP =>
IdentSet.empty
|
ASSIGN x a =>
fv_aexp a
|
SEQ c1 c2 =>
IdentSet.union (
fv_com c1) (
fv_com c2)
|
IFTHENELSE b c1 c2 =>
IdentSet.union (
fv_bexp b) (
IdentSet.union (
fv_com c1) (
fv_com c2))
|
WHILE b c =>
IdentSet.union (
fv_bexp b) (
fv_com c)
end.
To analyze loops, we will need, again!, to compute post-fixpoints
of a function from sets of variables to sets of variables.
We reuse the ``engineer's approach'' from file Constprop.v.
Section FIXPOINT.
Variable F:
IdentSet.t ->
IdentSet.t.
Variable default:
IdentSet.t.
Fixpoint fixpoint_rec (
fuel:
nat) (
x:
IdentSet.t) :
IdentSet.t :=
match fuel with
|
O =>
default
|
S fuel =>
let x' :=
F x in
if IdentSet.subset x'
x then x else fixpoint_rec fuel x'
end.
Definition fixpoint :
IdentSet.t :=
fixpoint_rec 20%
nat IdentSet.empty.
Lemma fixpoint_charact:
IdentSet.Subset (
F fixpoint)
fixpoint \/
fixpoint =
default.
Proof.
Hypothesis F_stable:
forall x,
IdentSet.Subset x default ->
IdentSet.Subset (
F x)
default.
Lemma fixpoint_upper_bound:
IdentSet.Subset fixpoint default.
Proof.
End FIXPOINT.
Liveness analysis.
L is the set of variables live "after" command c.
The result of live c L is the set of variables live "before" c.
Fixpoint live (
c:
com) (
L:
IdentSet.t) :
IdentSet.t :=
match c with
|
SKIP =>
L
|
ASSIGN x a =>
if IdentSet.mem x L
then IdentSet.union (
IdentSet.remove x L) (
fv_aexp a)
else L
|
SEQ c1 c2 =>
live c1 (
live c2 L)
|
IFTHENELSE b c1 c2 =>
IdentSet.union (
fv_bexp b) (
IdentSet.union (
live c1 L) (
live c2 L))
|
WHILE b c =>
let L' :=
IdentSet.union (
fv_bexp b)
L in
let default :=
IdentSet.union (
fv_com (
WHILE b c))
L in
fixpoint (
fun x =>
IdentSet.union L' (
live c x))
default
end.
Lemma live_upper_bound:
forall c L,
IdentSet.Subset (
live c L) (
IdentSet.union (
fv_com c)
L).
Proof.
induction c;
intros;
simpl.
-
fsetdec.
-
destruct (
IdentSet.mem x L)
eqn:
MEM.
fsetdec.
fsetdec.
-
specialize (
IHc1 (
live c2 L)).
specialize (
IHc2 L).
fsetdec.
-
specialize (
IHc1 L).
specialize (
IHc2 L).
fsetdec.
-
apply fixpoint_upper_bound.
intro x.
specialize (
IHc x).
fsetdec.
Qed.
Lemma live_while_charact:
forall b c L,
let L' :=
live (
WHILE b c)
L in
IdentSet.Subset (
fv_bexp b)
L' /\
IdentSet.Subset L L' /\
IdentSet.Subset (
live c L')
L'.
Proof.
10.2 The optimization: dead code elimination
Dead code elimination turns assignments x := a to dead variables x
into SKIP statements.
Fixpoint dce (
c:
com) (
L:
IdentSet.t):
com :=
match c with
|
SKIP =>
SKIP
|
ASSIGN x a =>
if IdentSet.mem x L then ASSIGN x a else SKIP
|
SEQ c1 c2 =>
SEQ (
dce c1 (
live c2 L)) (
dce c2 L)
|
IFTHENELSE b c1 c2 =>
IFTHENELSE b (
dce c1 L) (
dce c2 L)
|
WHILE b c =>
WHILE b (
dce c (
live (
WHILE b c)
L))
end.
Example of optimization.
Compute (
dce Euclidean_division (
IdentSet.singleton "
r")).
Result is:
r := a; ===> r := a;
q := 0; skip;
while b <= r do while b <= r do
r := r - b; r := r - b;
q := q + 1; skip;
done done
Compute (
dce Euclidean_division (
IdentSet.singleton "
q")).
Program is unchanged.
10.3 Correctness of the optimization
Two stores agree on a set L of live variables if they assign
the same values to each live variable.
Definition agree (
L:
IdentSet.t) (
s1 s2:
store) :
Prop :=
forall x,
IdentSet.In x L ->
s1 x =
s2 x.
Monotonicity property.
Lemma agree_mon:
forall L L'
s1 s2,
agree L'
s1 s2 ->
IdentSet.Subset L L' ->
agree L s1 s2.
Proof.
Agreement on the free variables of an expression implies that this
expression evaluates identically in both stores.
Lemma aeval_agree:
forall L s1 s2,
agree L s1 s2 ->
forall a,
IdentSet.Subset (
fv_aexp a)
L ->
aeval s1 a =
aeval s2 a.
Proof.
induction a; simpl; intros.
- auto.
- apply H. fsetdec.
- f_equal. apply IHa1. fsetdec. apply IHa2. fsetdec.
- f_equal. apply IHa1. fsetdec. apply IHa2. fsetdec.
Qed.
Lemma beval_agree:
forall L s1 s2,
agree L s1 s2 ->
forall b,
IdentSet.Subset (
fv_bexp b)
L ->
beval s1 b =
beval s2 b.
Proof.
induction b;
simpl;
intros.
-
auto.
-
auto.
-
rewrite ! (
aeval_agree L s1 s2);
auto;
fsetdec.
-
rewrite ! (
aeval_agree L s1 s2);
auto;
fsetdec.
-
f_equal;
apply IHb;
auto.
-
f_equal.
apply IHb1;
fsetdec.
apply IHb2;
fsetdec.
Qed.
Agreement is preserved by simultaneous assignment to a live variable.
Lemma agree_update_live:
forall s1 s2 L x v,
agree (
IdentSet.remove x L)
s1 s2 ->
agree L (
update x v s1) (
update x v s2).
Proof.
Agreement is also preserved by unilateral assignment to a dead variable.
Lemma agree_update_dead:
forall s1 s2 L x v,
agree L s1 s2 -> ~
IdentSet.In x L ->
agree L (
update x v s1)
s2.
Proof.
intros;
red;
intros.
unfold update.
destruct (
string_dec x x0).
-
subst x0.
contradiction.
-
apply H;
auto.
Qed.
We now show that dead code elimination preserves semantics for terminating
source program. We use big-step semantics to show the following diagram:
agree (live c L) s s1
c / s ----------------------------- dce C L / s1
| |
| |
| |
v v
s' -------------------------------------- s1'
agree L s' s1'
The proof is a simple induction on the big-step evaluation derivation on the left.
Theorem dce_correct_terminating:
forall s c s',
cexec s c s' ->
forall L s1,
agree (
live c L)
s s1 ->
exists s1',
cexec s1 (
dce c L)
s1' /\
agree L s'
s1'.
Proof.
Exercise (3 stars, optional)
To prove semantic preservation both for terminating and diverging programs.
complete the following simulation proof, which uses the small-step semantics
of Imp, without continuations.
Fixpoint measure (
c:
com) :
nat :=
match c with
|
ASSIGN x a => 1%
nat
|
SEQ c1 c2 =>
measure c1
|
_ => 0%
nat
end.
Theorem dce_simulation:
forall c s c'
s',
red (
c,
s) (
c',
s') ->
forall L s1,
agree (
live c L)
s s1 ->
(
exists s1',
red (
dce c L,
s1) (
dce c'
L,
s1') /\
agree (
live c'
L)
s'
s1')
\/
(
measure c' <
measure c /\
dce c L =
dce c'
L /\
agree (
live c'
L)
s'
s1)%
nat.
Proof.
intros until s'. intro STEP. dependent induction STEP; intros; cbn [dce].
Abort.