Require Import Coqlib.
Require Import Maps.
Require Import AST.
Require Import Op.
Require Import Registers.
Require Import Utils.
Require Import Integers.
Require Import Floats.
Require Import Classical.
Require Import Lattice.
Require Import Iteration.
Require Import DLib.
Require Import Kildall.
Require Import KildallComp.
Require Import SSA.
Require Import SSAutils.
Require Import Utilsvalidproof.
Require Opt.
Require Import Dsd.
Require Import ValueDomainSSA ValueAOpSSA.
Sparse Conditional Constant Propagation
Dataflow solver over DU chains
Module Type DATAFLOW_SOLVER.
Module L :=
AVal.
Definition lattice_values :=
PMap.t L.t.
Definition exec_state :=
P2Map.t bool.
Definition not_defined (
f:
function)
(
absint:
instruction ->
lattice_values ->
option (
reg *
L.t)) (
r:
reg) :=
(
forall lv r'
nv i pc, (
fn_code f) !
pc =
Some i ->
absint i lv =
Some (
r',
nv) ->
r <>
r')
/\ (
forall pc pb l r', (
fn_phicode f) !
pc =
Some pb ->
In (
Iphi l r')
pb ->
r <>
r').
Definition executable_node (
f :
function) (
pc' :
node) (
es :
exec_state) :=
pc' =
fn_entrypoint f
\/ (
exists pc,
es !!2 (
pc,
pc') =
true /\
cfg f pc pc').
Parameter fixpoint :
forall
(
f:
function)
(
absint:
instruction ->
lattice_values ->
option (
reg *
L.t))
(
init:
lattice_values),
PMap.t L.t *
exec_state.
Axiom defined_nowhere_stays_top:
forall f absint init r lv,
not_defined f absint r ->
init #
r =
L.top ->
lv =
fst (
fixpoint f absint init) ->
lv #
r =
L.top.
Axiom defined_nowhere_becomes_top:
forall f absint init r,
not_defined f absint r ->
init #
r =
L.bot ->
(
fst (
fixpoint f absint init)) #
r =
L.top.
Definition exec_flags_sound (
f :
function) (
lv :
Regmap.t L.t) (
es :
exec_state) :=
forall pc pc'
i,
cfg f pc pc' ->
es !!2 (
pc,
pc') =
false ->
(
fn_code f) !
pc' =
Some i ->
~
executable_node f pc es \/
match (
fn_code f) !
pc with
|
Some (
Icond cond args ifso ifnot) =>
(
ifso =
pc' ->
eval_static_condition cond lv ##
args =
Just false) /\
(
ifnot =
pc' ->
eval_static_condition cond lv ##
args =
Just true)
|
Some (
Ijumptable arg tbl) =>
exists n :
int,
lv #
arg =
I n /\
list_nth_z tbl (
Int.unsigned n) <>
Some pc'
|
_ =>
False
end.
Definition code_post_fixpoint (
f :
function)
(
absint:
instruction ->
lattice_values ->
option (
reg *
L.t))
(
lv :
lattice_values) (
es :
exec_state) :=
forall pc i r v,
(
fn_code f) !
pc =
Some i ->
executable_node f pc es ->
absint i lv =
Some (
r,
v) ->
L.ge lv #
r v.
Definition phicode_post_fixpoint (
f :
function)
(
lv :
Regmap.t L.t) (
es :
exec_state) :=
forall pc pb r l xi pc'
k,
(
fn_phicode f) !
pc' =
Some pb ->
In (
Iphi l r)
pb ->
index_pred (
make_predecessors (
fn_code f)
successors_instr)
pc pc' =
Some k ->
es !!2 (
pc,
pc') =
true ->
nth_error l k =
Some xi ->
L.ge (
lv #
r) (
lv #
xi).
Axiom post_fixpoint :
forall f absint init lv es,
fixpoint f absint init = (
lv,
es) ->
code_post_fixpoint f absint lv es
/\
phicode_post_fixpoint f lv es
/\
exec_flags_sound f lv es.
End DATAFLOW_SOLVER.
Module DataflowSolver:
DATAFLOW_SOLVER.
Module L :=
AVal.
Section CDS.
Definition lattice_values :=
PMap.t L.t.
Definition exec_state :=
P2Map.t bool.
Definition instr_workset := (
list reg *
list reg)%
type.
Definition edge := (
node *
node)%
type.
Definition state := (
list edge *
instr_workset *
lattice_values *
exec_state)%
type.
Def-Use Chains
Definition ssainstruction := (
node * (
instruction +
phiinstruction))%
type.
Definition du_chain :=
PMap.t (
list ssainstruction).
Record const_state :=
mkConstantState {
cs_duc:
du_chain;
cs_preds:
PTree.t (
list node)
}.
Variable f:
function.
Variable absint :
instruction ->
lattice_values ->
option (
reg *
L.t).
Variable initial_values :
lattice_values.
Definition bge (
x y :
L.t) :
bool :=
L.beq (
L.lub x y)
x.
Remark bge_correct:
forall x y,
bge x y =
true ->
L.ge x y.
Proof.
Determining whether a given node is executable, in current dataflow solver
Definition preds_of cs pc :=
match (
cs_preds cs) !
pc with
|
None =>
nil
|
Some l =>
l
end.
Fixpoint node_is_executable_rec (
es:
exec_state)
preds pc' :=
match preds with
|
nil =>
false
|
pc ::
pcs =>
if es #2 (
pc,
pc')
then true else node_is_executable_rec es pcs pc'
end.
Definition node_is_executable cs (
st:
state)
pc' :=
match st with
(
cfgwl,
iws,
lv,
es) =>
node_is_executable_rec es (
preds_of cs pc')
pc'
end.
Definition single_succ (
pc:
node) (
i:
instruction) :
option edge :=
match i with
|
Inop s =>
Some (
pc,
s)
|
Iop op args res s =>
Some (
pc,
s)
|
Iload chunk addr args dst s =>
Some (
pc,
s)
|
Istore chunk addr args src s =>
Some (
pc,
s)
|
Icall sig ros args res s =>
Some (
pc,
s)
|
Itailcall sig ros args =>
None
|
Ibuiltin ef args res s =>
Some (
pc,
s)
|
Icond cond args ifso ifnot =>
None
|
Ijumptable arg tbl =>
None
|
Ireturn optarg =>
None
end.
Picks a register from the worklist, from the top list if possible
Fixpoint pick_instr_rec vl (
iws_t:
list reg) (
iws_nt:
list reg) : (
option reg *
instr_workset) :=
match iws_t,
iws_nt with
|
x::
xs,
ys => (
Some x, (
xs,
ys))
|
nil,
y::
ys =>
if L.beq L.top vl#
y then pick_instr_rec vl nil ys else (
Some y, (
nil,
ys))
|
nil,
nil => (
None, (
nil,
nil))
end.
Definition pick_instr vl (
iws:
instr_workset) :
option reg *
instr_workset:=
match iws with
(
ts,
nts) =>
pick_instr_rec vl ts nts
end.
Updates the state with the new value nv of r, and adds it
to the workset if necessary
Definition add_instr_aux (
r:
reg) (
v:
L.t) (
iws:
instr_workset) :=
let (
top,
ntop) :=
iws in
if L.beq v L.top then (
r ::
top,
ntop)
else (
top,
r ::
ntop).
Definition update_vals lv iws r nv :=
let ov :=
lv #
r in
if bge ov nv
then (
lv,
iws)
else (
lv #
r <- (
L.lub nv ov),
add_instr_aux r (
L.lub nv ov)
iws).
Static evaluation of a phi-block
Fixpoint visit_phi_rec (
lv:
lattice_values) (
es:
exec_state)
pc'
args preds x :=
if L.beq L.top x then Some L.top else
match args,
preds with
|
y::
ys,
pc::
pcs =>
let a :=
if es #2 (
pc,
pc')
then lv #
y else L.bot in
visit_phi_rec lv es pc'
ys pcs (
L.lub x a)
|
nil,
nil =>
Some x
|
_,
_ =>
None
end.
Definition visit_phi cs (
st_in:
state)
pc'
r_used pi :
state :=
match st_in with (
cfgwl,
iws,
lv,
es) =>
match pi with Iphi args r =>
if L.beq L.top lv #
r then (
cfgwl,
iws,
lv,
es)
else
match visit_phi_rec lv es pc'
args (
preds_of cs pc')
r_used with
|
Some x =>
let (
lv',
iws') :=
update_vals lv iws r x in
(
cfgwl,
iws',
lv',
es)
|
None => (
cfgwl,
iws,
lv,
es)
end
end
end.
Definition visit_phibloc cs st r_used pc :=
match (
fn_phicode f) !
pc with
|
None =>
st
|
Some pb =>
fold_left (
fun acc pi =>
visit_phi cs acc pc r_used pi)
pb st
end.
Definition visit_instr (
st_in :
state)
pc instr :=
match st_in with (
cfgwl,
iws,
lv,
es) =>
match instr with
|
Icond cond args ifso ifnot =>
match eval_static_condition cond lv ##
args with
|
Just true => ((
pc,
ifso)::
cfgwl,
iws,
lv,
es)
|
Just false => ((
pc,
ifnot)::
cfgwl,
iws,
lv,
es)
|
_ => ((
pc,
ifso) :: (
pc,
ifnot) ::
cfgwl,
iws,
lv,
es)
end
|
Ijumptable arg tbl =>
match lv #
arg with
|
I k =>
match list_nth_z tbl (
Int.unsigned k)
with
|
None => (
map (
fun x:
node => (
pc,
x))
tbl ++
cfgwl,
iws,
lv,
es)
|
Some pc' => ((
pc,
pc')::
cfgwl,
iws,
lv,
es)
end
|
x => (
map (
fun x:
node => (
pc,
x))
tbl ++
cfgwl,
iws,
lv,
es)
end
|
_ =>
match absint instr lv with
|
Some (
r,
x) =>
let (
lv',
iws') :=
update_vals lv iws r x in
(
cfgwl,
iws',
lv',
es)
|
None => (
cfgwl,
iws,
lv,
es)
end
end
end.
The register defined by an instruction
Definition def_reg i :=
match i with
|
Iop _ _ r _ |
Iload _ _ _ r _ |
Istore _ _ _ r _
|
Icall _ _ _ r _ |
Ibuiltin _ _ (
BR r)
_ =>
Some r
|
_ =>
None
end.
Definition visit_ssainstr cs st r_used (
ssai :
ssainstruction) :=
match st with (
_,
_,
lv,
_) =>
match ssai with
| (
pc,
inr pi) =>
visit_phi cs st pc r_used pi
| (
pc,
inl instr) =>
match def_reg instr with
|
Some r =>
if L.beq L.top lv #
r
then st
else match node_is_executable cs st pc with
|
false =>
st
|
true =>
visit_instr st pc instr
end
|
None =>
match node_is_executable cs st pc with
|
false =>
st
|
true =>
visit_instr st pc instr
end
end
end
end.
Definition step (
ms :
const_state *
state) : (
option (
lattice_values *
exec_state))
+ (
const_state *
state) :=
let (
cs,
st) :=
ms in
match st with (
cfgwl,
iws,
lv,
es) =>
match cfgwl with
| (
pc0,
pc) ::
cfgwl' =>
match es #2 (
pc0,
pc)
with
|
true =>
inr _ (
cs, (
cfgwl',
iws,
lv,
es))
|
false =>
let es' :=
es #2 (
pc0,
pc) <-
true in
let st2 :=
visit_phibloc cs (
cfgwl',
iws,
lv ,
es')
L.bot pc in
match (
fn_code f) !
pc with
|
None =>
inl _ None
|
Some instr =>
match visit_instr st2 pc instr with
| (
cfgwl'',
iws'',
lv'',
es'') =>
match single_succ pc instr with
|
None =>
inr _ (
cs, (
cfgwl'',
iws'',
lv'',
es''))
|
Some (
x,
y) =>
inr _ (
cs, (
if es' #2 (
x,
y)
then cfgwl''
else (
x,
y)::
cfgwl'',
iws'',
lv'',
es''))
end
end
end
end
|
nil =>
match pick_instr lv iws with
| (
Some r,
iws') =>
inr _ (
cs, (
fold_left (
fun st_in ssai =>
visit_ssainstr cs st_in lv #
r ssai)
(
cs_duc cs) #
r
(
cfgwl,
iws',
lv,
es)))
|
_ =>
inl _ (
Some (
lv,
es))
end
end
end.
Definition initial_state :
option state :=
let start i :=
match single_succ (
fn_entrypoint f)
i with
|
None =>
nil
|
Some x =>
x ::
nil
end in
match (
fn_code f) ! (
fn_entrypoint f)
with
|
None =>
None
|
Some i =>
Some (
start i, (
nil,
nil),
initial_values,
P2Map.init false)
end.
Post-fixpoint checker
Definition executable_node (
pc':
node) (
es:
exec_state) :=
pc' =
fn_entrypoint f \/
exists pc,
es #2 (
pc,
pc') =
true /\
SSA.cfg f pc pc'.
Definition bexecutable_node (
pc':
node) (
preds:
PTree.t (
list node)) (
es:
exec_state) :=
if Pos.eq_dec pc' (
fn_entrypoint f)
then true else
existsb (
fun pc =>
es #2 (
pc,
pc'))
preds !!!
pc'.
Definition check_code lv preds es :=
forall_ptree (
fun pc i =>
match bexecutable_node pc preds es with
|
false =>
true
|
true =>
match absint i lv with
|
None =>
true
|
Some (
r,
nv) =>
bge (
lv #
r)
nv
end
end) (
fn_code f).
Fixpoint check_phiinstruction lv es r rs preds pc' :=
match rs,
preds with
|
nil,
nil =>
true
|
x::
xs,
pc::
pcs =>
match es #2 (
pc,
pc')
with
|
false =>
check_phiinstruction lv es r xs pcs pc'
|
true =>
bge (
lv #
r) (
lv #
x) &&
check_phiinstruction lv es r xs pcs pc'
end
|
_,
_ =>
false
end.
Definition check_phicode_g lv es preds (
pc:
node)
pb :=
forallb (
fun pi =>
match pi with
|
Iphi rs r =>
check_phiinstruction lv es r rs (
preds !!!
pc)
pc
end)
pb.
Definition check_phicode lv es preds :=
forall_ptree (
check_phicode_g lv es preds) (
fn_phicode f).
Definition check_non_exec_edge lv pc pc' :=
match (
fn_code f) !
pc with
|
Some (
Icond cond args ifso ifnot) =>
match Pos.eq_dec pc'
ifso with
|
left _ =>
match eval_static_condition cond lv ##
args with
|
Just false =>
match Pos.eq_dec pc'
ifnot with
|
right _ =>
true
|
left _ =>
match eval_static_condition cond lv ##
args with
|
Just true =>
true
|
_ =>
false
end
end
|
_ =>
false
end
|
right _ =>
match Pos.eq_dec pc'
ifnot with
|
right _ =>
false
|
left _ =>
match eval_static_condition cond lv ##
args with
|
Just true =>
true
|
_ =>
false
end
end
end
|
Some (
Ijumptable arg tbl) =>
match lv #
arg with
|
I n =>
match list_nth_z tbl (
Int.unsigned n)
with
|
Some p =>
if Pos.eq_dec p pc'
then false else true
|
None =>
true
end
|
_ =>
false
end
|
Some i =>
false
|
None =>
false
end.
Definition check_executable_flags lv es preds :=
forall_ptree
(
fun pc'
_ =>
forallb (
fun pc =>
match bexecutable_node pc preds es with
|
true =>
if bool_dec es #2 (
pc,
pc')
false
then check_non_exec_edge lv pc pc'
else true
|
false =>
true
end) (
preds !!!
pc')) (
fn_code f).
Definition check lv es preds :=
(
check_phicode lv es preds)
&& (
check_code lv preds es)
&& (
check_executable_flags lv es preds).
Fixpoint
Construction
Definition regs_used_by (
i :
instruction) :
list reg :=
match i with
|
Iop _ l _ _ =>
l
|
Iload _ _ l _ _ =>
l
|
Istore _ _ l _ _ =>
l
|
Icall _ ros l _ _
|
Itailcall _ ros l =>
match ros with
|
inl r =>
r ::
l
|
inr _ =>
l
end
|
Ibuiltin _ l _ _ => (
params_of_builtin_args l)
|
Icond _ l _ _ =>
l
|
Ijumptable r _ =>
r ::
nil
|
Ireturn (
Some r) =>
r ::
nil
|
_ =>
nil
end.
Definition handle_reg_list (
duc:
du_chain) (
ssai:
ssainstruction) (
rs:
list reg) :=
List.fold_left (
fun u r =>
PMap.set r (
ssai ::
u #
r)
u)
rs duc.
Definition def_use_1 duc c :=
PTree.fold (
fun u pc i =>
handle_reg_list u (
pc,
inl _ i) (
regs_used_by i))
c duc.
Definition handle_phi_block (
duc :
du_chain)
pc (
pb :
phiblock) :=
List.fold_left
(
fun u pi =>
match pi with
Iphi args _ =>
handle_reg_list u (
pc,
inr _ pi)
args end)
pb duc.
Definition def_use_2 duc phic :=
PTree.fold (
fun u pc pb =>
handle_phi_block u pc pb)
phic duc.
Definition make_du_chain f :
du_chain :=
let u :=
def_use_1 (
PMap.init nil) (
fn_code f)
in
def_use_2 u (
fn_phicode f).
Definition fixpoint :=
let failed := (
PMap.init L.top,
P2Map.init true)
in
let preds :=
make_predecessors (
fn_code f)
successors_instr in
let cs :=
mkConstantState (
make_du_chain f)
preds in
match initial_state with
|
Some is =>
match PrimIter.iterate _ _ step (
cs,
is)
with
|
Some (
Some (
lv,
es)) =>
let lv' :=
PMap.map (
fun v =>
if L.beq v L.bot then L.top else v)
lv in
if check lv'
es preds then (
lv',
es)
else failed
|
_ =>
failed
end
|
None =>
failed
end.
Proof of the checker
Remark bexecutable_node_correct:
forall es pc',
bexecutable_node pc' (
make_predecessors (
fn_code f)
successors_instr)
es =
true <->
executable_node pc'
es.
Proof.
Definition code_post_fixpoint lv es :=
forall pc i r v,
(
fn_code f) !
pc =
Some i ->
executable_node pc es ->
absint i lv =
Some (
r,
v) ->
L.ge lv #
r v.
Remark check_code_correct:
forall lv es,
check_code lv (
make_predecessors (
fn_code f)
successors_instr)
es =
true ->
code_post_fixpoint lv es.
Proof.
Definition phicode_post_fixpoint lv es :=
forall pc pb r l xi pc'
k,
(
fn_phicode f) !
pc' =
Some pb ->
In (
Iphi l r)
pb ->
index_pred (
make_predecessors (
fn_code f)
successors_instr)
pc pc' =
Some k ->
es #2 (
pc,
pc') =
true ->
nth_error l k =
Some xi ->
L.ge (
lv #
r) (
lv #
xi).
Hint Resolve bge_correct:
core.
Remark check_phiinstruction_correct:
forall lv es pb r l pc'
preds,
(
fn_phicode f) !
pc' =
Some pb ->
check_phiinstruction lv es r l preds pc' =
true->
forall pc xi k,
(
k <
length preds)%
nat ->
Utils.get_index preds pc =
Some k ->
es #2 (
pc,
pc') =
true ->
nth_error l k =
Some xi ->
L.ge (
lv #
r) (
lv #
xi).
Proof.
intros.
generalize dependent l.
induction preds.
+
simpl in *.
intros.
lia.
+
simpl in *.
intros.
destruct l.
simpl in *.
congruence.
assert (
check_phiinstruction lv es r l preds pc' =
true).
simpl in *.
flatten H0;
apply andb_true_iff in H0;
intuition.
destruct (
eq_nat_dec k O).
-
subst.
unfold nth_error in H4.
inv H4.
assert (
a =
pc).
apply get_index_nth_error in H2.
unfold nth_error in *.
simpl in *.
inv H2.
reflexivity.
subst.
simpl in *.
flatten H0.
apply bge_correct.
apply andb_true_iff in H0.
intuition.
-
assert (
exists k0,
k =
Datatypes.S k0)
as [
k0 Hk].
destruct (
O_or_S k).
inv s.
exists x.
auto.
congruence.
subst.
eapply IHpreds with (
k :=
k0) (
l :=
l);
eauto.
*
lia.
*
eapply get_index_cons_succ;
eauto.
Qed.
Remark check_phicode_correct:
forall lv es,
check_phicode lv es (
make_predecessors (
fn_code f)
successors_instr) =
true ->
phicode_post_fixpoint lv es.
Proof.
Definition exec_flags_sound lv es :=
forall pc pc'
i
(
EX_CFG:
SSA.cfg f pc pc')
(
EX_NOTEXEC:
es #2 (
pc,
pc') =
false)
(
EX_INS: (
fn_code f) !
pc' =
Some i),
~
executable_node pc es \/
match (
fn_code f) !
pc with
|
Some (
Icond cond args ifso ifnot) =>
(
ifso =
pc' ->
eval_static_condition cond lv ##
args =
Just false) /\
(
ifnot =
pc' ->
eval_static_condition cond lv ##
args =
Just true)
|
Some (
Ijumptable arg tbl) =>
exists n, (
lv #
arg =
I n /\
list_nth_z tbl (
Int.unsigned n) <>
Some pc')
|
_ =>
False
end.
Remark check_executable_flags_correct:
forall es lv,
check_executable_flags lv es (
make_predecessors (
fn_code f)
successors_instr) =
true ->
exec_flags_sound lv es.
Proof.
Remark top_is_post_fixpoint:
code_post_fixpoint (
PMap.init L.top) (
P2Map.init true)
/\
phicode_post_fixpoint (
PMap.init L.top) (
P2Map.init true)
/\
exec_flags_sound (
PMap.init L.top) (
P2Map.init true).
Proof.
Remark check_correct:
forall lv es,
check lv es (
make_predecessors (
fn_code f)
successors_instr) =
true ->
code_post_fixpoint lv es /\
phicode_post_fixpoint lv es /\
exec_flags_sound lv es.
Proof.
Correctness of du_chains
Definition ssai_in_function ssai f :=
match ssai with
| (
pc,
inl i) => (
fn_code f) !
pc =
Some i
| (
pc,
inr pi) =>
exists pb, (
fn_phicode f) !
pc =
Some pb /\
In pi pb
end.
Definition maps_into_function f m :=
forall r ssai,
In ssai (
m #
r) ->
ssai_in_function ssai f.
Hint Unfold maps_into_function ssai_in_function:
core.
Lemma duc_maps_into_function_handle_reg_list:
forall f duc ssai rs,
maps_into_function f duc ->
ssai_in_function ssai f ->
maps_into_function f (
handle_reg_list duc ssai rs).
Proof.
intros.
generalize dependent duc.
induction rs.
tauto.
intros.
simpl in *.
eapply IHrs;
eauto.
unfold maps_into_function in *.
intros.
destruct (
peq a r).
+
subst.
rewrite PMap.gss in *.
inv H1;
eauto.
+
rewrite PMap.gso in *;
auto.
eauto.
Qed.
Lemma duc_maps_into_function_code:
forall f duc,
maps_into_function f duc ->
maps_into_function f (
def_use_1 duc (
fn_code f)).
Proof.
Lemma duc_maps_into_function_phibloc:
forall f duc pc pb l,
maps_into_function f duc ->
(
fn_phicode f) !
pc =
Some pb ->
(
exists pref,
pref ++
l =
pb) ->
maps_into_function f (
handle_phi_block duc pc l).
Proof.
Lemma duc_maps_into_function_phicode:
forall f duc,
maps_into_function f duc ->
maps_into_function f (
def_use_2 duc (
fn_phicode f)).
Proof.
Lemma duc_maps_into_function:
forall f,
maps_into_function f (
make_du_chain f).
Proof.
Definition get_lv (
st:
state) :=
match st with
(
_,
_,
lv,
_) =>
lv
end.
Definition not_defined r := (
forall lv r'
nv i pc,
(
fn_code f) !
pc =
Some i ->
absint i lv =
Some (
r',
nv) ->
r <>
r') /\
(
forall pc pb l r', (
fn_phicode f) !
pc =
Some pb ->
In (
Iphi l r')
pb ->
r <>
r').
Remark defined_nowhere_stationary_aux_update_val:
forall lv b r t lv',
fst (
update_vals lv b r t) =
lv' ->
(
forall r',
r' <>
r ->
lv' #
r' =
lv #
r').
Proof.
intros.
unfold update_vals in *.
flatten H;
simpl in *;
try congruence.
subst.
rewrite PMap.gso;
auto.
Qed.
Remark defined_nowhere_stationary_aux_visit_instr:
forall st r pc i,
not_defined r ->
(
fn_code f) !
pc =
Some i ->
(
get_lv (
visit_instr st pc i)) #
r = (
get_lv st) #
r.
Proof.
Remark defined_nowhere_stationary_aux_visit_phi:
forall st cs r pc r_used pi pb,
not_defined r ->
(
fn_phicode f) !
pc =
Some pb ->
In pi pb ->
(
get_lv (
visit_phi cs st pc r_used pi)) #
r = (
get_lv st) #
r.
Proof.
Remark defined_nowhere_stationary_aux_visit_phibloc_rec:
forall st st'
cs r pc r_used pb,
not_defined r ->
(
fn_phicode f) !
pc =
Some pb ->
(
forall l, (
exists l',
l' ++
l =
pb) ->
fold_left (
fun acc pi =>
visit_phi cs acc pc r_used pi)
l
st =
st' ->
(
get_lv st') #
r = (
get_lv st) #
r).
Proof.
Remark defined_nowhere_stationary_aux_visit_phibloc:
forall st st'
cs r pc r_used,
not_defined r ->
visit_phibloc cs st r_used pc =
st' ->
(
get_lv (
visit_phibloc cs st r_used pc)) !!
r = (
get_lv st) !!
r.
Proof.
Remark defined_nowhere_stationary_aux_rec_helper:
forall r m (
x :
ssainstruction)
l',
maps_into_function f m ->
(
exists pref,
pref ++
x ::
l' =
m #
r) ->
ssai_in_function x f.
Proof.
intros.
destruct H0 as [
prefs H0].
assert (
In x m #
r).
rewrite <-
H0.
assert (
In x (
x::
l'));
auto.
apply in_app.
auto.
eapply H;
eauto.
Qed.
Remark defined_nowhere_stationary_aux:
forall st st'
r cs cs',
not_defined r ->
step (
cs,
st) =
inr (
cs',
st') ->
maps_into_function f (
cs_duc cs) ->
(
get_lv st) #
r = (
get_lv st') #
r.
Proof.
Remark cs_constant:
forall cs cs'
st st',
step (
cs,
st) =
inr (
cs',
st') ->
cs =
cs'.
Proof.
intros.
unfold step in *.
flatten H.
Qed.
Remark defined_nowhere_stationary:
forall r lv es is ,
not_defined r ->
initial_state =
Some is ->
PrimIter.iterate
_ _ step (
mkConstantState (
make_du_chain f)
(
make_predecessors (
fn_code f)
successors_instr),
is) =
Some (
Some (
lv,
es)) ->
lv #
r =
initial_values #
r.
Proof.
intros.
set (
P (
s:
const_state*
state) :=
forall cs st,
s = (
cs,
st) ->
((
get_lv st) #
r =
initial_values #
r)
/\ (
maps_into_function f (
cs_duc cs))).
set (
Q (
o:
option (
lattice_values *
exec_state)) :=
forall v es',
o =
Some (
v,
es') ->
v #
r =
initial_values #
r).
assert (
Q (
Some (
lv,
es))).
{
eapply PrimIter.iterate_prop with (
step :=
step) (
P :=
P) ;
unfold P,
Q.
+
intro.
destruct (
step a)
eqn:
eq.
unfold step in eq.
intros.
subst.
flatten eq.
assert ((
get_lv ((
nil,
i,
v,
es'):
state)) #
r =
v #
r)
by reflexivity.
destruct (
H2 c (
nil,
i,
v,
es'))
as [
Hlv Hduc];
auto.
intros.
subst.
destruct a as (
cs0,
st0).
destruct (
H2 cs0 st0)
as [
Hlv Hduc];
auto.
split.
rewrite <-
Hlv.
apply eq_sym.
eapply defined_nowhere_stationary_aux;
eauto.
eapply cs_constant in eq.
subst.
assumption.
+
eapply H1.
+
intros.
unfold initial_state in *.
flatten H0.
split; [
auto |
apply duc_maps_into_function].
split; [
auto |
apply duc_maps_into_function].
}
unfold Q in *.
apply H2 with es.
apply eq_refl.
Qed.
Lemma defined_nowhere_becomes_top:
forall r,
not_defined r ->
initial_values #
r =
L.bot -> (
fst fixpoint) #
r =
L.top.
Proof.
Lemma defined_nowhere_stays_top:
forall r lv,
not_defined r ->
initial_values #
r =
L.top ->
lv =
fst fixpoint ->
lv #
r =
L.top.
Proof.
Correctness lemma
Lemma post_fixpoint:
forall lv es,
fixpoint = (
lv,
es) ->
code_post_fixpoint lv es
/\
phicode_post_fixpoint lv es
/\
exec_flags_sound lv es.
Proof.
End CDS.
End DataflowSolver.
SCCP optimization, as an instantiation of the generic analysis.
Section Opt.
Definition
Definition handle_instr (
i:
instruction)
res :
option (
reg *
AVal.t) :=
match i with
|
Iop op regs r _ =>
let vs :=
List.map (
fun r => (
PMap.get r res))
regs in
Some (
r,
eval_static_operation op vs)
|
Iload _ _ _ r _ |
Icall _ _ _ r _ |
Ibuiltin _ _ (
BR r)
_ =>
Some (
r,
AVal.top)
|
_ =>
None
end.
Definition initial f :=
List.fold_left
(
fun vals r =>
PMap.set r AVal.top vals)
(
fn_params f)
(
PMap.init AVal.bot).
Definition fixpoint f:=
let fp := (
DataflowSolver.fixpoint f handle_instr (
initial f))
in
((
fun r =>
PMap.get r (
fst fp)),
snd fp).
Definition const_for_result (
a:
aval) :
option operation :=
match a with
|
I n =>
Some(
Ointconst n)
|
F n =>
if Compopts.generate_float_constants tt then Some(
Ofloatconst n)
else None
|
FS n =>
if Compopts.generate_float_constants tt then Some(
Osingleconst n)
else None
|
_ =>
None
end.
Definition transf_instr (
approx:
reg ->
AVal.t) (
n:
node)
i :=
match i with
|
Iop op args res s =>
match const_for_result (
eval_static_operation op (
map approx args))
with
|
Some cop =>
Iop cop nil res s
|
_ =>
i
end
|
_ =>
i
end.
Definition transf_function (
f:
function) :=
@
Opt.transf_function _ fixpoint transf_instr f.
Definition transf_fundef (
f:
fundef) :
fundef :=
AST.transf_fundef transf_function f.
Definition transf_program (
p:
program) :
program :=
AST.transform_program transf_fundef p.
End Opt.