Syntax and Sequential Semantics of Abstract Basic Blocks.
Require Import Setoid.
Require Import ImpPrelude.
Module Type PseudoRegisters.
Parameter t:
Set.
Parameter eq_dec:
forall (
x y:
t), {
x =
y } + {
x<>
y }.
End PseudoRegisters.
Parameters of the language of Basic Blocks
Module Type LangParam.
Declare Module R:
PseudoRegisters.
Parameter value:
Type.
Declare the type of operations
Parameter op:
Set.
Parameter genv:
Type.
Parameter op_eval:
genv ->
op ->
list value ->
option value.
End LangParam.
Syntax and (sequential) semantics of "abstract basic blocks"
Module MkSeqLanguage(
P:
LangParam).
Export P.
Local Open Scope list.
Section SEQLANG.
Variable ge:
genv.
Definition mem :=
R.t ->
value.
Definition assign (
m:
mem) (
x:
R.t) (
v:
value):
mem
:=
fun y =>
if R.eq_dec x y then v else m y.
Expressions
Inductive exp :=
|
PReg (
x:
R.t)
(* pseudo-register *)
|
Op (
o:
op) (
le:
list_exp)
(* operation *)
|
Old (
e:
exp)
(* evaluation of e in the initial state of the instruction (see inst below) *)
with list_exp :=
|
Enil
|
Econs (
e:
exp) (
le:
list_exp)
|
LOld (
le:
list_exp)
.
Fixpoint exp_eval (
e:
exp) (
m old:
mem):
option value :=
match e with
|
PReg x =>
Some (
m x)
|
Op o le =>
match list_exp_eval le m old with
|
Some lv =>
op_eval ge o lv
| _ =>
None
end
|
Old e =>
exp_eval e old old
end
with list_exp_eval (
le:
list_exp) (
m old:
mem):
option (
list value) :=
match le with
|
Enil =>
Some nil
|
Econs e le' =>
match exp_eval e m old,
list_exp_eval le' m old with
|
Some v,
Some lv =>
Some (
v::
lv)
| _, _ =>
None
end
|
LOld le =>
list_exp_eval le old old
end.
An instruction represents a sequence of assignments where Old refers to the initial state of the sequence.
Definition inst :=
list (
R.t *
exp).
Fixpoint inst_run (
i:
inst) (
m old:
mem):
option mem :=
match i with
|
nil =>
Some m
| (
x,
e)::
i' =>
match exp_eval e m old with
|
Some v' =>
inst_run i' (
assign m x v')
old
|
None =>
None
end
end.
A basic block is a sequence of instructions.
Definition bblock :=
list inst.
Fixpoint run (
p:
bblock) (
m:
mem):
option mem :=
match p with
|
nil =>
Some m
|
i::
p' =>
match inst_run i m m with
|
Some m' =>
run p' m'
|
None =>
None
end
end.
Lemma assign_eq m x v:
(
assign m x v)
x =
v.
Proof.
Lemma assign_diff m x y v:
x<>
y -> (
assign m x v)
y =
m y.
Proof.
Lemma assign_skips m x y:
(
assign m x (
m x))
y =
m y.
Proof.
Lemma assign_swap m x1 v1 x2 v2 y:
x1 <>
x2 -> (
assign (
assign m x1 v1)
x2 v2)
y = (
assign (
assign m x2 v2)
x1 v1)
y.
Proof.
A small theory of bblock simulation
Definition res_eq (
om1 om2:
option mem):
Prop :=
match om1 with
|
Some m1 =>
exists m2,
om2 =
Some m2 /\
forall x,
m1 x =
m2 x
|
None =>
om2 =
None
end.
Scheme exp_mut :=
Induction for exp Sort Prop
with list_exp_mut :=
Induction for list_exp Sort Prop.
Lemma exp_equiv e old1 old2:
(
forall x,
old1 x =
old2 x) ->
forall m1 m2, (
forall x,
m1 x =
m2 x) ->
(
exp_eval e m1 old1) = (
exp_eval e m2 old2).
Proof.
intros H1.
induction e using exp_mut with (
P0:=
fun l =>
forall m1 m2, (
forall x,
m1 x =
m2 x) ->
list_exp_eval l m1 old1 =
list_exp_eval l m2 old2);
cbn;
try congruence;
auto.
-
intros;
erewrite IHe;
eauto.
-
intros;
erewrite IHe,
IHe0;
auto.
Qed.
Definition bblock_simu (
p1 p2:
bblock):
Prop
:=
forall m, (
run p1 m) <>
None ->
res_eq (
run p1 m) (
run p2 m).
Lemma inst_equiv_refl i old1 old2:
(
forall x,
old1 x =
old2 x) ->
forall m1 m2, (
forall x,
m1 x =
m2 x) ->
res_eq (
inst_run i m1 old1) (
inst_run i m2 old2).
Proof.
intro H;
induction i as [ | [
x e]];
cbn;
eauto.
intros m1 m2 H1.
erewrite exp_equiv;
eauto.
destruct (
exp_eval e m2 old2);
cbn;
auto.
apply IHi.
unfold assign;
intro y.
destruct (
R.eq_dec x y);
auto.
Qed.
Lemma bblock_equiv_refl p:
forall m1 m2, (
forall x,
m1 x =
m2 x) ->
res_eq (
run p m1) (
run p m2).
Proof.
induction p as [ |
i p'];
cbn;
eauto.
intros m1 m2 H;
lapply (
inst_equiv_refl i m1 m2);
auto.
intros X;
lapply (
X m1 m2);
auto;
clear X.
destruct (
inst_run i m1 m1);
cbn.
-
intros [
m3 [
H1 H2]];
rewrite H1;
cbn;
auto.
-
intros H1;
rewrite H1;
cbn;
auto.
Qed.
Lemma res_eq_sym om1 om2:
res_eq om1 om2 ->
res_eq om2 om1.
Proof.
destruct om1; cbn.
- intros [m2 [H1 H2]]; subst; cbn. eauto.
- intros; subst; cbn; eauto.
Qed.
Lemma res_eq_trans (
om1 om2 om3:
option mem):
(
res_eq om1 om2) -> (
res_eq om2 om3) -> (
res_eq om1 om3).
Proof.
destruct om1;
cbn.
-
intros [
m2 [
H1 H2]];
subst;
cbn.
intros [
m3 [
H3 H4]];
subst;
cbn.
eapply ex_intro;
intuition eauto.
rewrite H2;
auto.
-
intro;
subst;
cbn;
auto.
Qed.
Lemma bblock_simu_alt p1 p2:
bblock_simu p1 p2 <-> (
forall m1 m2, (
forall x,
m1 x =
m2 x) -> (
run p1 m1)<>
None ->
res_eq (
run p1 m1) (
run p2 m2)).
Proof.
Lemma run_app p1:
forall m1 p2,
run (
p1++
p2)
m1 =
match run p1 m1 with
|
Some m2 =>
run p2 m2
|
None =>
None
end.
Proof.
induction p1;
cbn;
try congruence.
intros;
destruct (
inst_run _ _ _);
cbn;
auto.
Qed.
Lemma run_app_None p1 m1 p2:
run p1 m1 =
None ->
run (
p1++
p2)
m1 =
None.
Proof.
intro H;
rewrite run_app.
rewrite H;
auto.
Qed.
Lemma run_app_Some p1 m1 m2 p2:
run p1 m1 =
Some m2 ->
run (
p1++
p2)
m1 =
run p2 m2.
Proof.
intros H;
rewrite run_app.
rewrite H;
auto.
Qed.
End SEQLANG.
Terms in the symbolic execution
Such a term represents the successive computations in one given pseudo-register.
The hid has no formal semantics: it is only used by the hash-consing oracle (itself dynamically checked to behave like an identity function).
Module Terms.
Inductive term :=
|
Input (
x:
R.t) (
hid:
hashcode)
|
App (
o:
op) (
l:
list_term) (
hid:
hashcode)
with list_term :=
|
LTnil (
hid:
hashcode)
|
LTcons (
t:
term) (
l:
list_term) (
hid:
hashcode)
.
Scheme term_mut :=
Induction for term Sort Prop
with list_term_mut :=
Induction for list_term Sort Prop.
Bind Scope pattern_scope with term.
Delimit Scope term_scope with term.
Delimit Scope pattern_scope with pattern.
Local Open Scope pattern_scope.
Notation "[ ]" := (
LTnil _) (
format "[ ]"):
pattern_scope.
Notation "[ x ]" := (
LTcons x [ ] _):
pattern_scope.
Notation "[ x ; y ; .. ; z ]" := (
LTcons x (
LTcons y .. (
LTcons z (
LTnil _) _) .. _) _):
pattern_scope.
Notation "o @ l" := (
App o l _) (
at level 50,
no associativity):
pattern_scope.
Import HConsingDefs.
Notation "[ ]" := (
LTnil unknown_hid) (
format "[ ]"):
term_scope.
Notation "[ x ]" := (
LTcons x []%
term unknown_hid):
term_scope.
Notation "[ x ; y ; .. ; z ]" := (
LTcons x (
LTcons y .. (
LTcons z (
LTnil unknown_hid)
unknown_hid) ..
unknown_hid)
unknown_hid):
term_scope.
Notation "o @ l" := (
App o l unknown_hid) (
at level 50,
no associativity):
term_scope.
Fixpoint term_eval (
ge:
genv) (
t:
term) (
m:
mem):
option value :=
match t with
|
Input x _ =>
Some (
m x)
|
o @
l =>
match list_term_eval ge l m with
|
Some v =>
op_eval ge o v
| _ =>
None
end
end
with list_term_eval ge (
l:
list_term) (
m:
mem) {
struct l}:
option (
list value) :=
match l with
| [] =>
Some nil
|
LTcons t l' _ =>
match term_eval ge t m,
list_term_eval ge l' m with
|
Some v,
Some lv =>
Some (
v::
lv)
| _, _ =>
None
end
end.
Definition term_get_hid (
t:
term):
hashcode :=
match t with
|
Input _
hid =>
hid
|
App _ _
hid =>
hid
end.
Definition list_term_get_hid (
l:
list_term):
hashcode :=
match l with
|
LTnil hid =>
hid
|
LTcons _ _
hid =>
hid
end.
Fixpoint allvalid ge (
l:
list term)
m :
Prop :=
match l with
|
nil =>
True
|
t::
nil =>
term_eval ge t m <>
None
|
t::
l' =>
term_eval ge t m <>
None /\
allvalid ge l' m
end.
Lemma allvalid_extensionality ge (
l:
list term)
m:
allvalid ge l m <-> (
forall t,
List.In t l ->
term_eval ge t m <>
None).
Proof.
induction l as [|t l]; cbn; try (tauto).
destruct l.
- intuition (congruence || eauto).
- rewrite IHl; clear IHl. intuition (congruence || eauto).
Qed.
Rewriting rules in the symbolic execution
The symbolic execution is parametrized by rewriting rules on pseudo-terms.
Record pseudo_term:
Type :=
intro_fail {
mayfail:
list term;
effect:
term
}.
Simulation relation between a term and a pseudo-term
Definition match_pt (
t:
term) (
pt:
pseudo_term) :=
(
forall ge m,
term_eval ge t m <>
None <->
allvalid ge pt.(
mayfail)
m)
/\ (
forall ge m0 m1,
term_eval ge t m0 =
Some m1 ->
term_eval ge pt.(
effect)
m0 =
Some m1).
Lemma inf_option_equivalence (
A:
Type) (
o1 o2:
option A):
(
o1 <>
None ->
o1 =
o2) <-> (
forall m1,
o1 =
Some m1 ->
o2 =
Some m1).
Proof.
destruct o1; intuition (congruence || eauto).
symmetry; eauto.
Qed.
Lemma intro_fail_correct (
l:
list term) (
t:
term) :
(
forall ge m,
term_eval ge t m <>
None <->
allvalid ge l m) ->
match_pt t (
intro_fail l t).
Proof.
unfold match_pt;
cbn;
intros;
intuition congruence.
Qed.
#[
global]
Hint Resolve intro_fail_correct:
wlp.
The default reduction of a term to a pseudo-term
Definition identity_fail (
t:
term):=
intro_fail (
t::
nil)
t.
Lemma identity_fail_correct (
t:
term):
match_pt t (
identity_fail t).
Proof.
Global Opaque identity_fail.
#[
global]
Hint Resolve identity_fail_correct:
wlp.
The reduction for constant term
Definition nofail (
is_constant:
op ->
bool) (
t:
term):=
match t with
|
Input x _ =>
intro_fail nil t
|
o @ [] =>
if is_constant o then (
intro_fail nil t)
else (
identity_fail t)
| _ =>
identity_fail t
end.
Lemma nofail_correct (
is_constant:
op ->
bool)
t:
(
forall ge o,
is_constant o =
true ->
op_eval ge o nil <>
None) ->
match_pt t (
nofail is_constant t).
Proof.
destruct t;
cbn.
+
intros;
eapply intro_fail_correct;
cbn;
intuition congruence.
+
intros;
destruct l;
cbn;
auto with wlp.
destruct (
is_constant o)
eqn:
Heqo;
cbn;
intuition eauto with wlp.
eapply intro_fail_correct;
cbn;
intuition eauto with wlp.
Qed.
Global Opaque nofail.
#[
global]
Hint Resolve nofail_correct:
wlp.
Term equivalence preserve the simulation by pseudo-terms
Definition term_equiv t1 t2:=
forall ge m,
term_eval ge t1 m =
term_eval ge t2 m.
Global Instance term_equiv_Equivalence :
Equivalence term_equiv.
Proof.
Lemma match_pt_term_equiv t1 t2 pt:
term_equiv t1 t2 ->
match_pt t1 pt ->
match_pt t2 pt.
Proof.
unfold match_pt,
term_equiv.
intros H.
intuition;
try (
erewrite <-
H1 in * |- *;
congruence).
erewrite <-
H2;
eauto;
congruence.
Qed.
#[
global]
Hint Resolve match_pt_term_equiv:
wlp.
Other generic reductions
Definition app_fail (
l:
list term) (
pt:
pseudo_term):
pseudo_term :=
{|
mayfail :=
List.rev_append l pt.(
mayfail);
effect :=
pt.(
effect) |}.
Lemma app_fail_allvalid_correct l pt t1 t2:
forall
(
V1:
forall (
ge :
genv) (
m :
mem),
term_eval ge t1 m <>
None <->
allvalid ge (
mayfail pt)
m)
(
V2:
forall (
ge :
genv) (
m :
mem),
term_eval ge t2 m <>
None <->
allvalid ge (
mayfail {|
mayfail :=
t1 ::
l;
effect :=
t1 |})
m)
(
ge :
genv) (
m :
mem),
term_eval ge t2 m <>
None <->
allvalid ge (
mayfail (
app_fail l pt))
m.
Proof.
Local Hint Resolve app_fail_allvalid_correct:
core.
Lemma app_fail_correct l pt t1 t2:
match_pt t1 pt ->
match_pt t2 {|
mayfail:=
t1::
l;
effect:=
t1 |} ->
match_pt t2 (
app_fail l pt).
Proof.
unfold match_pt in * |- *;
intros (
V1 &
E1) (
V2 &
E2);
split;
intros ge m;
try (
eauto;
fail).
Qed.
Extraction Inline app_fail.
Import ImpCore.Notations.
Local Open Scope impure_scope.
Specification of rewriting functions in parameter of the symbolic execution: in the impure monad, because the rewriting functions produce hash-consed terms (wrapped in pseudo-terms).
Record reduction:= {
result:>
term -> ??
pseudo_term;
result_correct:
forall t,
WHEN result t ~>
pt THEN match_pt t pt;
}.
#[
global]
Hint Resolve result_correct:
wlp.
End Terms.
End MkSeqLanguage.
Module Type SeqLanguage.
Declare Module LP:
LangParam.
Include MkSeqLanguage LP.
End SeqLanguage.