Theory CFG_Prune

theory CFG_Prune
  imports CFG_Transfer
begin

section ‹Procedure-aware structural reachability›

text ‹
  ‹cfg_succ_rel› is the derived structural dependency graph the analysis pruning and
  cone proofs run on --- not the concrete execution relation.  It has four sources,
  induced by ‹intra g› and ‹calls g›:

  ▪ INTRA: an ordinary edge ‹(u, a, v) ∈ intra g› gives ‹u → v› (ordinary flow).
  ▪ ENTRY: a call ‹(c, ca, FunctionEntry p, k) ∈ calls g› gives ‹c → FunctionEntry p› ---
    the callee entry's abstract state depends on the caller state routed through
    the analysis's own enter operation.
  ▪ ‹COMB_CALLER›: the same call gives ‹c → k› --- the continuation depends on the saved
    caller state via const‹combine_collect›.  This is not a concrete execution edge; execution
    does not skip the callee.
  ▪ ‹COMB_RESULT›: the same call gives ‹FunctionResult p → k› --- the continuation depends
    on the callee's result, const‹combine_collect›'s callee-exit argument.

  ‹c → k› and ‹FunctionResult p → k› are combine dependencies of the analysis, kept
  visibly separate from ‹intra g›.  They are never added to ‹intra g›.
›

subsection ‹The structural successor relation›

definition cfg_succ_rel :: "cfg ⇒ (cfg_node × cfg_node) set" where
  "cfg_succ_rel g =
     {(u, v) | u a v. (u, a, v) ∈ intra g}
   ∪ {(c, ce) | c ca ce k. (c, ca, ce, k) ∈ calls g}
   ∪ {(c, k) | c ca ce k. (c, ca, ce, k) ∈ calls g}
   ∪ {(FunctionResult p, k) | c ca p k. (c, ca, FunctionEntry p, k) ∈ calls g}"

lemma cfg_succ_rel_intra:
  "(u, a, v) ∈ intra g ⟹ (u, v) ∈ cfg_succ_rel g"
  unfolding cfg_succ_rel_def by blast

lemma cfg_succ_rel_entry:
  "(c, ca, ce, k) ∈ calls g ⟹ (c, ce) ∈ cfg_succ_rel g"
  unfolding cfg_succ_rel_def by blast

lemma cfg_succ_rel_comb_caller:
  "(c, ca, ce, k) ∈ calls g ⟹ (c, k) ∈ cfg_succ_rel g"
  unfolding cfg_succ_rel_def by blast

lemma cfg_succ_rel_comb_result:
  "(c, ca, FunctionEntry p, k) ∈ calls g ⟹ (FunctionResult p, k) ∈ cfg_succ_rel g"
  unfolding cfg_succ_rel_def by blast

lemma cfg_succ_rel_cases:
  assumes "(y, z) ∈ cfg_succ_rel g"
  obtains (INTRA) a where "(y, a, z) ∈ intra g"
    | (ENTRY) ca k where "(y, ca, z, k) ∈ calls g"
    | (COMB_CALLER) ca ce where "(y, ca, ce, z) ∈ calls g"
    | (COMB_RESULT) c ca p k where "(c, ca, FunctionEntry p, k) ∈ calls g"
                                   "y = FunctionResult p" "z = k"
  using assms unfolding cfg_succ_rel_def by blast

subsection ‹Reachability: reflexive-transitive closure of the successor relation›

text ‹Reachability over that successor relation.  Because the relation already relates a
  caller to its continuation and a callee result to the same continuation, this closure
  crosses procedure boundaries without any separate interprocedural notion.›
definition cfg_reaches :: "cfg ⇒ cfg_node ⇒ cfg_node ⇒ bool" where
  "cfg_reaches g v v0 ⟷ (v, v0) ∈ (cfg_succ_rel g)*"

subsection ‹Reachability inside one activation›

text ‹The same closure without entering callees: a call site steps straight to its
  continuation.  These are the steps a routed equation reads backwards, so this is the
  reachability the solver's coverage argument needs.›

definition local_succ_rel :: "cfg ⇒ (cfg_node × cfg_node) set" where
  "local_succ_rel g =
     {(u, v). ∃a. (u, a, v) ∈ intra g}
     ∪ {(u, k). ∃ca q. (u, ca, FunctionEntry q, k) ∈ calls g}"

definition local_reaches :: "cfg ⇒ cfg_node ⇒ cfg_node ⇒ bool" where
  "local_reaches g u v ⟷ (u, v) ∈ (local_succ_rel g)*"

lemma local_reaches_refl [simp]: "local_reaches g v v"
  by (simp add: local_reaches_def)

lemma local_reaches_trans:
  "local_reaches g u v ⟹ local_reaches g v w ⟹ local_reaches g u w"
  unfolding local_reaches_def by (rule rtrancl_trans)

lemma local_reaches_intra_step:
  "(u, a, v) ∈ intra g ⟹ local_reaches g v w ⟹ local_reaches g u w"
  unfolding local_reaches_def local_succ_rel_def
  by (rule converse_rtrancl_into_rtrancl) blast+

lemma local_reaches_comb_step:
  "(u, ca, FunctionEntry q, k) ∈ calls g ⟹ local_reaches g k w ⟹ local_reaches g u w"
  unfolding local_reaches_def local_succ_rel_def
  by (rule converse_rtrancl_into_rtrancl) blast+

lemma local_reaches_cfg_reaches:
  "local_reaches g u v ⟹ cfg_reaches g u v"
  unfolding local_reaches_def cfg_reaches_def
proof (induction rule: rtrancl_induct)
  case (step y z)
  then have "(y, z) ∈ cfg_succ_rel g"
    unfolding local_succ_rel_def by (auto simp: cfg_succ_rel_intra cfg_succ_rel_comb_caller)
  with step.IH show ?case by (rule rtrancl_into_rtrancl)
qed simp

end