Theory Voblint_Compile.Procedure_Ownership
theory Procedure_Ownership
imports Compile_Invariants "Voblint_CFG.Activation_Trace_Def"
begin
section ‹Each procedure owns a disjoint block of nodes›
text ‹
An activation's own local \<^const>‹path_of› stays inside one compiled procedure fragment. A
∗‹fragment› of procedure ‹r› compiled over ‹[m, m')› is \<^term>‹FunctionEntry r›,
\<^term>‹FunctionResult r›, and the \<^term>‹Statement› nodes in that counter range. Every intra edge
keeps both endpoints in one fragment, every call edge keeps its source and continuation there
(the callee \<^term>‹FunctionEntry› is the only cross-fragment endpoint), and distinct fragments are
node-disjoint, so a single activation entered at \<^term>‹FunctionEntry p› reaches
\<^term>‹FunctionResult q› only for ‹p = q›.
›
definition pfn :: "pname ⇒ nat ⇒ nat ⇒ cfg_node set" where
"pfn r m m' = insert (FunctionEntry r) (insert (FunctionResult r) (Statement ` {m..<m'}))"
lemma pfn_disjoint:
"p ≠ r ⟹ mp' ≤ m ∨ m' ≤ mp ⟹ x ∈ pfn p mp mp' ⟹ x ∉ pfn r m m'"
by (auto simp: pfn_def)
subsection ‹Edges keep endpoints in one fragment›
text ‹At command level both endpoints lie in the fragment's own node set or are its
continuation (‹compile_E_shape›). At procedure level the continuation is the epilogue node,
which is inside the fragment's range, so the extra alternative collapses.›
lemma compile_K_shape:
"compile Π p c k n = (n', en, E, K) ⟹ (u, ce, tgt, af) ∈ K
⟹ (∃j. u = Statement j) ∧ (∃q. tgt = FunctionEntry q)
∧ (af = k ∨ (∃j. af = Statement j))"
proof (induction c arbitrary: k n n' en E K)
case (Seq c1 c2)
from Seq.prems(1) obtain n1 E1 K1 n2 E2 K2 where
c1: "compile Π p c1 (Statement (n + csize c1)) n = (n1, Statement n, E1, K1)"
and c2: "compile Π p c2 k (n + csize c1) = (n2, Statement (n + csize c1), E2, K2)"
and K: "K = K1 ∪ K2"
by (rule compile_SeqE)
from Seq.prems(2) K show ?case using Seq.IH(1)[OF c1] Seq.IH(2)[OF c2] by auto
next
case (If i b c1 c2)
from If.prems(1) obtain n1 E1 K1 n2 E2 K2 where
c1: "compile Π p c1 k (Suc n) = (n1, Statement (Suc n), E1, K1)"
and c2: "compile Π p c2 k (Suc n + csize c1)
= (n2, Statement (Suc n + csize c1), E2, K2)"
and K: "K = K1 ∪ K2"
by (rule compile_IfE)
from If.prems(2) K show ?case using If.IH(1)[OF c1] If.IH(2)[OF c2] by auto
next
case (While i b c)
from While.prems(1) obtain n1 E1 K1 where
c1: "compile Π p c (Statement n) (Suc n) = (n1, Statement (Suc n), E1, K1)"
and K: "K = K1"
by (rule compile_WhileE)
from While.prems(2) K show ?case using While.IH[OF c1] by auto
qed (auto split: if_splits option.splits)
lemma compile_intra_pfn:
assumes cp: "compile Π p c k n = (n', en, E, K)" and e: "(u, a, v) ∈ E"
shows "u ∈ insert k (pfn p n n') ∧ v ∈ insert k (pfn p n n')"
proof -
have rng: "frag_stmts E K ⊆ {n..<n'} ∪ kstmt k" using compile_frag_stmts_range[OF cp] .
from compile_E_shape[OF cp e] obtain ku where u: "u = Statement ku"
and vshape: "v = k ∨ v = FunctionResult p ∨ (∃kv. v = Statement kv)" by blast
have "ku ∈ frag_stmts E K" using frag_stmts_E_srcI[of ku a v E] e u by simp
hence uin: "u ∈ insert k (pfn p n n')" using rng u by (auto simp: pfn_def)
have vin: "v ∈ insert k (pfn p n n')"
proof (cases "v = k ∨ v = FunctionResult p")
case True then show ?thesis by (auto simp: pfn_def)
next
case False
then obtain kv where v: "v = Statement kv" using vshape by blast
have "kv ∈ frag_stmts E K" using frag_stmts_E_tgtI[of u a kv E] e v by simp
then show ?thesis using rng v False by (auto simp: pfn_def)
qed
from uin vin show ?thesis ..
qed
lemma compile_calls_pfn:
assumes cp: "compile Π p c k n = (n', en, E, K)" and e: "(u, ce, tgt, af) ∈ K"
shows "u ∈ insert k (pfn p n n') ∧ af ∈ insert k (pfn p n n')"
proof -
have rng: "frag_stmts E K ⊆ {n..<n'} ∪ kstmt k" using compile_frag_stmts_range[OF cp] .
from compile_K_shape[OF cp e] obtain ku where u: "u = Statement ku"
and afshape: "af = k ∨ (∃kaf. af = Statement kaf)" by blast
have "ku ∈ frag_stmts E K" using frag_stmts_K_srcI[of ku ce tgt af K E] e u by simp
hence uin: "u ∈ insert k (pfn p n n')" using rng u by (auto simp: pfn_def)
have "af ∈ insert k (pfn p n n')"
proof (cases "af = k")
case True then show ?thesis by simp
next
case False
then obtain kaf where af: "af = Statement kaf" using afshape by blast
have "kaf ∈ frag_stmts E K" using frag_stmts_K_tgtI[of u ce tgt kaf K E] e af by simp
then show ?thesis using rng af False by (auto simp: pfn_def)
qed
with uin show ?thesis ..
qed
text ‹The entry wiring edge targets the first statement index of the fragment, which is what
identifies a fragment by its start counter alone.›
lemma compile_proc_entry_edge:
assumes cp: "compile_proc Π p decl n = (n', E, K)"
shows "(FunctionEntry p, EA_Body p, Statement n) ∈ E"
using cp by auto
lemma compile_proc_intra_pfn:
assumes cp: "compile_proc Π r decl n = (n', E, K)" and e: "(u, a, v) ∈ E"
shows "u ∈ pfn r n n' ∧ v ∈ pfn r n n'"
proof -
from cp obtain Eb where
cb: "compile Π r (body decl) (Statement (n + csize (body decl))) n
= (n + csize (body decl), Statement n, Eb, K)"
and E: "E = insert (FunctionEntry r, EA_Body r, Statement n)
(insert (Statement (n + csize (body decl)), EA_Ret None r, FunctionResult r) Eb)"
and n': "n' = Suc (n + csize (body decl))"
by (rule compile_procE)
have kin: "Statement (n + csize (body decl)) ∈ pfn r n n'"
using n' by (auto simp: pfn_def)
have entryin: "Statement n ∈ pfn r n n'"
using n' csize_pos[of "body decl"] by (auto simp: pfn_def)
have inner: "insert (Statement (n + csize (body decl)))
(pfn r n (n + csize (body decl))) ⊆ pfn r n n'"
using n' by (auto simp: pfn_def)
consider "(u, a, v) = (FunctionEntry r, EA_Body r, Statement n)"
| "(u, a, v) = (Statement (n + csize (body decl)), EA_Ret None r, FunctionResult r)"
| "(u, a, v) ∈ Eb"
using e E by (auto split: if_splits)
then show ?thesis
proof cases
case 1 then show ?thesis using entryin by (auto simp: pfn_def)
next
case 2 then show ?thesis using kin by (auto simp: pfn_def)
next
case 3 then show ?thesis using compile_intra_pfn[OF cb] inner by blast
qed
qed
lemma compile_proc_calls_pfn:
assumes cp: "compile_proc Π r decl n = (n', E, K)" and e: "(u, ce, tgt, af) ∈ K"
shows "u ∈ pfn r n n' ∧ af ∈ pfn r n n'"
proof -
from cp obtain Eb where
cb: "compile Π r (body decl) (Statement (n + csize (body decl))) n
= (n + csize (body decl), Statement n, Eb, K)"
and n': "n' = Suc (n + csize (body decl))"
by (rule compile_procE)
have inner: "insert (Statement (n + csize (body decl)))
(pfn r n (n + csize (body decl))) ⊆ pfn r n n'"
using n' by (auto simp: pfn_def)
show ?thesis using compile_calls_pfn[OF cb e] inner by blast
qed
text ‹A fragment of another procedure over a disjoint counter range sources none of its edges
in ‹pfn r m m'›.›
lemma compile_proc_src_notin_pfn:
assumes cp: "compile_proc Π p decl mp = (mp', Ep, Kp)"
and neq: "p ≠ r" and disjoint: "mp' ≤ m ∨ m' ≤ mp"
shows "(u, a, v) ∈ Ep ⟹ u ∉ pfn r m m'"
and "(u, ce, tgt, af) ∈ Kp ⟹ u ∉ pfn r m m'"
using compile_proc_intra_pfn[OF cp] compile_proc_calls_pfn[OF cp] pfn_disjoint[OF neq disjoint]
by blast+
lemma compile_procs_intra_src_notin_pfn:
assumes cps: "compile_procs Π ps n = (n', E, K)" and e: "(u, a, v) ∈ E"
and absent: "r ∉ set ps" and disjoint: "n' ≤ m ∨ m' ≤ n"
shows "u ∉ pfn r m m'"
proof -
from compile_procs_intra_origin[OF cps e] obtain p decl mp mp' Ep Kp
where "p ∈ set ps" and cp: "compile_proc Π p decl mp = (mp', Ep, Kp)"
and "n ≤ mp" "mp' ≤ n'" and ep: "(u, a, v) ∈ Ep" by blast
have "p ≠ r" using ‹p ∈ set ps› absent by blast
moreover have "mp' ≤ m ∨ m' ≤ mp" using ‹n ≤ mp› ‹mp' ≤ n'› disjoint by auto
ultimately show ?thesis using compile_proc_src_notin_pfn(1)[OF cp _ _ ep] by blast
qed
lemma compile_procs_calls_src_notin_pfn:
assumes cps: "compile_procs Π ps n = (n', E, K)" and e: "(u, ce, tgt, af) ∈ K"
and absent: "r ∉ set ps" and disjoint: "n' ≤ m ∨ m' ≤ n"
shows "u ∉ pfn r m m'"
proof -
from compile_procs_calls_origin[OF cps e] obtain p decl mp mp' Ep Kp
where "p ∈ set ps" and cp: "compile_proc Π p decl mp = (mp', Ep, Kp)"
and "n ≤ mp" "mp' ≤ n'" and ep: "(u, ce, tgt, af) ∈ Kp" by blast
have "p ≠ r" using ‹p ∈ set ps› absent by blast
moreover have "mp' ≤ m ∨ m' ≤ mp" using ‹n ≤ mp› ‹mp' ≤ n'› disjoint by auto
ultimately show ?thesis using compile_proc_src_notin_pfn(2)[OF cp _ _ ep] by blast
qed
subsection ‹A procedure entry has a unique out-edge›
text ‹Within a compiled procedure only the entry wiring edge sources from \<^term>‹FunctionEntry›
(bodies source from \<^term>‹Statement› nodes by ‹compile_E_shape›, the exit wiring from the exit
\<^term>‹Statement›), so the source name is the procedure and the target is unique.›
lemma compile_proc_entry_mem:
assumes cp: "compile_proc Π q decl n = (n', E, K)" and e: "(FunctionEntry r, a, v) ∈ E"
shows "r = q"
proof -
from cp obtain Eb where
cb: "compile Π q (body decl) (Statement (n + csize (body decl))) n
= (n + csize (body decl), Statement n, Eb, K)"
and E: "E = insert (FunctionEntry q, EA_Body q, Statement n)
(insert (Statement (n + csize (body decl)), EA_Ret None q, FunctionResult q) Eb)"
by (rule compile_procE)
have "⋀aa vv. (FunctionEntry r, aa, vv) ∉ Eb" using compile_E_shape[OF cb] by blast
then show ?thesis using e E by (auto split: if_splits)
qed
lemma compile_proc_entry_unique:
assumes cp: "compile_proc Π q decl n = (n', E, K)"
and e1: "(FunctionEntry r, a1, v1) ∈ E" and e2: "(FunctionEntry r, a2, v2) ∈ E"
shows "v1 = v2"
proof -
from cp obtain Eb where
cb: "compile Π q (body decl) (Statement (n + csize (body decl))) n
= (n + csize (body decl), Statement n, Eb, K)"
and E: "E = insert (FunctionEntry q, EA_Body q, Statement n)
(insert (Statement (n + csize (body decl)), EA_Ret None q, FunctionResult q) Eb)"
by (rule compile_procE)
have nb: "⋀aa vv. (FunctionEntry r, aa, vv) ∉ Eb" using compile_E_shape[OF cb] by blast
from e1 E nb have "v1 = Statement n" by (auto split: if_splits)
moreover from e2 E nb have "v2 = Statement n" by (auto split: if_splits)
ultimately show ?thesis by simp
qed
lemma compile_procs_entry_mem:
"compile_procs Π ps n = (n', E, K) ⟹ (FunctionEntry r, a, v) ∈ E ⟹ r ∈ set ps"
using compile_procs_intra_origin compile_proc_entry_mem by blast
text ‹Under ‹distinct ps› a member's fragment owns every edge of the pass that sources in the
member's nodes: the other members are compiled over disjoint counter ranges, and their
edges stay in their own fragments.›
lemma compile_procs_member_frag:
assumes "compile_procs Π ps n = (n', E, K)" and "distinct ps"
and "r ∈ set ps" and "Π r = Some decl"
shows "∃m m' Er Kr. compile_proc Π r decl m = (m', Er, Kr) ∧ n ≤ m ∧ m' ≤ n'
∧ Er ⊆ E ∧ Kr ⊆ K
∧ (∀u a v. (u, a, v) ∈ E ⟶ u ∈ pfn r m m' ⟶ (u, a, v) ∈ Er)
∧ (∀u ce tgt af. (u, ce, tgt, af) ∈ K ⟶ u ∈ pfn r m m'
⟶ (u, ce, tgt, af) ∈ Kr)"
using assms
proof (induction ps arbitrary: n n' E K)
case Nil then show ?case by simp
next
case (Cons q qs)
show ?case
proof (cases "Π q")
case None
with Cons.prems have "compile_procs Π qs n = (n', E, K)" "distinct qs" "r ∈ set qs"
by auto
from Cons.IH[OF this Cons.prems(4)] show ?thesis .
next
case (Some dq)
with Cons.prems(1) obtain n1 E0 K0 n2 E' K' where
cp0: "compile_proc Π q dq n = (n1, E0, K0)"
and rest: "compile_procs Π qs n1 = (n2, E', K')"
and n': "n' = n2" and E: "E = E0 ∪ E'" and K: "K = K0 ∪ K'"
by (auto split: prod.splits)
have qnotin: "q ∉ set qs" and dqs: "distinct qs" using Cons.prems(2) by simp_all
show ?thesis
proof (cases "r = q")
case True
with Cons.prems(4) Some cp0 have cpr: "compile_proc Π r decl n = (n1, E0, K0)" by simp
have rnotin: "r ∉ set qs" using qnotin True by simp
have tailE: "⋀u a v. (u, a, v) ∈ E' ⟹ u ∉ pfn r n n1"
using compile_procs_intra_src_notin_pfn[OF rest _ rnotin] by blast
have tailK: "⋀u ce tgt af. (u, ce, tgt, af) ∈ K' ⟹ u ∉ pfn r n n1"
using compile_procs_calls_src_notin_pfn[OF rest _ rnotin] by blast
have "∀u a v. (u, a, v) ∈ E ⟶ u ∈ pfn r n n1 ⟶ (u, a, v) ∈ E0"
using E tailE by blast
moreover have "∀u ce tgt af. (u, ce, tgt, af) ∈ K ⟶ u ∈ pfn r n n1
⟶ (u, ce, tgt, af) ∈ K0"
using K tailK by blast
moreover have "n1 ≤ n'" using compile_procs_counter_mono[OF rest] n' by simp
moreover have "E0 ⊆ E" "K0 ⊆ K" using E K by auto
ultimately show ?thesis using cpr by blast
next
case False
have rqs: "r ∈ set qs" using Cons.prems(3) False by simp
from Cons.IH[OF rest dqs rqs Cons.prems(4)] obtain m m' Er Kr where
cpr: "compile_proc Π r decl m = (m', Er, Kr)" and bnds: "n1 ≤ m" "m' ≤ n2"
and sub: "Er ⊆ E'" "Kr ⊆ K'"
and ownE: "∀u a v. (u, a, v) ∈ E' ⟶ u ∈ pfn r m m' ⟶ (u, a, v) ∈ Er"
and ownK: "∀u ce tgt af. (u, ce, tgt, af) ∈ K' ⟶ u ∈ pfn r m m'
⟶ (u, ce, tgt, af) ∈ Kr"
by blast
have qr: "q ≠ r" using False by simp
have headE: "⋀u a v. (u, a, v) ∈ E0 ⟹ u ∉ pfn r m m'"
and headK: "⋀u ce tgt af. (u, ce, tgt, af) ∈ K0 ⟹ u ∉ pfn r m m'"
using compile_proc_src_notin_pfn[OF cp0 qr] bnds(1) by blast+
have "∀u a v. (u, a, v) ∈ E ⟶ u ∈ pfn r m m' ⟶ (u, a, v) ∈ Er"
using E ownE headE by blast
moreover have "∀u ce tgt af. (u, ce, tgt, af) ∈ K ⟶ u ∈ pfn r m m'
⟶ (u, ce, tgt, af) ∈ Kr"
using K ownK headK by blast
moreover have "n ≤ m" using compile_proc_counter_mono[OF cp0] bnds(1) by simp
moreover have "m' ≤ n'" using bnds(2) n' by simp
moreover have "Er ⊆ E" "Kr ⊆ K" using sub E K by auto
ultimately show ?thesis using cpr by blast
qed
qed
qed
lemma compile_procs_entry_unique:
assumes cps: "compile_procs Π ps n = (n', E, K)" and distinct: "distinct ps"
and e1: "(FunctionEntry r, a1, v1) ∈ E" and e2: "(FunctionEntry r, a2, v2) ∈ E"
shows "v1 = v2"
proof -
from compile_procs_intra_origin[OF cps e1] obtain decl
where rin: "r ∈ set ps" and decl: "Π r = Some decl"
using compile_proc_entry_mem by metis
from compile_procs_member_frag[OF cps distinct rin decl] obtain m m' Er Kr where
cp: "compile_proc Π r decl m = (m', Er, Kr)"
and own: "∀u a v. (u, a, v) ∈ E ⟶ u ∈ pfn r m m' ⟶ (u, a, v) ∈ Er" by blast
have "(FunctionEntry r, a1, v1) ∈ Er" "(FunctionEntry r, a2, v2) ∈ Er"
using own e1 e2 by (auto simp: pfn_def)
then show ?thesis by (rule compile_proc_entry_unique[OF cp])
qed
text ‹In a whole compiled program \<^term>‹FunctionEntry r› has a unique out-target: ‹main› and the
callees are node-disjoint (‹prog_main_name ∉ set ps›), and each contributes a single entry edge.›
lemma compile_prog_entry_out_unique:
assumes wf: "wf_compile_input 𝒢 Π ps"
and e1: "(FunctionEntry r, a1, v1) ∈ intra (compile_prog Π ps)"
and e2: "(FunctionEntry r, a2, v2) ∈ intra (compile_prog Π ps)"
shows "v1 = v2"
proof -
obtain n1 Eprocs Kprocs n2 Emain Kmain where
procs: "compile_procs Π ps 0 = (n1, Eprocs, Kprocs)"
and mainc: "compile_proc Π prog_main_name ⦇formals = [], body = (main_body Π)⦈ n1
= (n2, Emain, Kmain)"
and EI: "intra (compile_prog Π ps) = Eprocs ∪ Emain"
by (rule compile_prog_intra_split)
have main_mem: "⋀b w. (FunctionEntry r, b, w) ∈ Emain ⟹ r = prog_main_name"
using compile_proc_entry_mem[OF mainc] by simp
have procs_mem: "⋀b w. (FunctionEntry r, b, w) ∈ Eprocs ⟹ r ∈ set ps"
using compile_procs_entry_mem[OF procs] by simp
show ?thesis
proof (cases "r = prog_main_name")
case True
have "(FunctionEntry r, a1, v1) ∈ Emain" "(FunctionEntry r, a2, v2) ∈ Emain"
using e1 e2 EI procs_mem True wf_compile_inputD(11)[OF wf] by auto
then show ?thesis using compile_proc_entry_unique[OF mainc] by blast
next
case False
have "(FunctionEntry r, a1, v1) ∈ Eprocs" "(FunctionEntry r, a2, v2) ∈ Eprocs"
using e1 e2 EI main_mem False by auto
then show ?thesis using compile_procs_entry_unique[OF procs wf_compile_inputD(9)[OF wf]]
by blast
qed
qed
subsection ‹Whole-program edge shapes›
text ‹Two shape facts a consumer of a compiled graph may use without redoing the
callee/‹main› split: a call edge always leaves a \<^term>‹Statement› node, and an entry edge
names a procedure the table declares. The first is a range property each pass has on its
own; the second needs well-formed input, which is what ties the compiled procedure list
back to the table.›lemma compile_prog_calls_source_stmt:
assumes "(u, ce, tgt, af) ∈ calls (compile_prog Π ps)"
shows "∃k. u = Statement k"
proof -
obtain n1 Eprocs Kprocs n2 Emain Kmain where
procs: "compile_procs Π ps 0 = (n1, Eprocs, Kprocs)"
and mainc: "compile_proc Π prog_main_name ⦇formals = [], body = (main_body Π)⦈ n1
= (n2, Emain, Kmain)"
and KC: "calls (compile_prog Π ps) = Kprocs ∪ Kmain"
by (rule compile_prog_intra_split)
from assms KC show ?thesis
using compile_procs_calls_source_range[OF procs] compile_proc_calls_source_range[OF mainc]
by blast
qed
lemma compile_prog_entry_declared:
assumes wf: "wf_compile_input 𝒢 Π ps"
and e: "(FunctionEntry p, a, v) ∈ intra (compile_prog Π ps)"
shows "∃d. Π p = Some d"
proof -
obtain n1 Eprocs Kprocs n2 Emain Kmain where
procs: "compile_procs Π ps 0 = (n1, Eprocs, Kprocs)"
and mainc: "compile_proc Π prog_main_name ⦇formals = [], body = (main_body Π)⦈ n1
= (n2, Emain, Kmain)"
and EI: "intra (compile_prog Π ps) = Eprocs ∪ Emain"
by (rule compile_prog_intra_split)
from e EI consider "(FunctionEntry p, a, v) ∈ Eprocs" | "(FunctionEntry p, a, v) ∈ Emain"
by auto
then show ?thesis
proof cases
case 1
then have "p ∈ set ps" using compile_procs_entry_mem[OF procs] by blast
with wf_compile_inputD(10)[OF wf] show ?thesis by auto
next
case 2
then have "p = prog_main_name" by (rule compile_proc_entry_mem[OF mainc])
with wf_compile_inputD(2)[OF wf] show ?thesis by blast
qed
qed
subsection ‹Edges stay in the activation fragment›
text ‹The fragment a procedure's entry wiring identifies owns every edge of the program that
sources in its nodes. The callee pass and ‹main› occupy disjoint counter ranges, and inside
the pass ‹compile_procs_member_frag› settles ownership; the entry edge pins the start counter
through ‹compile_prog_entry_out_unique›.›
lemma compile_prog_entry_frag:
assumes wf: "wf_compile_input 𝒢 Π ps"
and decl: "Π r = Some d"
and cb: "compile_proc Π r d m = (m', Ep, Kp)"
and ent: "(FunctionEntry r, EA_Body r, Statement m) ∈ intra (compile_prog Π ps)"
shows "(∀u a v. (u, a, v) ∈ intra (compile_prog Π ps) ⟶ u ∈ pfn r m m'
⟶ (u, a, v) ∈ Ep)
∧ (∀u ce tgt af. (u, ce, tgt, af) ∈ calls (compile_prog Π ps)
⟶ u ∈ pfn r m m' ⟶ (u, ce, tgt, af) ∈ Kp)"
proof -
let ?g = "compile_prog Π ps"
obtain n1 Eprocs Kprocs n2 Emain Kmain where
procs: "compile_procs Π ps 0 = (n1, Eprocs, Kprocs)"
and mainc: "compile_proc Π prog_main_name ⦇formals = [], body = (main_body Π)⦈ n1
= (n2, Emain, Kmain)"
and EI: "intra ?g = Eprocs ∪ Emain" and KC: "calls ?g = Kprocs ∪ Kmain"
by (rule compile_prog_intra_split)
have distinct: "distinct ps" and setps: "set ps = {p. Π p ≠ None} - {prog_main_name}"
and main_notin: "prog_main_name ∉ set ps"
using wf_compile_inputD(9,10,11)[OF wf] by blast+
show ?thesis
proof (cases "r = prog_main_name")
case True
have dd: "d = ⦇formals = [], body = (main_body Π)⦈"
using decl True wf_compile_inputD(2)[OF wf] by simp
have entmain: "(FunctionEntry prog_main_name, EA_Body prog_main_name, Statement n1) ∈ intra ?g"
using EI compile_proc_entry_edge[OF mainc] by simp
have entr: "(FunctionEntry prog_main_name, EA_Body prog_main_name, Statement m) ∈ intra ?g"
using ent True by simp
have mn: "m = n1" using compile_prog_entry_out_unique[OF wf entr entmain] by simp
have mout: "m' = n2" "Ep = Emain" "Kp = Kmain" using cb mainc True dd mn by simp_all
show ?thesis unfolding True mn mout
using EI KC compile_procs_intra_src_notin_pfn[OF procs _ main_notin]
compile_procs_calls_src_notin_pfn[OF procs _ main_notin] by auto
next
case False
have rin: "r ∈ set ps" using decl setps False by auto
from compile_procs_member_frag[OF procs distinct rin decl] obtain m0 m0' Er Kr where
cpr: "compile_proc Π r d m0 = (m0', Er, Kr)" and upper: "m0' ≤ n1"
and Ersub: "Er ⊆ Eprocs"
and ownE: "∀u a v. (u, a, v) ∈ Eprocs ⟶ u ∈ pfn r m0 m0' ⟶ (u, a, v) ∈ Er"
and ownK: "∀u ce tgt af. (u, ce, tgt, af) ∈ Kprocs ⟶ u ∈ pfn r m0 m0'
⟶ (u, ce, tgt, af) ∈ Kr"
by blast
have ent0: "(FunctionEntry r, EA_Body r, Statement m0) ∈ intra ?g"
using EI Ersub compile_proc_entry_edge[OF cpr] by auto
have mm: "m = m0" using compile_prog_entry_out_unique[OF wf ent ent0] by simp
have mout: "m' = m0'" "Ep = Er" "Kp = Kr" using cb cpr mm by simp_all
have neq: "prog_main_name ≠ r" using False by simp
show ?thesis unfolding mm mout
using EI KC ownE ownK compile_proc_src_notin_pfn[OF mainc neq] upper by blast
qed
qed
text ‹Given a genuine procedure fragment (declared body, entry wiring) and a node in it, every
intra edge leaving that node, and every call leaving it, lands its same-activation successor
back in the fragment.›
lemma frag_edge_intra:
assumes wf: "wf_compile_input 𝒢 Π ps"
and decl: "Π r = Some d"
and cb: "compile_proc Π r d m = (m', Ep, Kp)"
and ent: "(FunctionEntry r, EA_Body r, Statement m) ∈ intra (compile_prog Π ps)"
and uin: "u ∈ pfn r m m'"
and e: "(u, a, v) ∈ intra (compile_prog Π ps)"
shows "v ∈ pfn r m m'"
using compile_prog_entry_frag[OF wf decl cb ent] compile_proc_intra_pfn[OF cb] uin e by blast
lemma frag_edge_calls:
assumes wf: "wf_compile_input 𝒢 Π ps"
and decl: "Π r = Some d"
and cb: "compile_proc Π r d m = (m', Ep, Kp)"
and ent: "(FunctionEntry r, EA_Body r, Statement m) ∈ intra (compile_prog Π ps)"
and uin: "u ∈ pfn r m m'"
and e: "(u, ce, tgt, af) ∈ calls (compile_prog Π ps)"
shows "af ∈ pfn r m m'"
using compile_prog_entry_frag[OF wf decl cb ent] compile_proc_calls_pfn[OF cb] uin e by blast
subsection ‹Procedure locality of a valid activation›
text ‹Every declared procedure of a compiled program owns a fragment, identified by the start
counter its entry wiring edge points at.›
lemma compile_prog_proc_frag:
assumes wf: "wf_compile_input 𝒢 Π ps"
and decl: "Π q = Some d"
obtains m m' Ep Kp where
"compile_proc Π q d m = (m', Ep, Kp)"
"(FunctionEntry q, EA_Body q, Statement m) ∈ intra (compile_prog Π ps)"
proof -
obtain n1 Eprocs Kprocs n2 Emain Kmain where
procs: "compile_procs Π ps 0 = (n1, Eprocs, Kprocs)"
and mainc: "compile_proc Π prog_main_name ⦇formals = [], body = (main_body Π)⦈ n1
= (n2, Emain, Kmain)"
and EI: "intra (compile_prog Π ps) = Eprocs ∪ Emain"
by (rule compile_prog_intra_split)
show thesis
proof (cases "q = prog_main_name")
case True
have dd: "d = ⦇formals = [], body = (main_body Π)⦈"
using decl True wf_compile_inputD(2)[OF wf] by simp
have "(FunctionEntry q, EA_Body q, Statement n1) ∈ intra (compile_prog Π ps)"
using EI compile_proc_entry_edge[OF mainc] True by auto
then show ?thesis using that[of n1 n2 Emain Kmain] mainc True dd by simp
next
case False
have rin: "q ∈ set ps" using decl wf_compile_inputD(10)[OF wf] False by auto
obtain m m' Ep Kp where cp0: "compile_proc Π q d m = (m', Ep, Kp)" and sub: "Ep ⊆ Eprocs"
using compile_procs_member[OF procs rin decl] by blast
have "(FunctionEntry q, EA_Body q, Statement m) ∈ intra (compile_prog Π ps)"
using EI sub compile_proc_entry_edge[OF cp0] by auto
then show ?thesis using that cp0 by blast
qed
qed
text ‹‹frag_ok u›: the activation ‹u› either lies wholly inside a genuine procedure fragment
(declared body, entry wiring, all path nodes in the fragment) or is a single-node activation
stuck at an undeclared \<^term>‹FunctionEntry› --- which can never advance and never reaches a
\<^term>‹FunctionResult›.›
definition frag_ok :: "'a ptable ⇒ pname list ⇒ activation_trace ⇒ bool" where
"frag_ok Π ps u ⟷
(∃r d m m' Ep Kp. Π r = Some d
∧ compile_proc Π r d m = (m', Ep, Kp)
∧ (FunctionEntry r, EA_Body r, Statement m) ∈ intra (compile_prog Π ps)
∧ fst (hd (path_of u)) = FunctionEntry r
∧ (∀nd ∈ fst ` set (path_of u). nd ∈ pfn r m m'))
∨ (∃p s. path_of u = [(FunctionEntry p, s)] ∧ Π p = None)"
lemma frag_okI_frag:
assumes "Π r = Some d" "compile_proc Π r d m = (m', Ep, Kp)"
"(FunctionEntry r, EA_Body r, Statement m) ∈ intra (compile_prog Π ps)"
"fst (hd (path_of u)) = FunctionEntry r"
"⋀nd. nd ∈ fst ` set (path_of u) ⟹ nd ∈ pfn r m m'"
shows "frag_ok Π ps u"
unfolding frag_ok_def using assms by blast
lemma frag_okE [elim]:
assumes "frag_ok Π ps u"
obtains (frag) r d m m' Ep Kp where "Π r = Some d" "compile_proc Π r d m = (m', Ep, Kp)"
"(FunctionEntry r, EA_Body r, Statement m) ∈ intra (compile_prog Π ps)"
"fst (hd (path_of u)) = FunctionEntry r" "∀nd ∈ fst ` set (path_of u). nd ∈ pfn r m m'"
| (stub) p s where "path_of u = [(FunctionEntry p, s)]" "Π p = None"
using assms unfolding frag_ok_def by meson
lemma sink_in_path_nodes:
"t ∈ 𝒯⇘𝒢,g,S⇙ ⟹ sink_node t ∈ fst ` set (path_of t)"
using valid_activation_trace_path_nonempty by (auto simp: sink_node_def)
text ‹Extending a fragment-local activation by one node keeps it fragment-local when the step
to that node stays inside every genuine fragment holding its sink and no step leaves a stub.
An intra step and the resumption after a call are the two such extensions.›
lemma frag_ok_snoc:
assumes t: "t ∈ 𝒯⇘𝒢,compile_prog Π ps,S⇙" and ok: "frag_ok Π ps t"
and pu: "path_of u = path_of t @ [(v, s)]"
and stay: "⋀r d m m' Ep Kp. Π r = Some d ⟹ compile_proc Π r d m = (m', Ep, Kp) ⟹
(FunctionEntry r, EA_Body r, Statement m) ∈ intra (compile_prog Π ps) ⟹
sink_node t ∈ pfn r m m' ⟹ v ∈ pfn r m m'"
and nostub: "⋀q. sink_node t = FunctionEntry q ⟹ Π q = None ⟹ False"
shows "frag_ok Π ps u"
using ok
proof (cases rule: frag_okE)
case (frag r d m m' Ep Kp)
have "sink_node t ∈ pfn r m m'" using sink_in_path_nodes[OF t] frag(5) by blast
from stay[OF frag(1,2,3) this] have v_in: "v ∈ pfn r m m'" .
show ?thesis
by (rule frag_okI_frag[OF frag(1,2,3)])
(use frag(4,5) valid_activation_trace_path_nonempty[OF t] v_in in ‹auto simp: pu hd_append›)
next
case (stub q s0)
have "sink_node t = FunctionEntry q" using stub(1) by (simp add: sink_node_def)
from nostub[OF this stub(2)] show ?thesis ..
qed
text ‹Every activation in the caller chain of a valid activation trace is fragment-local. The property is
carried over the whole \<^const>‹callers› chain so that the return case, which resumes the caller,
can read the caller's own fragment (‹caller ∈ callers callee›).›
lemma valid_activation_trace_frag_callers:
assumes wf: "wf_compile_input 𝒢 Π ps"
and t: "t ∈ 𝒯⇘𝒢,compile_prog Π ps,S⇙"
shows "∀u ∈ callers t. frag_ok Π ps u"
proof -
let ?g = "compile_prog Π ps"
show ?thesis
proof (rule caller_chain_closure)
fix s assume "s ∈ S"
have main_decl: "Π prog_main_name = Some ⦇formals = [], body = (main_body Π)⦈"
by (rule wf_compile_inputD(2)[OF wf])
obtain m m' Ep Kp where
cb: "compile_proc Π prog_main_name ⦇formals = [], body = (main_body Π)⦈ m = (m', Ep, Kp)"
and ent: "(FunctionEntry prog_main_name, EA_Body prog_main_name, Statement m) ∈ intra ?g"
by (rule compile_prog_proc_frag[OF wf main_decl])
show "frag_ok Π ps (Root [(cfg_entry ?g, s)])"
by (rule frag_okI_frag[OF main_decl cb ent])
(simp_all add: pfn_def)
next
fix t a v s'
assume ht: "t ∈ 𝒯⇘𝒢,?g,S⇙" and ch: "∀u ∈ callers t. frag_ok Π ps u"
and e: "(sink_node t, a, v) ∈ intra ?g"
have "frag_ok Π ps t" using ch callers_refl by blast
then show "frag_ok Π ps (extend t (v, s'))"
by (rule frag_ok_snoc[OF ht _ _ frag_edge_intra[OF wf _ _ _ _ e]])
(use e in ‹auto dest: compile_prog_entry_declared[OF wf]›)
next
fix caller dst pars args p cont
assume "(sink_node caller, CallEdge dst pars args, FunctionEntry p, cont) ∈ calls ?g"
show "frag_ok Π ps (Call caller
[(FunctionEntry p, call_enter 𝒢 (CallEdge dst pars args) (sink_store caller))])"
proof (cases "Π p")
case None
then show ?thesis unfolding frag_ok_def by fastforce
next
case (Some d)
obtain m m' Ep Kp where
cb: "compile_proc Π p d m = (m', Ep, Kp)"
and ent: "(FunctionEntry p, EA_Body p, Statement m) ∈ intra ?g"
by (rule compile_prog_proc_frag[OF wf Some])
show ?thesis
by (rule frag_okI_frag[OF Some cb ent]) (simp_all add: pfn_def)
qed
next
fix callee caller p dst pars args cont
assume cvcallee: "callee ∈ 𝒯⇘𝒢,?g,S⇙"
and ch: "∀u ∈ callers callee. frag_ok Π ps u"
and cof: "caller_of callee = Some caller" and "sink_node callee = FunctionResult p"
and e: "(sink_node caller, CallEdge dst pars args, FunctionEntry p, cont) ∈ calls ?g"
have cin: "caller ∈ callers callee" using cof callers_caller_subset callers_refl by blast
have cv: "caller ∈ 𝒯⇘𝒢,?g,S⇙" using valid_activation_trace_caller_valid[OF cvcallee cof] .
have "frag_ok Π ps caller" using ch cin by blast
then show "frag_ok Π ps (Resume caller callee (path_of caller
@ [(cont, combine_collect 𝒢 dst (sink_store caller) (sink_store callee))]))"
by (rule frag_ok_snoc[OF cv _ _ frag_edge_calls[OF wf _ _ _ _ e]])
(use e in ‹auto dest: compile_prog_calls_source_stmt›)
next
show "t ∈ 𝒯⇘𝒢,?g,S⇙" by (rule t)
qed
qed
text ‹An activation entered at \<^term>‹FunctionEntry p› reaches \<^term>‹FunctionResult q› only for
‹p = q›.›
lemma valid_activation_trace_entry_result_eq:
assumes wf: "wf_compile_input 𝒢 Π ps"
and t: "t ∈ 𝒯⇘𝒢,compile_prog Π ps,S⇙"
and en: "fst (hd (path_of t)) = FunctionEntry p"
and sk: "sink_node t = FunctionResult q"
shows "p = q"
proof -
have "frag_ok Π ps t"
using valid_activation_trace_frag_callers[OF wf t] callers_refl by blast
then show ?thesis
proof (cases rule: frag_okE)
case (frag r d m m' Ep Kp)
have "sink_node t ∈ pfn r m m'" using sink_in_path_nodes[OF t] frag(5) by blast
with frag(4) en sk show ?thesis by (auto simp: pfn_def)
next
case (stub p' s)
then have "sink_node t = FunctionEntry p'" by (simp add: sink_node_def)
with sk show ?thesis by simp
qed
qed
end