Require Import SetoidList.
Require Import Coqlib.
Require Import Time.
Require Import Errors.
Require Import Maps.
Require Import Lattice.
Require Import AST.
Require Import Integers.
Require Import Values.
Require Import Globalenvs.
Require Import Op.
Require Import Registers.
Require Import Utils.
Require Import SSA.
Require Import SSAutils.
Require Import Kildall.
Require Import KildallComp.
Require Import Kildall_v2.
Require Import DLib.
Require Import Classical.
Local Open Scope string_scope.
Liveness analysis over SSA
Notation reg_live :=
SSARegSet.add.
Notation reg_dead :=
SSARegSet.remove.
Definition reg_option_live (
or:
option reg)
(
lv:
SSARegSet.t) :=
match or with
|
None =>
lv
|
Some r =>
reg_live r lv
end.
Definition reg_sum_live (
ros:
reg +
ident)
(
lv:
SSARegSet.t) :=
match ros with
|
inl r =>
reg_live r lv
|
inr s =>
lv
end.
Fixpoint reg_list_live
(
rl:
list reg) (
lv:
SSARegSet.t) {
struct rl}
:
SSARegSet.t :=
match rl with
|
nil =>
lv
|
r1 ::
rs =>
reg_list_live rs (
reg_live r1 lv)
end.
Fixpoint reg_list_dead
(
rl:
list reg) (
lv:
SSARegSet.t) {
struct rl}
:
SSARegSet.t :=
match rl with
|
nil =>
lv
|
r1 ::
rs =>
reg_list_dead rs (
reg_dead r1 lv)
end.
Definition transfer_phib k pb after :=
List.fold_left (
fun lv pi =>
match pi with
|
Iphi args _ =>
match nth_error args k with
|
None =>
lv
|
Some r =>
reg_live r lv
end
end)
pb after.
Definition transfer_instruction preds (
phicode:
phicode)
(
i :
instruction)
pc
(
after:
SSARegSet.t) :
SSARegSet.t :=
match i with
|
Inop s =>
match phicode !
s with
|
None =>
after
|
Some pb =>
match index_pred preds pc s with
|
None =>
after
|
Some k =>
transfer_phib k pb after
end
end
|
Iop op args res s =>
reg_list_live args (
reg_dead res after)
|
Iload chunk addr args dst s =>
reg_list_live args (
reg_dead dst after)
|
Istore chunk addr args src s =>
reg_list_live args (
reg_live src after)
|
Icall sig ros args res s =>
reg_list_live args
(
reg_sum_live ros (
reg_dead res after))
|
Itailcall sig ros args =>
reg_list_live args
(
reg_sum_live ros after)
|
Ibuiltin ef args (
BR res)
s =>
reg_list_live (
params_of_builtin_args args) (
reg_dead res after)
|
Ibuiltin ef args res s =>
reg_list_live (
params_of_builtin_args args)
after
|
Icond cond args ifso ifnot =>
reg_list_live args after
|
Ijumptable arg tbl =>
reg_live arg after
|
Ireturn optarg =>
reg_option_live optarg after
end.
Definition transfer_phib' (
phib :
phiblock)
(
after:
SSARegSet.t) :
SSARegSet.t :=
List.fold_left (
fun lv pi =>
match pi with
|
Iphi _ res =>
reg_dead res lv
end)
phib after.
Definition transfer
(
f:
function)
preds (
pc:
node)
(
after:
SSARegSet.t) :
SSARegSet.t :=
match f.(
fn_code)!
pc with
|
None =>
SSARegSet.empty
|
Some i =>
let lv :=
transfer_instruction preds f.(
fn_phicode)
i pc after in
if peq pc f.(
fn_entrypoint)
then reg_list_dead f.(
fn_params)
lv
else
match (
fn_phicode f) !
pc with
|
None =>
lv
|
Some phib =>
transfer_phib'
phib lv
end
end.
The liveness analysis is then obtained by instantiating the
general framework for backward dataflow analysis provided by
module Kildall.
Module RegsetLat :=
LFSet(
SSARegSet).
Module DS :=
Backward_Dataflow_Solver(
RegsetLat)(
NodeSetBackward).
Definition analyze (
f:
function):
option (
PMap.t SSARegSet.t) :=
let preds :=
time (
Some "
analyze ") "
predecessors " (
fun _ =>
(
make_predecessors (
fn_code f)
successors_instr))
tt
in
time (
Some "
analyze ") "
fixpoint " (
fun _ =>
DS.fixpoint f.(
fn_code)
successors_instr (
transfer f preds))
tt.
Lemma use_phicode_is_inop :
forall f x pc
(
wf :
wf_ssa_function f),
use_phicode f x pc ->
exists s,
f.(
fn_code) !
pc =
Some (
Inop s).
Proof.
Lemma wf_ssa_use_and_def_same_pc_precise :
forall f x pc,
wf_ssa_function f ->
use f x pc ->
def f x pc ->
assigned_phi_spec f.(
fn_phicode)
pc x \/
ext_params f x /\
use_phicode f x pc.
Proof.
intros.
inversion H1;
subst.
-
right.
split; [
assumption|].
inversion H0;
subst;
auto.
apply fn_entry in H.
destruct H as (
s &
H).
inversion H3;
subst;
congruence.
-
inversion H0;
subst;
auto.
+
eapply fn_use_def_code in H;
eauto.
contradiction.
+
apply (
use_phicode_is_inop _ _ _ H)
in H3.
destruct H3 as (
s &
H3).
inversion H2;
subst;
congruence.
-
left.
assumption.
Qed.
Lemma prop_dec_exists_dec :
forall (
c :
code) (
P :
node ->
Prop)
(
P_finite :
forall pc,
P pc ->
c !
pc <>
None)
(
P_dec :
forall pc,
P pc \/ ~
P pc),
(
exists pc,
P pc) \/ ~
exists pc,
P pc.
Proof.
intros.
induction c.
-
right.
intros (
pc &
contra).
apply P_finite in contra.
contradiction contra.
apply PTree.gempty.
-
assert ((
exists pc :
node,
P (
xO pc)) \/ ~ (
exists pc :
node,
P (
xO pc)))
as Ha.
{
apply IHc1.
-
intros.
intros contra.
apply (
P_finite (
xO pc));
assumption.
-
intros.
apply P_dec.
}
assert ((
exists pc :
node,
P (
xI pc)) \/ ~ (
exists pc :
node,
P (
xI pc)))
as Hb.
{
apply IHc2.
-
intros.
intros contra.
apply (
P_finite (
xI pc));
assumption.
-
intros.
apply P_dec.
}
destruct Ha as [
Ha|
Ha].
+
left.
destruct Ha as (
pc &
Ha).
exists (
xO pc).
assumption.
+
destruct (
P_dec xH).
*
left.
exists xH;
assumption.
*
destruct Hb as [
Hb|
Hb].
--
left.
destruct Hb as (
pc &
Hb).
exists (
xI pc).
assumption.
--
right.
intros contra.
destruct contra as (
pc &
contra).
destruct pc.
++
apply Hb.
exists pc.
assumption.
++
apply Ha.
exists pc.
assumption.
++
contradiction.
Qed.
Lemma assigned_code_spec_finite :
forall c pc x,
assigned_code_spec c pc x ->
c !
pc <>
None.
Proof.
intros. inv H; congruence.
Qed.
Lemma assigned_code_spec_dec :
forall c pc x,
assigned_code_spec c pc x \/ ~
assigned_code_spec c pc x.
Proof.
intros.
destruct (
c !
pc)
eqn:
Hpc;
try (
right;
intros contra;
inv contra;
congruence).
destruct i;
try (
right;
intros contra;
inv contra;
congruence).
-
destruct (
p2eq r x);
try (
right;
intros contra;
inv contra;
congruence).
left.
subst.
econstructor;
eassumption.
-
destruct (
p2eq r x);
try (
right;
intros contra;
inv contra;
congruence).
left.
subst.
econstructor;
eassumption.
-
destruct (
p2eq r x);
try (
right;
intros contra;
inv contra;
congruence).
left.
subst.
econstructor;
eassumption.
-
destruct b;
try (
right;
intros contra;
inv contra;
congruence).
destruct (
p2eq x0 x);
try (
right;
intros contra;
inv contra;
congruence).
left.
subst.
econstructor;
eassumption.
Qed.
Lemma assigned_phi_spec_finite :
forall c pc x,
assigned_phi_spec c pc x ->
c !
pc <>
None.
Proof.
intros. inv H; congruence.
Qed.
Definition phi_eq (
phi1 phi2 :
phiinstruction) :=
match phi1,
phi2 with
|
Iphi _ r1,
Iphi _ r2 =>
r1 =
r2
end.
Lemma phi_eq_dec :
forall (
phi1 phi2 :
phiinstruction),
{
phi_eq phi1 phi2 } + { ~
phi_eq phi1 phi2 }.
Proof.
intros.
destruct phi1,
phi2.
simpl.
apply p2eq.
Qed.
Lemma assigned_phi_spec_dec :
forall c pc x,
assigned_phi_spec c pc x \/ ~
assigned_phi_spec c pc x.
Proof.
intros.
destruct (
c !
pc)
eqn:
Hpc;
try (
right;
intros contra;
inv contra;
congruence).
destruct (
InA_dec phi_eq_dec (
Iphi nil x)
p).
-
left.
econstructor.
eassumption.
apply InA_alt in i.
destruct i as (
phi &
i1 &
i2).
destruct phi as [
args r].
simpl in i1.
subst.
exists args.
assumption.
-
right.
intros contra.
apply n.
inv contra.
rewrite Hpc in H.
inv H.
destruct H0 as (
args &
H0).
apply InA_alt.
eexists.
split; [|
eassumption].
reflexivity.
Qed.
Lemma make_predecessors_some :
forall {
A} (
code:
PTree.t A)
successors s l,
(
make_predecessors code successors) !
s =
Some l ->
forall p,
In p l ->
exists i,
code !
p =
Some i.
Proof.
Lemma fn_phicode_code :
forall f,
wf_ssa_function f ->
forall pc block,
(
fn_phicode f) !
pc =
Some block ->
exists ins, (
fn_code f) !
pc =
Some ins.
Proof.
Lemma ssa_use_exists_def :
forall f,
wf_ssa_function f ->
forall x u,
use f x u ->
exists d,
def f x d.
Proof.
Inductive live_spec (
f :
function) (
r :
reg)
(
pc :
node) :
Prop :=
|
live_spec_use :
~
def f r pc ->
use f r pc ->
live_spec f r pc
|
live_spec_pred :
forall pc',
cfg f pc pc' ->
~
def f r pc ->
live_spec f r pc' ->
live_spec f r pc.
Inductive liveout_spec (
f :
function) (
r :
reg)
(
pc :
node) :
Prop :=
|
liveout_spec_use :
forall pc',
cfg f pc pc' ->
~
def f r pc' ->
use f r pc' ->
liveout_spec f r pc
|
liveout_spec_trans :
forall pc',
cfg f pc pc' ->
~
def f r pc' ->
liveout_spec f r pc' ->
liveout_spec f r pc.
Record wf_live1 f (
live :
reg ->
node ->
Prop) :=
MK_WF_LIVE1 {
wf_live_incl1 :
forall pc pc'
x,
cfg f pc pc' ->
live x pc' ->
live x pc \/
def f x pc;
wf_live_use1 :
forall pc x,
use f x pc -> ~
def f x pc ->
live x pc
}.
Record wf_live2 f (
live_in live_out :
reg ->
node ->
Prop):=
MK_WF_LIVE2 {
wf_liveout_incl2 :
forall pc pc'
x,
cfg f pc pc' ->
live_in x pc' ->
live_out x pc ;
wf_livein_incl2 :
forall pc x,
~
def f x pc ->
live_out x pc ->
live_in x pc ;
wf_live_use2 :
forall pc x,
use f x pc ->
~
def f x pc ->
live_in x pc
}.
Definition wf_live3 f (
live :
reg ->
node ->
Prop) :=
forall r pc,
liveout_spec f r pc ->
live r pc.
Definition wf_live4 f (
live :
reg ->
node ->
Prop) :=
forall r pc,
live_spec f r pc ->
live r pc.
Lemma liveout_live :
forall f (
wf :
wf_ssa_function f),
forall r pc pc',
cfg f pc pc' ->
live_spec f r pc' ->
liveout_spec f r pc.
Proof.
Lemma live_liveout :
forall f pc x,
~
def f x pc ->
liveout_spec f x pc ->
live_spec f x pc.
Proof.
Lemma live_spec_exists_def :
forall f (
wf :
wf_ssa_function f)
r pc,
live_spec f r pc ->
exists pc',
def f r pc'.
Proof.
Lemma liveout_spec_exists_def :
forall f (
wf :
wf_ssa_function f)
r pc,
liveout_spec f r pc ->
exists pc',
def f r pc'.
Proof.
Lemma live_wf_live1 :
forall f (
wf :
wf_ssa_function f),
wf_live1 f (
live_spec f).
Proof.
Lemma live_wf_live2 :
forall f (
wf :
wf_ssa_function f),
wf_live2 f (
live_spec f) (
liveout_spec f).
Proof.
Lemma wf_live2_wf_live1 :
forall f (
wf :
wf_ssa_function f)
live_in live_out,
wf_live2 f live_in live_out ->
wf_live1 f live_in.
Proof.
Lemma wf_live2_wf_live3 :
forall f (
wf :
wf_ssa_function f)
live_in live_out,
wf_live2 f live_in live_out ->
wf_live3 f live_out.
Proof.
Lemma wf_live2_wf_live4 :
forall f (
wf :
wf_ssa_function f)
live_in live_out,
wf_live2 f live_in live_out ->
wf_live4 f live_in.
Proof.
Lemma wf_live1_wf_live4 :
forall f (
wf :
wf_ssa_function f)
live,
wf_live1 f live ->
wf_live4 f live.
Proof.
Lemma live_spec_exists_path :
forall f (
wf :
wf_ssa_function f)
r pc,
live_spec f r pc ->
exists d u l,
def f r d /\
use f r u /\
SSApath f (
Dom.PState pc)
l (
Dom.PState u) /\
~
In d (
l ++
cons u nil).
Proof.
intros.
destruct (
live_spec_exists_def _ wf _ _ H)
as (
d &
Hdef).
exists d.
induction H.
-
exists pc,
nil.
split; [
assumption|].
split; [
assumption|].
split; [
constructor|].
intros [
contra|[]].
congruence.
-
apply IHlive_spec in Hdef as H2.
destruct H2 as (
u &
l &
_ &
H21 &
H22 &
H23).
exists u, (
pc::
l).
split; [
assumption|].
split; [
assumption|].
split.
+
econstructor;
try eassumption.
econstructor;
try eassumption.
inv H.
eapply fn_code_reached;
eassumption.
+
intros [
contra|
contra];
congruence.
Qed.
Lemma liveout_spec_exists_path :
forall f (
wf :
wf_ssa_function f)
r pc,
liveout_spec f r pc ->
exists d u l,
def f r d /\
use f r u /\
SSApath f (
Dom.PState pc) (
pc::
l) (
Dom.PState u) /\
~
In d (
l ++
u ::
nil).
Proof.
intros.
destruct (
liveout_spec_exists_def _ wf _ _ H)
as (
d &
Hdef).
exists d.
induction H.
-
exists pc',
nil.
split; [
assumption|].
split; [
assumption|].
split.
+
econstructor; [|
constructor].
constructor; [|
assumption].
inv H.
eapply fn_code_reached;
eassumption.
+
intros [
contra|[]].
congruence.
-
apply IHliveout_spec in Hdef as H2.
destruct H2 as (
u &
l &
_ &
H21 &
H22 &
H23).
exists u, (
pc'::
l).
split; [
assumption|].
split; [
assumption|].
split.
+
econstructor;
try eassumption.
econstructor;
try eassumption.
inv H.
eapply fn_code_reached;
eassumption.
+
intros [
contra|
contra];
congruence.
Qed.
Lemma exists_path_live_spec :
forall f (
wf :
wf_ssa_function f)
r d u pc l,
def f r d ->
use f r u ->
SSApath f (
Dom.PState pc)
l (
Dom.PState u) ->
~
In d (
l ++
u ::
nil) ->
live_spec f r pc.
Proof.
intros.
induction H1.
-
constructor; [|
assumption].
intros contra.
apply H2.
left.
eapply ssa_def_unique;
eassumption.
-
inv STEP; [|
inv H1;
inv STEP].
eapply live_spec_pred.
eassumption.
intros contra.
apply H2.
left.
eapply ssa_def_unique;
eassumption.
eapply IHpath;
eauto.
intros contra.
apply H2.
right;
assumption.
Qed.
Lemma exists_path_liveout_spec :
forall f (
wf :
wf_ssa_function f)
r d u pc l,
def f r d ->
use f r u ->
SSApath f (
Dom.PState pc) (
pc::
l) (
Dom.PState u) ->
~
In d (
l ++
u ::
nil) ->
liveout_spec f r pc.
Proof.
intros.
induction H1.
inv STEP; [|
inv H1;
inv STEP].
inv H1.
-
eapply liveout_spec_use.
eassumption.
intros contra.
apply H2.
left.
eapply ssa_def_unique;
eassumption.
assumption.
-
inv STEP; [|
inv PATH;
inv STEP].
eapply liveout_spec_trans.
eassumption.
intros contra.
apply H2.
left.
eapply ssa_def_unique;
eassumption.
eapply IHpath;
eauto.
intros contra.
apply H2.
right;
assumption.
Qed.
Lemma live_spec_not_def :
forall f r d pc,
def f r d ->
live_spec f r pc ->
d <>
pc.
Proof.
intros. inversion H0; congruence.
Qed.
Lemma live_spec_reached :
forall f (
wf :
wf_ssa_function f)
r pc,
live_spec f r pc ->
reached f pc.
Proof.
Lemma liveout_spec_reached :
forall f (
wf :
wf_ssa_function f)
r pc,
liveout_spec f r pc ->
reached f pc.
Proof.
Lemma live_spec_strict :
forall f (
wf :
wf_ssa_function f)
r d pc,
def f r d ->
live_spec f r pc ->
sdom f d pc.
Proof.
Lemma liveout_spec_strict :
forall f (
wf :
wf_ssa_function f)
r d pc,
def f r d ->
liveout_spec f r pc ->
dom f d pc.
Proof.
intros.
destruct (
peq d pc).
-
subst.
left.
-
right.
eapply liveout_spec_reached;
eassumption.
intros.
destruct (
liveout_spec_exists_path _ wf _ _ H0)
as (
d' &
u &
l &
H21 &
H22 &
H23 &
H24).
assert (
d' =
d)
by (
eapply ssa_def_unique;
eauto);
subst.
pose proof (
fn_strict _ wf _ _ _ H22 H21).
inv H2.
+
contradiction H24.
rewrite in_app_iff.
right;
left;
reflexivity.
+
assert (
In d (
p ++
pc ::
l)).
{
apply PATH.
eapply Dom.path_app;
eassumption. }
apply in_app_iff in H2.
destruct H2; [
assumption|].
destruct H2; [
congruence|].
contradiction H24.
rewrite in_app_iff.
left;
assumption.
Qed.
Corollary live_spec_no_entry :
forall f (
wf :
wf_ssa_function f)
r,
~
live_spec f r f.(
fn_entrypoint).
Proof.
Section WF_LIVE.
Variable f :
function.
Variable wf :
wf_ssa_function f.
Notation preds := (
make_predecessors (
fn_code f)
successors_instr).
Definition Lin := (
fun pc live_out => (
transfer f preds pc (
live_out pc))).
Definition Lout {
A:
Type} := (
fun (
live:
PMap.t A)
pc =>
live #
pc).
Lemma reg_list_live_incl :
forall x l s,
SSARegSet.In x s ->
SSARegSet.In x (
reg_list_live l s).
Proof.
induction l ;
intros ;
simpl ;
auto.
eapply IHl ;
eauto.
eapply SSARegSet.add_2 ;
eauto.
Qed.
Lemma reg_list_live_incl_2 :
forall x l s,
In x l ->
SSARegSet.In x (
reg_list_live l s).
Proof.
Lemma reg_list_dead_not_in :
forall l r s,
SSARegSet.In r s ->
~
In r l ->
SSARegSet.In r (
reg_list_dead l s).
Proof.
induction l ;
intros;
go.
simpl.
eapply IHl;
eauto.
eapply SSARegSet.remove_2 ;
eauto.
destruct a,
r ;
intro Hcont ;
invh and ;
subst ;
go.
Qed.
Lemma reg_sum_live_incl :
forall x ros s,
SSARegSet.In x s ->
SSARegSet.In x (
reg_sum_live ros s).
Proof.
intros.
destruct ros;
simpl.
-
apply SSARegSet.add_2.
assumption.
-
assumption.
Qed.
Lemma reg_option_live_incl :
forall x or s,
SSARegSet.In x s ->
SSARegSet.In x (
reg_option_live or s).
Proof.
intros.
destruct or;
simpl.
-
apply SSARegSet.add_2.
assumption.
-
assumption.
Qed.
Lemma OP2_eq :
forall r1 r2,
OP2.eq r1 r2 <->
r1 =
r2.
Proof.
split; intros.
- destruct r1, r2, H; simpl in *; congruence.
- split; congruence.
Qed.
Lemma liveout_incl :
forall live,
analyze f =
Some live ->
forall pc pc',
cfg f pc pc' ->
SSARegSet.Subset (
Lin pc' (
Lout live)) (
Lout live pc).
Proof.
Lemma transfer_phib_livein_incl :
forall live k phib,
SSARegSet.Subset live (
transfer_phib k phib live).
Proof.
intros.
revert live.
induction phib;
intros.
-
reflexivity.
-
simpl.
destruct a.
destruct (
nth_error l k); [|
apply IHphib].
unfold SSARegSet.Subset;
intros.
apply IHphib.
apply SSARegSet.add_2;
assumption.
Qed.
Lemma transfer_phib'
_livein_incl :
forall live pc phib x,
f.(
fn_phicode) !
pc =
Some phib ->
SSARegSet.In x live ->
~
def f x pc ->
SSARegSet.In x (
transfer_phib'
phib live).
Proof.
intros.
remember phib as phib'
in H.
assert (
incl phib phib')
as Ha by (
subst;
apply incl_refl).
clear Heqphib'.
revert dependent live.
induction phib;
intros.
-
assumption.
-
simpl.
destruct a.
eapply IHphib;
try eassumption.
+
eapply incl_cons_inv;
eassumption.
+
apply SSARegSet.remove_2; [|
eassumption].
intros contra.
apply OP2_eq in contra.
subst.
apply H1.
apply def_phicode.
econstructor; [
eassumption|].
eexists.
apply Ha.
left;
reflexivity.
Qed.
Lemma transfer_instruction_livein_incl :
forall live i pc x,
f.(
fn_code) !
pc =
Some i ->
SSARegSet.In x live ->
~
def f x pc ->
SSARegSet.In x (
transfer_instruction preds (
fn_phicode f)
i pc live).
Proof.
Lemma livein_incl :
forall live pc x
(
Hincl :
forall x,
SSARegSet.In x (
Lout live pc) ->
f.(
fn_code) !
pc <>
None),
SSARegSet.In x (
Lout live pc) ->
~
def f x pc ->
SSARegSet.In x (
Lin pc (
Lout live)).
Proof.
Lemma analyze_some :
forall live,
analyze f =
Some live ->
forall pc x,
SSARegSet.In x (
Lout live pc) ->
f.(
fn_code) !
pc <>
None.
Proof.
Lemma transfer_phib_live_use :
forall live k phib args dst x,
In (
Iphi args dst)
phib ->
nth_error args k =
Some x ->
SSARegSet.In x (
transfer_phib k phib live).
Proof.
Lemma transfer_instruction_live_use :
forall live i pc x,
f.(
fn_code) !
pc =
Some i ->
use f x pc ->
SSARegSet.In x (
transfer_instruction preds (
fn_phicode f)
i pc live).
Proof.
Lemma live_use :
forall live pc x,
~
def f x pc ->
use f x pc ->
SSARegSet.In x (
Lin pc (
Lout live)).
Proof.
Lemma live_wf_live :
forall live,
analyze f =
Some live ->
wf_live2 f
(
fun x pc =>
SSARegSet.In x (
Lin pc (
Lout live)))
(
fun x pc =>
SSARegSet.In x (
Lout live pc)).
Proof.
End WF_LIVE.