Theory CFG_Exec
theory CFG_Exec
imports CFG_Transfer
begin
section ‹How a control-flow graph runs›
text ‹
‹cstep› executes an arbitrary CFG over a node, a store, and a stack of suspended
activations. Each ‹cframe› records where to resume, which variable receives the
result, and the caller's store. There are three rules, one per graph phenomenon:
follow an ‹intra› edge and apply its transfer; follow a ‹calls› edge, enter the callee
and push a frame; at a \<^term>‹FunctionResult›, pop the top frame and combine the
callee's store into the caller's.
A return follows no edge. The stack supplies the continuation, so one
\<^term>‹FunctionResult› node serves every caller of a procedure and recursion needs no
duplicated nodes. Nothing here mentions the compiler: this is the execution of any
graph, just as ‹valid_activation_trace› is the activation-trace semantics of any graph.
›
type_synonym cframe = "cfg_node × vname option × store"
type_synonym cconf = "cfg_node × store × cframe list"
inductive cstep :: "(vname ⇒ bool) ⇒ cfg ⇒ cconf ⇒ cconf ⇒ bool"
("(_,_ ⊢/ _ →⇩c/ _)" [51, 51, 51, 51] 50) for 𝒢 and g where
Intra [intro]:
"(u, a, v) ∈ intra g ⟹ s' ∈ edge_step a s ⟹
𝒢, g ⊢ (u, s, stk) →⇩c (v, s', stk)"
| Call [intro]:
"(u, CallEdge dst pars actuals, FunctionEntry q, cont) ∈ calls g ⟹
𝒢, g ⊢ (u, s, stk)
→⇩c (FunctionEntry q, call_enter 𝒢 (CallEdge dst pars actuals) s, (cont, dst, s) # stk)"
| Return [intro]:
"𝒢, g ⊢ (FunctionResult q, t, (cont, dst, caller) # stk)
→⇩c (cont, combine_collect 𝒢 dst caller t, stk)"
abbreviation csteps :: "(vname ⇒ bool) ⇒ cfg ⇒ cconf ⇒ cconf ⇒ bool"
("(_,_ ⊢/ _ →⇩c⇧*/ _)" [51, 51, 51, 51] 50)
where "csteps 𝒢 g x y ≡ star (cstep 𝒢 g) x y"
text ‹One inversion rule, because the three clauses are told apart by the edge taken rather
than by the shape of the configuration. It stays plain ‹[elim]› so the classical reasoner
does not split every ‹cstep› hypothesis three ways before trying anything else.›
inductive_cases cstep_E [elim]: "𝒢, g ⊢ (u, s, stk) →⇩c y"
subsection ‹Single-step and small-step lemmas›
text ‹
Introduction lemmas for the store-preserving edge actions the simulation steps
through: body, assume and assume-not.
›
lemma cstep_body:
assumes "(u, EA_Body p, v) ∈ intra g"
shows "𝒢, g ⊢ (u, s, stk) →⇩c (v, s, stk)"
by (rule cstep.Intra[OF assms]) simp
lemma cstep_assume:
assumes "(u, EA_Assume b, v) ∈ intra g" and "truthy (⟦b⟧⇩e s)"
shows "𝒢, g ⊢ (u, s, stk) →⇩c (v, s, stk)"
by (rule cstep.Intra[OF assms(1)]) (use assms(2) in simp)
lemma cstep_assume_not:
assumes "(u, EA_AssumeNot b, v) ∈ intra g" and "¬ truthy (⟦b⟧⇩e s)"
shows "𝒢, g ⊢ (u, s, stk) →⇩c (v, s, stk)"
by (rule cstep.Intra[OF assms(1)]) (use assms(2) in simp)
subsection ‹Intra-only paths as stack-preserving runs›
text ‹An \<^const>‹intra_path› is a ‹cstep› run at any stack: every \<^const>‹cfg_intra_step› is a
‹cstep.Intra›, which passes the stack through untouched. Callers that only need to move
along local edges can therefore reason on ‹(node, store)› pairs and lift the result here.›
lemma intra_path_imp_cstep_star:
"intra_path g x y ⟹ 𝒢, g ⊢ (fst x, snd x, stk) →⇩c⇧* (fst y, snd y, stk)"
proof (induction rule: star.induct)
case (refl a) show ?case by simp
next
case (step a b c)
obtain ua sa where a: "a = (ua, sa)" by (cases a)
obtain ub sb where b: "b = (ub, sb)" by (cases b)
from step.hyps(1) a b obtain e where "(ua, e, ub) ∈ intra g" "sb ∈ edge_step e sa" by auto
hence "𝒢, g ⊢ (ua, sa, stk) →⇩c (ub, sb, stk)" by (rule cstep.Intra)
with step.IH a b show ?case by (auto intro: star.step)
qed
subsection ‹Activation-stack matching›
text ‹Both stacks record procedure activations. ‹act_frames› removes the CFG-only
continuation component and retains the caller store and destination used by the source.›
fun act_frames :: "frame list ⇒ (store × vname option) list" where
"act_frames [] = []"
| "act_frames (Frame s d # frs) = (s, d) # act_frames frs"
definition cframe_act :: "cframe ⇒ store × vname option" where
"cframe_act cf = (case cf of (cont, d, s) ⇒ (s, d))"
text ‹‹frames_match› ties a source frame stack to a CFG activation stack: the activation
frames agree, in order, on caller store and destination (the continuation node is the CFG's
own bookkeeping and is not visible to the source).›
definition frames_match :: "frame list ⇒ cframe list ⇒ bool" where
"frames_match frs stk = (act_frames frs = map cframe_act stk)"
lemma frames_match_Nil [simp]: "frames_match [] []"
by (simp add: frames_match_def)
text ‹An activation frame matches the top CFG activation on caller store and destination.›
lemma frames_match_activation:
"frames_match (Frame s d # frs) ((cont, d, s) # stk)
= frames_match frs stk"
by (simp add: frames_match_def cframe_act_def)
end