Theory Nonrelational_Ops
theory Nonrelational_Ops
imports
Special_Ops
Exec_Backward
"Voblint_Framework.DG_Local_State_Spec"
"Voblint_Exec.Default_St_Restriction_Refinement"
begin
unbundle default_st_syntax
section ‹The primitives one abstract value per variable is built from›
text ‹
Say what an expression evaluates to, which comparisons the domain decides, how
a guard refines its operands, how the two special calls combine two values,
and every edge of a compiled graph is determined; the whole-value element is
the class \<^const>‹top›. ‹nonrelational_ops› is that bundle of primitive choices, written
once per domain. Everything else is derived from it: the guard filter on the
executable store here, the abstract branch and the transfer in
‹Nonrelational_Transfer›, which certifies one such bundle.
Two constructions follow from it here. ‹generic_enter_st_for› is procedure
entry on the executable store: evaluate the actuals in the caller's state,
reset the callee frame to the whole-value element, bind the formals.
‹generic_tf_st_for› is the per-edge executable step, and
‹generic_tf_st_for_commute› says it agrees with the abstract dispatcher
‹generic_tf_abs› on the function the executable store represents, provided the guard
filter commutes with the abstract branch on the state at hand.
‹generic_tf_abs› takes the abstract branch as a separate argument so this
commutation lemma needs no soundness locale. For a certified bundle the branch
is not an independent choice: ‹sound_nonrelational_ops› instantiates
it with the backward branch of ‹n_aval› and ‹n_refine› and discharges the
guard obligation once, generically.
›
text ‹
The bundle is constrained to ‹'a::{bot, top}› and the executable step to
‹'a::executable_domain›, never ‹'a::numeric_domain›. ‹numeric_domain› fixes
‹gamma› as a class operation, and code generation for a definition at that
sort must resolve ‹gamma›'s code equation for the concrete type even though
nothing here calls it. ‹ivl›'s ‹gamma_ivl› has no well-sorted code equation
(‹int› is not of sort ‹enum›), so that unused obligation breaks unrelated
‹by eval› proofs downstream.
›
text ‹
The comparisons a domain decides, each answering ‹None› when it cannot tell.
›
record 'a query_ops =
q_less :: "'a ⇒ 'a ⇒ bool option"
q_eq :: "'a ⇒ 'a ⇒ bool option"
record ('a::"{bot, top}") nonrelational_ops =
n_aval :: "exp => (vname => 'a) => 'a"
n_query :: "'a query_ops"
n_refine :: "'a refine_ops"
n_special :: "'a special_ops"
text ‹
The guard filter on the executable store is derived from the evaluator and the
refinement operations, never supplied: the bundle determines it.
›
abbreviation n_bfilter ::
"'a::executable_domain nonrelational_ops
⇒ (vname ⇒ bool) ⇒ exp ⇒ bool ⇒ 'a default_st ⇒ 'a default_st" where
"n_bfilter ops ≡ branch_st_with (n_aval ops) (n_refine ops)"
subsection ‹Procedure entry›
text ‹
‹generic_enter_st_for› opens a callee frame with every local at ‹top›, then
binds the formals to the arguments evaluated in the caller's store.
›
definition generic_enter_st_for ::
"'a::{bot, top} nonrelational_ops => (vname => bool) => call_info =>
'a default_st => 'a default_st" where
"generic_enter_st_for ops 𝒢 ci s =
bind_formals_default_st 𝒢 (ci_formals ci)
(map (λe. n_aval ops e (ρ⇘𝒢⇙ s)) (ci_args ci))
(enter_frame_D_default_st top s)"
subsection ‹The per-edge step, on both stores›
text ‹
‹generic_tf_st_for› is the executable step on the split local/global store,
routing each write through ‹location_of›. ‹generic_tf_abs› states the same
edge semantics on a plain abstract state via ‹local_spec_step›.
›
fun generic_tf_st_for ::
"'a::executable_domain nonrelational_ops => (vname => bool) => edge_action =>
'a default_st => 'a default_st" where
"generic_tf_st_for ops 𝒢 EA_Nop s = s"
| "generic_tf_st_for ops 𝒢 (EA_Assign x a) s =
s⟨location_of 𝒢 x := n_aval ops a (ρ⇘𝒢⇙ s)⟩"
| "generic_tf_st_for ops 𝒢 (EA_Special sc x) s =
s⟨location_of 𝒢 x :=
(case sc of
Nondet_Int => top
| Min a b => special_min (n_special ops)
(n_aval ops a (ρ⇘𝒢⇙ s))
(n_aval ops b (ρ⇘𝒢⇙ s))
| Max a b => special_max (n_special ops)
(n_aval ops a (ρ⇘𝒢⇙ s))
(n_aval ops b (ρ⇘𝒢⇙ s)))⟩"
| "generic_tf_st_for ops 𝒢 (EA_Assume b) s = n_bfilter ops 𝒢 b True s"
| "generic_tf_st_for ops 𝒢 (EA_AssumeNot b) s = n_bfilter ops 𝒢 b False s"
| "generic_tf_st_for ops 𝒢 (EA_Body p) s = s"
| "generic_tf_st_for ops 𝒢 (EA_Ret None p) s = s"
| "generic_tf_st_for ops 𝒢 (EA_Ret (Some a) p) s =
s⟨location_of 𝒢 ret_var := n_aval ops a (ρ⇘𝒢⇙ s)⟩"
| "generic_tf_st_for ops 𝒢 (EA_Check l cnd) s = s"
definition generic_tf_abs ::
"'a::{bot, top} nonrelational_ops => (exp => bool => 'a abs_state => 'a abs_state) =>
edge_action => 'a abs_state => 'a abs_state" where
"generic_tf_abs ops br =
local_spec_step
(λsigma. sigma)
(λx a sigma. sigma(x := n_aval ops a sigma))
(λsc x sigma. sigma(x := (case sc of
Nondet_Int => top
| Min a b => special_min (n_special ops) (n_aval ops a sigma) (n_aval ops b sigma)
| Max a b => special_max (n_special ops) (n_aval ops a sigma) (n_aval ops b sigma))))
br
(λp sigma. sigma)
(λeo p sigma. case eo of None => sigma | Some a => sigma(ret_var := n_aval ops a sigma))
(λevt sigma. sigma)"
lemma generic_tf_abs_simps [simp]:
"generic_tf_abs ops br EA_Nop sigma = sigma"
"generic_tf_abs ops br (EA_Assign x a) sigma = sigma(x := n_aval ops a sigma)"
"generic_tf_abs ops br (EA_Special sc x) sigma =
sigma(x := (case sc of
Nondet_Int => top
| Min a b => special_min (n_special ops) (n_aval ops a sigma) (n_aval ops b sigma)
| Max a b => special_max (n_special ops) (n_aval ops a sigma) (n_aval ops b sigma)))"
"generic_tf_abs ops br (EA_Assume b) = br b True"
"generic_tf_abs ops br (EA_AssumeNot b) = br b False"
"generic_tf_abs ops br (EA_Body p) sigma = sigma"
"generic_tf_abs ops br (EA_Ret None p) sigma = sigma"
"generic_tf_abs ops br (EA_Ret (Some a) p) sigma = sigma(ret_var := n_aval ops a sigma)"
"generic_tf_abs ops br (EA_Check l cnd) sigma = sigma"
by (simp_all add: generic_tf_abs_def)
text ‹
Every case but the guard rewrites by \<^const>‹default_st_to_fun›'s own
update equation, which is what leaves the guard as the only obligation an
instance still has to discharge.
›
theorem generic_tf_st_for_commute:
fixes ops :: "'a::executable_domain nonrelational_ops"
assumes branch:
"⋀b pol. ρ⇘𝒢⇙ (n_bfilter ops 𝒢 b pol s) =
br b pol (ρ⇘𝒢⇙ s)"
shows
"ρ⇘𝒢⇙ (generic_tf_st_for ops 𝒢 a s) =
generic_tf_abs ops br a (ρ⇘𝒢⇙ s)"
proof (cases a)
case EA_Nop
then show ?thesis by simp
next
case (EA_Assign x e)
then show ?thesis by simp
next
case (EA_Special sc x)
then show ?thesis by (cases sc) simp_all
next
case (EA_Assume b)
then show ?thesis by (simp add: branch)
next
case (EA_AssumeNot b)
then show ?thesis by (simp add: branch)
next
case (EA_Body p)
then show ?thesis by simp
next
case (EA_Ret eo p)
then show ?thesis by (cases eo) simp_all
next
case (EA_Check l cnd)
then show ?thesis by simp
qed
unbundle no default_st_syntax
end