Theory Voblint_Result.DG_Analysis

theory DG_Analysis
  imports
    DG_Result_Construction
    Analysis_Surface
    "Voblint_Framework.Routed_Analysis_Sound"
    "Voblint_Exec.Routed_Exec_Refinement"
    Source_Activation_Sound
    "Voblint_Routing.Compiled_Routed_Equations"
    "Voblint_Routing.Entry_State_Routed_Context"
    "Voblint_Solver.TD_Solver_Bridge"
    "Voblint_Framework.DG_Keyed_Split_Spec"
begin

section ‹One context-sensitive analysis, assembled from its choices›

text ‹
  A concrete analysis picks a domain implementation, an initial state, a way of
  telling activations apart, a solver and a classifier; everything between those
  choices and the published table is the same work at every domain and at every
  context policy. This theory does that work once.

  The construction half is ‹dg_pipeline›, which carries no correctness
  assumptions, so its defining equations are unconditional and can be declared to
  the code generator. The correctness half is ‹dg_analysis›, which adds
  the domain and solver contracts and derives, for one program, the routed
  soundness statement every policy shares.

  Vocabulary. A ∗‹routed› equation system publishes a callee's entry state at a
  global seed key instead of reading the callee entry directly; a ∗‹context› is
  the value the routing function attaches to a program point so that one point
  can be solved at several unknowns; ∗‹covered› keys are the unknowns the solver
  actually visited, which is not the same as the unknowns holding a bottom state.

  What this covers, and what it does not. It is the assembly for executable
  non-relational analyses whose local unknown carries a whole reachability-lifted
  store and whose entry operation answers with a pure list of alternatives. The
  context type, the global-key type, the seed constructor, the routing function
  and the root context are all parameters; the carrier and the entry shape are
  not.
›

subsection ‹The formals route, once for every domain›

text ‹
  Keying a callee on the abstract values its formals hold on entry, at the
  executable carrier. const‹formals_route_lifted_gen› is the same decision one
  layer up, on the state a solved table hands out; the equation below is what
  lets a routed instance discharge its routing-agreement obligation without a
  domain-specific lemma. The routed generator enters the callee frame before it
  routes, so the route itself only projects the formals out of the state it is
  handed.
›

definition exec_formals_route ::
    "(vname ⇒ bool) ⇒ pp ⇒ 'a list
       ⇒ ('a::bot) default_st lifted ⇒ call_action ⇒ 'a list" where
  "exec_formals_route 𝒢 u ctx d ca =
     (case ca of CallEdge dst pars args ⇒
        formals_context pars
          (ρ⇘𝒢⇙ (case d of Bot ⇒ bot | Lifted d0 ⇒ d0)))"

text ‹
  The same routing decision taken from a solved ∗‹result› instead of from the
  solver's carrier. A rendering that draws one node per activation has to know
  which callee context a call edge enters, and the table it draws from holds
  typ‹'a abs_state› rather than typ‹'a default_st› --- so the projection
  const‹exec_formals_route› performs is unavailable to it, and the entered
  state has to be recomputed from the caller's. ‹enter› is the domain's own
  entry transfer and is the only domain-specific value involved; emptiness is
  const‹is_empty›, the class‹executable_domain› operation every domain carries.

  The witness search runs over the formals rather than over the whole state,
  and it has to: const‹is_empty_state› quantifies over every typ‹vname›, so
  its code equation would demand an class‹enum› instance for
  typ‹String.literal› and this constant would not generate code at all.
  Restricting to the formals is also exact rather than merely cheaper ---
  procedure entry resets every non-global variable to top and leaves every
  global at the caller's own value, so no name outside the formals can witness
  emptiness that the caller did not already have.

  const‹None› means the call routes nowhere, because the entered state
  represents no concrete store. It is not a sentinel context: an empty formal
  list is a legitimate context in its own right --- a zero-formal callee's own
  entry is genuinely keyed at ‹[]› --- so no typ‹'a list› value could carry
  that meaning without colliding with a real one.
›

definition callee_ctx_of ::
    "((vname ⇒ bool) ⇒ vname list ⇒ exp list ⇒ 'a abs_state ⇒ 'a abs_state)
       ⇒ (vname ⇒ bool) ⇒ call_action ⇒ ('a::executable_domain) abs_state
       ⇒ 'a list option" where
  "callee_ctx_of enter 𝒢 ca st =
     (case ca of CallEdge dst pars args ⇒
        (let entered = enter 𝒢 pars args st
         in if list_ex (λx. is_empty (entered x)) pars then None
            else Some (formals_context pars entered)))"

lemma exec_formals_route_commute:
  "formals_route_lifted_gen u ctx (ρ⇘𝒢⇙ d) ca
     = exec_formals_route 𝒢 u ctx d ca"
  by (cases d; cases ca)
     (simp_all add: formals_route_lifted_gen_def formals_route_lifted_def
        exec_formals_route_def default_st_to_fun_def)

subsection ‹The construction›

text ‹
  The pipeline runs one component, ‹comp 𝒢 p›: a single analysis, or the
  combination of several. ‹init_st› is the unlifted entry state; the pipeline
  lifts it, because a reachability-lifted carrier is what the routed generator
  solves over. ‹empty p› is the carrier's executable emptiness test and ‹rd 𝒢›
  reads a carrier state back as the value the result table publishes. Nothing
  here fixes what the carrier or the published value is.
  ‹bot_state› is a parameter rather than the class‹order_bot› operation for the
  same code-generation reason locale‹analysis_surface› states: a sort constraint
  here would demand an executable const‹bot› at a function type.
›

text ‹
  Everything one solve publishes, read back as values: the result table, the
  solved value of each global name, the seed a call published at a procedure entry under a
  context, what one edge's local step makes of a point's solved state, and where a
  call leads when it contributes at all.
›

record ('c, 'n, 'v) solved_run =
  run_table :: "('c, 'v) solved_table"
  run_shared :: "'n ⇒ 'v lifted"
  run_seed :: "pname ⇒ 'c ⇒ 'v lifted"
  run_step :: "pp ⇒ 'c ⇒ edge_action ⇒ 'v lifted"
  run_succ :: "cfg_node ⇒ 'c ⇒ call_action ⇒ pname ⇒ 'c option"

text ‹
  ‹dg_pipeline› only fixes parameters: the component ‹comp›, the carrier
  operations ‹emp›, ‹rd› and ‹init_st›, the key a node buffers its own
  global part at and the key ‹global_of n› each global name lives at, the
  context policy ‹seed›/‹route›/
  ‹root_ctx›, the solver ‹solve› with its domain, the check classifier, and the
  placement of program state between the two halves of a solver unknown.
  The soundness assumptions live in the locales built on it.

  The placement is six operations. ‹place_spec› builds the D/G specification
  the equations run, from the component. ‹place_cmb› recombines a local value
  with the solved global environment into the state a point describes, and
  ‹place_rl› and ‹place_rg› project a state onto the part each half keeps.
  ‹place_inits› lists the initial value of each global name, which the program
  entry publishes at that name's key ‹global_of n›. ‹place_enter› is the caller
  state a call enters from: it may describe more than ‹place_cmb›, so a call reads
  only the globals it needs. An analysis that keeps all of its state in the local
  half builds the specification with const‹dg_spec_of›, recombines by ignoring
  the environment, keeps every state locally, publishes const‹Bot›, lists no
  initial values and enters from the caller's state as it is.
›

locale dg_pipeline =
  fixes comp :: "(vname ⇒ bool) ⇒ imp_prog ⇒ 's::semilattice_sup lifted local_spec"
    and emp :: "imp_prog ⇒ 's ⇒ bool"
    and rd :: "(vname ⇒ bool) ⇒ 's ⇒ 'v"
    and init_st :: 's
    and buffer_key :: 'k
    and global_of :: "'n ⇒ 'k"
    and seed :: "pp ⇒ 'c ⇒ 'k"
    and route :: "(vname ⇒ bool) ⇒ pp ⇒ 'c ⇒ 's lifted ⇒ call_action ⇒ 'c"
    and root_ctx :: 'c
    and solve :: "(pp × 'c, 'k, ('s lifted, 's lifted) dg_state) eqsT
                  ⇒ pp × 'c
                  ⇒ (pp × 'c) set × (pp × 'c + 'k ⇒ ('s lifted, 's lifted) dg_state)"
    and solve_dom :: "(pp × 'c, 'k, ('s lifted, 's lifted) dg_state) eqsT ⇒ pp × 'c ⇒ bool"
    and bot_state :: 'v
    and classify :: "exp ⇒ 'v ⇒ check_result"
    and place_spec :: "imp_prog ⇒ 's lifted local_spec
                        ⇒ (pp × 'c, 'k, 'n, 's lifted, 's lifted) dg_spec"
    and place_cmb :: "imp_prog ⇒ 's lifted ⇒ ('n ⇒ 's lifted) ⇒ 's lifted"
    and place_rl :: "(vname ⇒ bool) ⇒ 's lifted ⇒ 's lifted"
    and place_rg :: "(vname ⇒ bool) ⇒ 's lifted ⇒ 's lifted"
    and place_inits :: "(vname ⇒ bool) ⇒ imp_prog ⇒ ('n × 's lifted) list"
    and place_enter :: "imp_prog ⇒ call_info ⇒ 's lifted ⇒ ('n ⇒ 's lifted) ⇒ 's lifted"
begin

definition analysis_spec :: "(vname ⇒ bool) ⇒ imp_prog
    ⇒ (pp × 'c, 'k, 'n, 's lifted, 's lifted) dg_spec" where
  "analysis_spec 𝒢 p = place_spec p (comp 𝒢 p)"

text ‹
  The state the component enters a callee with, from a whole caller state. The
  pipeline supports components whose entry answers one alternative, the caller's
  state paired with this one.
›

definition comp_entry :: "(vname ⇒ bool) ⇒ imp_prog ⇒ call_info ⇒ 's lifted
    ⇒ 's lifted" where
  "comp_entry 𝒢 p ci w = snd (hd (ls_enter (comp 𝒢 p) (ls_channel (comp 𝒢 p) w) ci (w, w)))"

text ‹
  The alternative a call enters its callee with, as the solver stores it: the
  component enters from the caller state ‹place_enter› recombines, and each
  half of the answer keeps its local part.
›

definition entry_alt :: "(vname ⇒ bool) ⇒ imp_prog ⇒ call_info ⇒ 's lifted
    ⇒ ('n ⇒ 's lifted) ⇒ 's lifted × 's lifted" where
  "entry_alt 𝒢 p ci d g =
     (let w = place_enter p ci d g in (place_rl 𝒢 w, place_rl 𝒢 (comp_entry 𝒢 p ci w)))"

text ‹The state a call enters its callee with.›

definition entry_of :: "(vname ⇒ bool) ⇒ imp_prog ⇒ call_info ⇒ 's lifted
    ⇒ ('n ⇒ 's lifted) ⇒ 's lifted"
  where "entry_of 𝒢 p ci d g = snd (entry_alt 𝒢 p ci d g)"

text ‹
  The unknown the solver is asked for. It is the program exit at the root
  context: a demand-driven solver is started at the answer a caller wants and
  explores backwards from it, so the root query is the exit and the entry point
  is reached as one of its dependencies.
›

definition root_query :: "imp_prog ⇒ pp × 'c" where
  "root_query p = (cfg_exit (prog_cfg p), root_ctx)"

text ‹
  ‹root_query›'s own type mentions no domain, so a code equation for it carries a
  sort hypothesis the code generator cannot see and silently rejects. The two
  constants that use it inline it instead: their types do mention the domain, so
  their equations are accepted, and ‹root_query› never has to be executable.
›

text ‹
  The initial value of each global name is a side effect like any other: the
  program entry publishes it, once, at the name's key, as a run's initialization
  happens before its first statement. A placement that keeps its initial global
  part at the node's own key passes no such values.
›

definition init_publications :: "(vname ⇒ bool) ⇒ imp_prog
    ⇒ pp × 'c ⇒ ('k × ('s lifted, 's lifted) dg_state) list" where
  "init_publications 𝒢 p x =
     (if x = (cfg_entry (prog_cfg p), root_ctx)
      then map (λ(n, d). (global_of n, DG Bot d)) (place_inits 𝒢 p) else [])"

text ‹
  The generated code evaluates this test at every equation evaluation, so the code
  equation names the entry node directly instead of compiling the program each time.
›

lemma init_publications_code:
  "init_publications 𝒢 p x =
     (if x = (FunctionEntry prog_main_name, root_ctx)
      then map (λ(n, d). (global_of n, DG Bot d)) (place_inits 𝒢 p) else [])"
  by (simp add: init_publications_def prog_cfg_def)

text ‹
  The initial global environment a run starts from: what the entry node buffers at
  its own key, read at every name living there, together with the root's initial
  publications.
›

definition init_env :: "(vname ⇒ bool) ⇒ imp_prog ⇒ 'n ⇒ 's lifted" where
  "init_env 𝒢 p n =
     (if global_of n = buffer_key then place_rg 𝒢 (Lifted init_st) else Bot)
       ⊔ (⨆(m, d)←filter (λ(m, d). m = n) (place_inits 𝒢 p). d)"

definition equations :: "(vname ⇒ bool) ⇒ imp_prog
    ⇒ (pp × 'c, 'k,
         ('s lifted, 's lifted) dg_state) eqsT" where
  "equations 𝒢 p =
     with_init (init_publications 𝒢 p)
       (compiled_routed_eqs_for buffer_key global_of seed (route 𝒢)
          (analysis_spec 𝒢 p) (prog_cfg p) (Lifted init_st) (place_rg 𝒢 (Lifted init_st)))"

definition solution :: "(vname ⇒ bool) ⇒ imp_prog
    ⇒ (pp × 'c) set
         × (pp × 'c + 'k
              ⇒ ('s lifted, 's lifted) dg_state)" where
  "solution 𝒢 p = solve (equations 𝒢 p) (root_query p)"

definition terminates :: "(vname ⇒ bool) ⇒ imp_prog ⇒ bool" where
  "terminates 𝒢 p = solve_dom (equations 𝒢 p) (root_query p)"

lemma solution_code: "solution 𝒢 p = solve (equations 𝒢 p) (cfg_exit (prog_cfg p), root_ctx)"
  by (simp add: solution_def root_query_def)

lemma terminates_code:
  "terminates 𝒢 p = solve_dom (equations 𝒢 p) (cfg_exit (prog_cfg p), root_ctx)"
  by (simp add: terminates_def root_query_def)

definition sol_vars :: "(vname ⇒ bool) ⇒ imp_prog ⇒ (pp × 'c) set" where
  "sol_vars 𝒢 p = fst (solution 𝒢 p)"

definition sol_env :: "(vname ⇒ bool) ⇒ imp_prog
    ⇒ pp × 'c + 'k ⇒ ('s lifted, 's lifted) dg_state" where
  "sol_env 𝒢 p = snd (solution 𝒢 p)"

definition reader :: "(vname ⇒ bool) ⇒ imp_prog
    ⇒ pp × 'c + 'k ⇒ 's lifted" where
  "reader 𝒢 p = solved_local_reader (sol_vars 𝒢 p) (sol_env 𝒢 p)"

definition result :: "(vname ⇒ bool) ⇒ imp_prog
    ⇒ ('c, 'v) solved_table" where
  "result 𝒢 p = dg_result_for (rd 𝒢) (emp p) (place_cmb p) global_of (solution 𝒢 p)"

text ‹The solved global environment every recombination reads.›

definition sol_global :: "(vname ⇒ bool) ⇒ imp_prog ⇒ 'n ⇒ 's lifted" where
  "sol_global 𝒢 p = genv global_of (sol_env 𝒢 p)"

text ‹
  Where a call leads, as a function of the call site and the caller's context
  alone: this route applied to the state this solve published at that site. It
  names no concretization, so it belongs here rather than beside the soundness
  endpoints -- which is what lets a registration that cannot export a binder
  publish it by spelling this out, exactly as it publishes const‹result›.
›

definition ctx_succ :: "(vname ⇒ bool) ⇒ imp_prog ⇒ cfg_node ⇒ 'c
    ⇒ call_action ⇒ pname ⇒ 'c" where
  "ctx_succ 𝒢 p u ctx ca q =
     route 𝒢 u ctx (entry_of 𝒢 p (call_info_of ca q) (dg_local (sol_env 𝒢 p (Inl (u, ctx))))
                      (sol_global 𝒢 p)) ca"


text ‹
  Where a call leads when it contributes at all: ‹None› when the entered state is
  bottom, which is exactly when the routed call program drops the alternative, and
  otherwise the context const‹ctx_succ› names.
›

definition live_succ :: "(vname ⇒ bool) ⇒ imp_prog ⇒ cfg_node ⇒ 'c
    ⇒ call_action ⇒ pname ⇒ 'c option" where
  "live_succ 𝒢 p u ctx ca q =
     (if entry_of 𝒢 p (call_info_of ca q) (dg_local (sol_env 𝒢 p (Inl (u, ctx))))
           (sol_global 𝒢 p) = Bot
      then None else Some (ctx_succ 𝒢 p u ctx ca q))"


text ‹
  The values one solution publishes. The seeds are read exactly as a table entry is,
  so an unwritten key reads as const‹Bot›. The shared global is read
  without that canonicalization: its local half is bottom by construction, which
  would make every value it holds read as const‹Bot›.

  A step is what one edge's local step makes of a point's solved state: the term
  that edge contributes to its target's equation, re-evaluated on the solution. A
  target with several incoming edges stores only their join, so this is the one
  place the contribution of a single edge can be read. It is the component's own
  step, run with the answers its handler gives on the recombined state, as the
  edge transfer runs it, and published with the state it describes.

  The successor is const‹live_succ› read off the same solution, so a caller
  listing where every call leads does not solve the system again for each call.

  The solution is an argument, so a caller holding the executable solver's answer
  reads it without solving again; ‹run› below reads the specification's solve.
›

definition solved_run_of :: "(vname ⇒ bool) ⇒ imp_prog
    ⇒ (pp × 'c) set × (pp × 'c + 'k ⇒ ('s lifted, 's lifted) dg_state)
    ⇒ ('c, 'n, 'v) solved_run" where
  "solved_run_of 𝒢 p sol =
     (let c = comp 𝒢 p;
          g = genv global_of (snd sol);
          read = (λd. map_lift (rd 𝒢) (canonicalize_lift (emp p) d))
      in ⦇ run_table = dg_result_for (rd 𝒢) (emp p) (place_cmb p) global_of sol,
           run_shared = (λn. map_lift (rd 𝒢) (g n)),
           run_seed = (λf ctx. read (place_cmb p
                         (dg_local (snd sol (Inr (seed (FunctionEntry f) ctx)))) g)),
           run_step = (λv ctx a. read (let d = place_cmb p (dg_local (snd sol (Inl (v, ctx)))) g
                                       in ⟦a⟧♯⇘c⇙ d)),
           run_succ = (λu ctx ca q.
             let d = entry_of 𝒢 p (call_info_of ca q) (dg_local (snd sol (Inl (u, ctx)))) g
             in if d = Bot then None else Some (route 𝒢 u ctx d ca)) ⦈)"

definition run :: "(vname ⇒ bool) ⇒ imp_prog ⇒ ('c, 'n, 'v) solved_run" where
  "run 𝒢 p = solved_run_of 𝒢 p (solution 𝒢 p)"

lemma run_table_run [simp]: "run_table (run 𝒢 p) = result 𝒢 p"
  by (simp add: run_def solved_run_of_def result_def Let_def)

lemma run_succ_run: "run_succ (run 𝒢 p) = live_succ 𝒢 p"
  by (simp add: run_def solved_run_of_def live_succ_def ctx_succ_def sol_env_def sol_global_def
      Let_def fun_eq_iff)

text ‹
  The contextual publication surface: one verdict per context at a check, and
  the aggregate a caller prints. Both are const‹classify_checks_ctx› and
  const‹classify_checks_verdicts› unchanged -- they are generic in the context
  type already -- so nothing here is policy-specific beyond supplying this
  pipeline's own result table.
›

definition check_projection :: "(vname ⇒ bool) ⇒ imp_prog
    ⇒ (pp × exp × ('c × contextual_verdict) set) list" where
  "check_projection 𝒢 p = classify_checks_ctx (prog_cfg p) (result 𝒢 p) classify"

definition verdict_report :: "(vname ⇒ bool) ⇒ imp_prog
    ⇒ (pp × exp × contextual_verdict) list" where
  "verdict_report 𝒢 p = classify_checks_verdicts (prog_cfg p) (result 𝒢 p) classify"

text ‹
  The same table read at one context: the state published there, and the check
  report off those states. These are locale‹analysis_surface›'s readings of this
  pipeline's result, so every policy publishes the same surface; a
  context-insensitive run reads it at ‹()›.
›

definition state_at :: "(vname ⇒ bool) ⇒ imp_prog ⇒ 'c ⇒ pp ⇒ 'v" where
  "state_at 𝒢 = analysis_surface.state_at (result 𝒢) bot_state"

definition report :: "(vname ⇒ bool) ⇒ imp_prog ⇒ 'c ⇒ check_report_entry list" where
  "report 𝒢 = analysis_surface.report (result 𝒢) bot_state classify"

definition report_with_state :: "(vname ⇒ bool) ⇒ imp_prog
    ⇒ 'c ⇒ (pp × exp × check_result × bool × 'v) list" where
  "report_with_state 𝒢 = analysis_surface.report_with_state (result 𝒢) bot_state classify"

lemma state_at_unfold:
  "state_at 𝒢 p ctx v
     = (case lookup_table (result 𝒢 p) v ctx of Bot ⇒ bot_state | Lifted st ⇒ st)"
  by (simp add: state_at_def analysis_surface.state_at_def)
end

text ‹Register the exported locale equations for direct calls to the generic pipeline.
  Attributes inside the locale apply through interpretation; code generation here also
  needs equations for the constants with their locale parameters left abstract.›

declare
  dg_pipeline.analysis_spec_def [code]
  dg_pipeline.comp_entry_def [code]
  dg_pipeline.entry_alt_def [code]
  dg_pipeline.entry_of_def [code]
  dg_pipeline.sol_global_def [code]
  dg_pipeline.init_publications_code [code]
  dg_pipeline.equations_def [code]
  dg_pipeline.solution_code [code]
  dg_pipeline.terminates_code [code]
  dg_pipeline.sol_vars_def [code]
  dg_pipeline.sol_env_def [code]
  dg_pipeline.reader_def [code]
  dg_pipeline.result_def [code]
  dg_pipeline.ctx_succ_def [code]
  dg_pipeline.live_succ_def [code]
  dg_pipeline.solved_run_of_def [code]
  dg_pipeline.run_def [code]
  dg_pipeline.check_projection_def [code]
  dg_pipeline.verdict_report_def [code]
  dg_pipeline.state_at_def [code]
  dg_pipeline.report_def [code]
  dg_pipeline.report_with_state_def [code]

subsection ‹The contracts›

text ‹
  What an instance owes, and nothing more: its component is sound for the
  concretization its publication map induces, its entry answers one alternative, its two
  emptiness tests are exact, its seed keys are distinct from the analysis-wide
  global, its solver is a locale‹certified_solver› (a post-solution over
  finitely many keys once it terminates), its classifier is correct, and its entry
  state describes every initial store. The equation system, the solve, the covered keys, the reader,
  the result table and the report are all fixed by locale‹dg_pipeline›
  above and appear here only in conclusions. Each obligation is stated at the
  program's own declared globals, the one set the soundness statement uses.
›

locale dg_analysis =
  dg_pipeline comp emp rd init_st buffer_key global_of seed route root_ctx solve solve_dom
    bot_state classify place_spec place_cmb place_rl place_rg place_inits place_enter
  + certified_solver solve solve_dom solve_c
  for comp :: "(vname ⇒ bool) ⇒ imp_prog ⇒ 's::semilattice_sup lifted local_spec"
    and emp :: "imp_prog ⇒ 's ⇒ bool"
    and rd :: "(vname ⇒ bool) ⇒ 's ⇒ 'v"
    and init_st :: 's
    and buffer_key :: 'k
    and global_of :: "'n ⇒ 'k"
    and seed :: "pp ⇒ 'c ⇒ 'k"
    and route :: "(vname ⇒ bool) ⇒ pp ⇒ 'c ⇒ 's lifted ⇒ call_action ⇒ 'c"
    and root_ctx :: 'c
    and solve solve_dom
    and bot_state :: 'v
    and classify :: "exp ⇒ 'v ⇒ check_result"
    and γV :: "'v ⇒ store set"
    and emptyV :: "'v ⇒ bool"
    and solve_c :: "(pp × 'c, 'k, ('s lifted, 's lifted) dg_state) eqsT
                    ⇒ pp × 'c
                    ⇒ ((pp × 'c) set
                          × (pp × 'c + 'k ⇒ ('s lifted, 's lifted) dg_state)) option"
    and place_spec :: "imp_prog ⇒ 's lifted local_spec
                        ⇒ (pp × 'c, 'k, 'n, 's lifted, 's lifted) dg_spec"
    and place_cmb :: "imp_prog ⇒ 's lifted ⇒ ('n ⇒ 's lifted) ⇒ 's lifted"
    and place_rl :: "(vname ⇒ bool) ⇒ 's lifted ⇒ 's lifted"
    and place_rg :: "(vname ⇒ bool) ⇒ 's lifted ⇒ 's lifted"
    and place_inits :: "(vname ⇒ bool) ⇒ imp_prog ⇒ ('n × 's lifted) list"
    and place_enter :: "imp_prog ⇒ call_info ⇒ 's lifted ⇒ ('n ⇒ 's lifted) ⇒ 's lifted" +
  assumes comp_sound:
      "⋀p. sound_local_spec (declared_global p)
               (λd. gamma_lift γV (map_lift (rd (declared_global p)) d))
               (comp (declared_global p) p)"
    and enter_single:
      "⋀p ci d. ls_enter (comp (declared_global p) p) (ls_channel (comp (declared_global p) p) d)
                    ci (d, d)
                  = [(d, comp_entry (declared_global p) p ci d)]"
    and place_contract:
      "⋀p. analysis_contract (analysis_spec (declared_global p) p)
               (λd e. gamma_lift γV
                        (map_lift (rd (declared_global p)) (place_cmb p d e)))
               (declared_global p)"
    and place_enter_runs:
      "⋀p ci d σ. ∃pub. enter_runs (enter♯ (analysis_spec (declared_global p) p) ci)
          (mk_dg_man d global_of) σ
          [entry_alt (declared_global p) p ci d (genv global_of σ)] pub"
    and place_enter_deps:
      "⋀p ci d σ. ∃deps. enter_deps (enter♯ (analysis_spec (declared_global p) p) ci)
          (mk_dg_man d global_of) σ
          [entry_alt (declared_global p) p ci d (genv global_of σ)] deps"
    and place_entry_sound:
      "⋀p ci d σ pub s.
         enter_runs (enter♯ (analysis_spec (declared_global p) p) ci)
           (mk_dg_man d global_of) σ
           [entry_alt (declared_global p) p ci d (genv global_of σ)] pub
         ⟹ genv global_of pub ≤ genv global_of σ
         ⟹ s ∈ gamma_lift γV (map_lift (rd (declared_global p))
              (place_cmb p d (genv global_of σ)))
         ⟹ s ∈ gamma_lift γV (map_lift (rd (declared_global p))
                   (place_cmb p
                      (fst (entry_alt (declared_global p) p ci d (genv global_of σ)))
                      (genv global_of σ)))
           ∧ call_enter (declared_global p) (CallEdge (ci_dst ci) (ci_formals ci) (ci_args ci)) s
               ∈ gamma_lift γV (map_lift (rd (declared_global p))
                   (place_cmb p
                      (snd (entry_alt (declared_global p) p ci d (genv global_of σ)))
                      (genv global_of σ)))"
    and place_cmb_bot: "⋀p e. place_cmb p Bot e = Bot"
    and place_init:
      "⋀p. gamma_lift γV (map_lift (rd (declared_global p)) (Lifted init_st))
         ⊆ gamma_lift γV (map_lift (rd (declared_global p))
              (place_cmb p (Lifted init_st) (init_env (declared_global p) p)))"
    and empty_rd: "⋀p s. emp p s ⟷ emptyV (rd (declared_global p) s)"
    and emptyV_sound: "sound_emptiness emptyV γV"
    and seed_ne_buffer_key: "⋀v ctx. seed v ctx ≠ buffer_key"
    and seed_ne_global_of: "⋀v ctx n. seed v ctx ≠ global_of n"
    and classify_proved:
      "⋀c d s. classify c d = Check_Proved ⟹ s ∈ γV d ⟹ truthy (⟦c⟧e s)"
    and classify_refuted:
      "⋀c d s. classify c d = Check_Refuted ⟹ s ∈ γV d
         ⟹ ¬ truthy (⟦c⟧e s)"
    and bot_state_empty: "γV bot_state = {}"
    and init_sound:
      "⋀p. cinit_stores (declared_global p) ⊆ γV (rd (declared_global p) init_st)"
begin

text ‹The generic contract asks the published emptiness test only to be sound: a
  product of analyses may describe nothing without any one of them saying so. A
  particular instance may still be exact.›

lemmas emptyV_soundD = sound_emptinessD [OF emptyV_sound]

text ‹
  Termination is a per-program side condition, and this is how a caller decides
  it: run the solver's own executable termination check on this program's
  equations. The premise stays ‹solve_dom› everywhere else, so nothing in the
  soundness argument depends on the check having been run.
›

lemma terminates_of_solve_c:
  assumes "solve_c (equations 𝒢 p) (root_query p) ≠ None"
  shows "terminates 𝒢 p"
  unfolding terminates_def by (rule dom_of_solve_c[OF assms])

text ‹
  An answer of the executable solver is the solve itself, so a run built from that
  answer is const‹run›, and its program needs no separate termination premise.
›

lemma solve_c_run:
  assumes "solve_c (equations 𝒢 p) (root_query p) = Some sol"
  shows "terminates 𝒢 p" and "solved_run_of 𝒢 p sol = run 𝒢 p"
  using assms terminates_of_solve_c solve_of_solve_c[OF assms]
  by (simp_all add: run_def solution_def)

lemma vars_finite_of_terminates:
  assumes "terminates 𝒢 p"
  shows "finite (sol_vars 𝒢 p)"
  using solve_fin[OF assms[unfolded terminates_def]]
  by (simp add: sol_vars_def solution_def)

text ‹
  The contexts a concrete call is admitted at under an entry-state policy: every
  context the entered state routes to. This is a top-level constant rather than
  something read out of a per-program interpretation, because an analysis
  publishes it -- a caller stating a context-indexed collecting fact has to name
  the relation those contexts are indexed by, and an example checking a routing
  decision has to name it too.

  With one alternative per call it is one context per abstract caller state; it
  is still a relation and not a function because several concrete callers sharing
  one abstract state reach the same context.
›

definition admitted_contexts :: "(vname ⇒ bool) ⇒ imp_prog ⇒ 'c context_policy" where
  "admitted_contexts 𝒢 p =
     routed_entry_context_rel
       (λci d. [entry_alt 𝒢 p ci d (sol_global 𝒢 p)])
       (λd e. gamma_lift γV (map_lift (rd 𝒢) (place_cmb p d e)))
       (sol_env 𝒢 p) global_of (route 𝒢)"

subsection ‹What the assembly derives, for one program›

text ‹
  Fixing one program ‹p›, this part equates the published table with the solved
  reader, puts the solver's answer in the routed spine's shape, and states routed
  soundness for any relation of admitted contexts.
›

context
  fixes p :: imp_prog
begin

abbreviation (input) pgs :: "vname ⇒ bool" where "pgs ≡ declared_global p"
  ― ‹input-only, so interpreted facts print ‹declared_global p››

text ‹What a carrier state describes, published and concretized, and what a point's
  two halves describe once recombined against the solved global.›

abbreviation cgam :: "'s lifted ⇒ store set" where
  "cgam d ≡ gamma_lift γV (map_lift (rd pgs) d)"

abbreviation (input) pgam :: "'s lifted ⇒ ('n ⇒ 's lifted) ⇒ store set" where
  "pgam d e ≡ cgam (place_cmb p d e)"

abbreviation (input) gsol :: "'n ⇒ 's lifted" where
  "gsol ≡ sol_global pgs p"

abbreviation entered :: "call_info ⇒ 's lifted ⇒ 's lifted" where
  "entered ci d ≡ entry_of pgs p ci d gsol"

lemma empty_rd_exact: "emp p s = emptyV (rd pgs s)"
  by (simp add: empty_rd)

lemma dg_spec_wf_analysis_spec [intro, simp]: "dg_spec_wf (analysis_spec pgs p)"
  by (rule analysis_contract.spec_wf[OF place_contract])

lemma pgam_Bot [simp]: "pgam Bot g = {}"
  by (simp add: place_cmb_bot)

text ‹
  The published table and the solved reader describe the same stores at every
  key. A caller states soundness against const‹lookup_table› of the result
  table, while the routed endpoints are stated against the reader; this is the
  equation between them, and it needs no coverage premise. At a covered key it
  is publication commuting with the normalization; at an uncovered one it is
  const‹Bot› against const‹Bot›, and both describe nothing.
›

lemma gamma_reader_eq_lookup:
  "pgam (reader pgs p (Inl (v, ctx))) gsol
     = gamma_lift γV (lookup_table (result pgs p) v ctx)"
proof -
  have gc: "gamma_lift γV (canonicalize_lift emptyV x) = gamma_lift γV x" for x
    by (rule gamma_lift_canonicalize_lift) (rule emptyV_soundD)
  show ?thesis
  proof (cases "(v, ctx) ∈ sol_vars pgs p")
    case True
    then show ?thesis
      by (simp add: reader_def result_def sol_vars_def sol_env_def sol_global_def gc
          map_lift_canonicalize_lift[of "emp p" "emptyV" "rd pgs", OF empty_rd_exact])
  next
    case False
    then show ?thesis by (simp add: reader_def result_def sol_vars_def bot_lifted_eq)
  qed
qed

subsubsection ‹The solver's answer, in the shape the routed spine consumes›

text ‹
  An analysis solves the buffered generator -- a node with several intra
  predecessors or several returning calls publishes its analysis-wide
  contribution once per evaluation rather than once per contribution -- while the
  framework states its soundness over the unbuffered one. ‹pp_routed› is that
  reconciliation at this pipeline's own equations, which the placement owes.
›

lemma pp_buffered:
  assumes solves: "terminates pgs p"
  shows "part_post_solution (equations pgs p) (root_query p)
           (sol_env pgs p) (sol_vars pgs p)"
  using solve_pp[OF solves[unfolded terminates_def]]
  by (simp add: sol_env_def sol_vars_def solution_def)

theorem pp_routed:
  assumes solves: "terminates pgs p"
  shows "part_post_solution
     (routed_node_rhs intra_predecessor_addr_list call_site_list (λ_. buffer_key) (route pgs)
        (λctx' src a. dg_spec_edge_program (analysis_spec pgs p) a src global_of)
        (routed_call_program (analysis_spec pgs p) global_of seed
           (static_resolve (prog_cfg p)) (λd. d = Bot))
        (routed_entry_seed_programs seed)
        (prog_cfg p) Bot (Lifted init_st) (place_rg pgs (Lifted init_st)))
     (root_query p) (sol_env pgs p) (sol_vars pgs p)"
  using part_post_solution_with_init[OF pp_buffered[OF solves, unfolded equations_def]]
  unfolding compiled_routed_eqs_for_def bot_lifted_eq
  by (rule pp_routed_of_buffered[OF dg_spec_wf_analysis_spec seed_ne_buffer_key])

text ‹
  The run starts below the solved environment once the solve covers the program
  entry: the entry node publishes the placement's initial global part at its own
  key, and each listed initial value at its name's key.
›

lemma init_publication_le_sol:
  assumes solves: "terminates pgs p"
    and entry_cov: "(cfg_entry (prog_cfg p), root_ctx) ∈ sol_vars pgs p"
    and mem: "(n, d) ∈ set (place_inits pgs p)"
  shows "d ≤ gsol n"
proof -
  have pp: "part_post_solution (equations pgs p) (root_query p) (sol_env pgs p) (sol_vars pgs p)"
    by (rule pp_buffered[OF solves])
  have "DG Bot d ≤ sol_env pgs p (Inr (global_of n))"
    by (rule post_bounded_with_init_at[OF post_bounded_of_part_post_solution[OF pp,
          unfolded equations_def] entry_cov])
       (use mem in ‹auto simp: init_publications_def›)
  then show ?thesis by (simp add: sol_global_def less_eq_dg_state_def)
qed

lemma foldr_init_le:
  "∀(m, d) ∈ set xs. d ≤ f m
     ⟹ (⨆(m, d)←filter (λ(m, d). m = n) xs. d) ≤ (f n :: 's lifted)"
  by (induction xs) auto

lemma init_env_le_sol:
  assumes solves: "terminates pgs p"
    and entry_cov: "(cfg_entry (prog_cfg p), root_ctx) ∈ sol_vars pgs p"
    and own: "place_rg pgs (Lifted init_st) ≤ dg_global (sol_env pgs p (Inr buffer_key))"
  shows "init_env pgs p ≤ gsol"
proof (rule le_funI)
  fix n
  have "(⨆(m, d)←filter (λ(m, d). m = n) (place_inits pgs p). d) ≤ gsol n"
    by (rule foldr_init_le) (use init_publication_le_sol[OF solves entry_cov] in blast)
  moreover have "(if global_of n = buffer_key then place_rg pgs (Lifted init_st) else Bot)
                   ≤ gsol n"
    using own by (auto simp: sol_global_def)
  ultimately show "init_env pgs p n ≤ gsol n"
    unfolding init_env_def by simp
qed

subsubsection ‹The one entry alternative this carrier answers with›

text ‹
  The specification's entry answers with a single alternative, the recombined
  caller state and the callee frame entered from it, each split back by the
  placement. ‹entry_cover› is the only fact about it any context policy needs,
  namely that a concrete call from a described caller lands in the described
  callee.
›

text ‹
  What a call publishes on entry is below the solved global, once the node the
  call returns to is solved: that node's equation runs the call, and a
  post-solution bounds what every solved equation publishes.
›

lemma enter_pub_le_sol:
  assumes solves: "terminates pgs p"
    and cont: "(k, ctx) ∈ sol_vars pgs p"
    and ce: "(u, ca, FunctionEntry q, k) ∈ calls (prog_cfg p)"
    and R: "enter_runs (enter♯ (analysis_spec pgs p) (call_info_of ca q))
              (mk_dg_man (dg_local (sol_env pgs p (Inl (u, ctx)))) global_of)
              (sol_env pgs p) pairs pub"
  shows "genv global_of pub ≤ gsol"
proof (rule le_funI)
  fix n
  let ?K = "Inr (global_of n)"
  let ?σ = "sol_env pgs p"
  let ?callee = "routed_callee_call_program (analysis_spec pgs p) global_of seed (route pgs)
                   (λd. d = Bot) ctx ca u (dg_local (?σ (Inl (u, ctx)))) q"
  let ?call = "routed_call_program (analysis_spec pgs p) global_of seed
                 (static_resolve (prog_cfg p)) (λd. d = Bot) (route pgs) ctx ca u k"
  let ?contribs = "routed_contribution_programs intra_predecessor_addr_list call_site_list (route pgs)
        (λctx' src a. dg_spec_edge_program (analysis_spec pgs p) a src global_of)
        (routed_call_program (analysis_spec pgs p) global_of seed
           (static_resolve (prog_cfg p)) (λd. d = Bot))
        (routed_entry_seed_programs seed) (prog_cfg p) ctx k"
  let ?rhs = "routed_node_rhs intra_predecessor_addr_list call_site_list (λ_. buffer_key) (route pgs)
        (λctx' src a. dg_spec_edge_program (analysis_spec pgs p) a src global_of)
        (routed_call_program (analysis_spec pgs p) global_of seed
           (static_resolve (prog_cfg p)) (λd. d = Bot))
        (routed_entry_seed_programs seed)
        (prog_cfg p) Bot (Lifted init_st) (place_rg pgs (Lifted init_st))"
  have fin: "finite (calls (prog_cfg p))"
    unfolding prog_cfg_def by (simp add: compile_prog_finite)
  have wf: "∀t ∈ set ?contribs. sp_wf t"
    by (rule routed_contribution_programs_wf) auto
  have "pub ?K ≤ sides_of_program ?callee ?σ ?K"
    unfolding routed_callee_call_program_def
    by (simp add: sp_compile_bind sp_wf_observes enter_runsD_sides[OF R] sup_fun_def)
  also have "… ≤ sides_of_program ?call ?σ ?K"
    by (rule routed_call_program_sides_ge_at[OF dg_spec_wf_analysis_spec])
       (use ce fin in simp)
  also have "… ≤ sides_of_program (side_rhs_fold_dg
      (if k = cfg_entry (prog_cfg p) then Bot ⊔ Lifted init_st else Bot) ?contribs)
      ?σ ?K"
  proof (rule sides_le_side_rhs_fold_dg[OF wf])
    have "(u, ca) ∈ set (call_site_list (prog_cfg p) k)" using ce fin by auto
    then show "?call ∈ set ?contribs" by (rule routed_contribution_programs_combineI)
  qed
  also have "… ≤ sides_of_rhs (?rhs (k, ctx)) ?σ ?K"
    unfolding routed_node_rhs_def Let_def
    by (cases "k = cfg_entry (prog_cfg p)") (auto simp: Let_def)
  also have "… ≤ ?σ ?K"
    using pp_routed[OF solves] cont by (blast dest: le_funD)
  finally show "genv global_of pub n ≤ gsol n"
    unfolding sol_global_def by (simp add: less_eq_dg_state_def)
qed

lemma entry_cover:
  assumes solves: "terminates pgs p"
    and cont: "(k, ctx) ∈ sol_vars pgs p"
    and ce: "(u, CallEdge dst pars args, FunctionEntry q, k) ∈ calls (prog_cfg p)"
    and sin: "s ∈ pgam (dg_local (sol_env pgs p (Inl (u, ctx)))) gsol"
  shows "entry_pairs_cover (λd. pgam d gsol) s (call_enter pgs (CallEdge dst pars args) s)
           [entry_alt pgs p (call_info_of (CallEdge dst pars args) q)
              (dg_local (sol_env pgs p (Inl (u, ctx)))) gsol]"
proof -
  let ?ci = "call_info_of (CallEdge dst pars args) q"
  let ?d = "dg_local (sol_env pgs p (Inl (u, ctx)))"
  let ?alt = "entry_alt pgs p ?ci ?d gsol"
  obtain pub where R: "enter_runs (enter♯ (analysis_spec pgs p) ?ci)
      (mk_dg_man ?d global_of) (sol_env pgs p)
      [entry_alt pgs p ?ci ?d (genv global_of (sol_env pgs p))] pub"
    using place_enter_runs by blast
  have le: "genv global_of pub ≤ genv global_of (sol_env pgs p)"
    using enter_pub_le_sol[OF solves cont ce R] by (simp add: sol_global_def)
  have "s ∈ pgam (fst ?alt) gsol ∧ call_enter pgs (CallEdge dst pars args) s ∈ pgam (snd ?alt) gsol"
    using place_entry_sound[OF R le sin[unfolded sol_global_def]]
    unfolding sol_global_def by (simp del: declared_global_iff)
  then show ?thesis
    by (intro entry_pairs_coverI[of "fst ?alt" "snd ?alt"]) simp_all
qed

subsubsection ‹The routed soundness statement, at any admitted-context relation›

text ‹
  Which contexts a concrete call is admitted at is the one thing a context policy
  still chooses, so ‹adm› is a parameter here rather than a derived object. What
  the policy owes about it is exactly two facts: a context ‹adm› admits is the one
  this pipeline's route computes on the entered state, at a covered unknown
  (‹cover_R›); and ‹adm› admits at least one context for every concrete call at a
  covered call site (‹total_R›). Everything else below is fixed by the pipeline.
›

text ‹
  The specification is sound for the recombined concretization; the placement
  owes this, so the routed statement below never re-derives it.
›

interpretation dg_base: analysis_contract "analysis_spec pgs p" "λd e. pgam d e" pgs
  by (rule place_contract)

lemma routed_analysis_sound_of_live:
  fixes adm :: "'c context_policy"
  assumes solves: "terminates pgs p"
    and fwd_ok: "⋀u a v ctx. (u, ctx) ∈ sol_vars pgs p
        ⟹ dg_local (sol_env pgs p (Inl (u, ctx))) ≠ Bot
        ⟹ (u, a, v) ∈ intra (prog_cfg p) ⟹ (v, ctx) ∈ sol_vars pgs p"
    and comb_fwd_ok: "⋀cl c1 dst pars args q cont. (cl, c1) ∈ sol_vars pgs p
        ⟹ (cl, CallEdge dst pars args, FunctionEntry q, cont) ∈ calls (prog_cfg p)
        ⟹ (cont, c1) ∈ sol_vars pgs p"
    and cover_R: "⋀u ctx dst pars args q cont s ctx'.
        (u, ctx) ∈ sol_vars pgs p
        ⟹ (u, CallEdge dst pars args, FunctionEntry q, cont) ∈ calls (prog_cfg p)
        ⟹ ctx' ∈ adm u ctx (call_info_of (CallEdge dst pars args) q) s
              (call_enter pgs (CallEdge dst pars args) s)
        ⟹ entered (call_info_of (CallEdge dst pars args) q)
              (dg_local (sol_env pgs p (Inl (u, ctx)))) ≠ Bot
        ⟹ route pgs u ctx
                (entered (call_info_of (CallEdge dst pars args) q)
                   (dg_local (sol_env pgs p (Inl (u, ctx)))))
                (CallEdge dst pars args) = ctx'
            ∧ (FunctionEntry q, ctx') ∈ sol_vars pgs p"
    and total_R: "⋀u ctx dst pars args q cont s.
        (u, ctx) ∈ sol_vars pgs p
        ⟹ (u, CallEdge dst pars args, FunctionEntry q, cont) ∈ calls (prog_cfg p)
        ⟹ s ∈ pgam (dg_local (sol_env pgs p (Inl (u, ctx)))) gsol
        ⟹ adm u ctx (call_info_of (CallEdge dst pars args) q) s
                      (call_enter pgs (CallEdge dst pars args) s) ≠ {}"
  shows "routed_analysis (analysis_spec pgs p) (λd e. pgam d e) pgs (prog_cfg p)
     buffer_key global_of
     (route pgs) Bot (Lifted init_st) (place_rg pgs (Lifted init_st)) (sol_env pgs p)
     (sol_vars pgs p) (root_query p)
     seed (λd. d = Bot) adm (λd e. map_lift (rd pgs) (place_cmb p d e)) γV emptyV
     classify"
proof (unfold_locales, goal_cases CmbWf ExtraWf FinE PP SgCov SgUncov Fwd FinC CallsUnique
    SeedUnknown SeedNeGlobal IsBotBot IsBotSound ResolveSound EnterCover EnterTotal CombFwd GammaRd
    EmptyExact ClProved ClRefuted VarsFin)
  case CmbWf show ?case by (rule sp_wf_routed_call_program[OF dg_spec_wf_analysis_spec])
next
  case ExtraWf then show ?case by (rule sp_wf_routed_entry_seed_programs)
next
  case FinE show ?case unfolding prog_cfg_def by (simp add: compile_prog_finite)
next
  case PP show ?case by (rule post_bounded_of_part_post_solution[OF pp_routed[OF solves]])
next
  case (SgCov v c) then show ?case by simp
next
  case (SgUncov v c) then show ?case by (simp add: bot_lifted_eq)
next
  case (Fwd u a v c)
  have "dg_local (sol_env pgs p (Inl (u, c))) ≠ Bot"
    using Fwd(2) by auto
  with Fwd(1,3) show ?case by (blast intro: fwd_ok)
next
  case FinC show ?case unfolding prog_cfg_def by (simp add: compile_prog_finite)
next
  case CallsUnique show ?case
    unfolding calls_source_unique_def prog_cfg_def
    using compile_prog_calls_source_unique by blast
next
  case (SeedUnknown q ctx) show ?case by (rule seed_ne_buffer_key)
next
  case (SeedNeGlobal q ctx v) show ?case by (rule seed_ne_global_of)
next
  case IsBotBot show ?case by simp
next
  case (IsBotSound d g') then show ?case by simp
next
  case (ResolveSound u ctx dst pars args q cont s)
  then show ?case unfolding prog_cfg_def by (simp add: compile_prog_finite)
next
  case (EnterCover u ctx dst pars args q cont s ctx')
  let ?ci = "call_info_of (CallEdge dst pars args) q"
  let ?caller = "dg_local (sol_env pgs p (Inl (u, ctx)))"
  let ?g = "genv global_of (sol_env pgs p)"
  have sin: "s ∈ pgam ?caller gsol"
    using EnterCover(3) by (simp add: sol_global_def)
  have cov: "entry_pairs_cover (λd. pgam d gsol) s
      (call_enter pgs (CallEdge dst pars args) s) [entry_alt pgs p ?ci ?caller gsol]"
    by (rule entry_cover[OF solves comb_fwd_ok[OF EnterCover(1,2)] EnterCover(2) sin])
  obtain c e where ce: "entry_alt pgs p ?ci ?caller gsol = (c, e)"
    by (cases "entry_alt pgs p ?ci ?caller gsol")
  have nbE: "entered ?ci ?caller ≠ Bot"
  proof
    assume "entered ?ci ?caller = Bot"
    with cov ce show False by (simp add: entry_pairs_cover_def entry_of_def place_cmb_bot)
  qed
  have req: "route pgs u ctx (entered ?ci ?caller) (CallEdge dst pars args) = ctx'"
    and covE: "(FunctionEntry q, ctx') ∈ sol_vars pgs p"
    using cover_R[OF EnterCover(1,2,4) nbE] by blast+
  obtain pub where runs: "enter_runs (enter♯ (analysis_spec pgs p) ?ci)
      (mk_dg_man ?caller global_of) (sol_env pgs p)
      [entry_alt pgs p ?ci ?caller ?g] pub"
    using place_enter_runs by blast
  obtain deps where deps: "enter_deps (enter♯ (analysis_spec pgs p) ?ci)
      (mk_dg_man ?caller global_of) (sol_env pgs p)
      [entry_alt pgs p ?ci ?caller ?g] deps"
    using place_enter_deps by blast
  have ce': "entry_alt pgs p ?ci ?caller ?g = (c, e)"
    using ce by (simp add: sol_global_def)
  have sc: "s ∈ pgam c ?g" and ec: "call_enter pgs (CallEdge dst pars args) s ∈ pgam e ?g"
    using cov ce by (simp_all add: entry_pairs_cover_def sol_global_def)
  have re: "route pgs u ctx e (CallEdge dst pars args) = ctx'"
    using req ce by (simp add: entry_of_def)
  show ?case
    using runs deps sc ec re covE unfolding ce' by fastforce
next
  case (EnterTotal u ctx dst pars args q cont s)
  then show ?case by (intro total_R) (simp_all add: sol_global_def)
next
  case (CombFwd cl c1 dst pars args q cont)
  then show ?case by (rule comb_fwd_ok)
next
  case (GammaRd d g') show ?case by simp
next
  case (EmptyExact v) then show ?case by (rule emptyV_soundD)
next
  case (ClProved c d s) then show ?case by (rule classify_proved)
next
  case (ClRefuted c d s) then show ?case by (rule classify_refuted)
next
  case VarsFin show ?case by (rule vars_finite_of_terminates[OF solves])
qed

lemma routed_analysis_sound_of:
  fixes adm :: "'c context_policy"
  assumes solves: "terminates pgs p"
    and fwd_ok: "⋀u a v ctx. (u, ctx) ∈ sol_vars pgs p
        ⟹ (u, a, v) ∈ intra (prog_cfg p) ⟹ (v, ctx) ∈ sol_vars pgs p"
    and comb_fwd_ok: "⋀cl c1 dst pars args q cont. (cl, c1) ∈ sol_vars pgs p
        ⟹ (cl, CallEdge dst pars args, FunctionEntry q, cont) ∈ calls (prog_cfg p)
        ⟹ (cont, c1) ∈ sol_vars pgs p"
    and cover_R: "⋀u ctx dst pars args q cont s ctx'.
        (u, ctx) ∈ sol_vars pgs p
        ⟹ (u, CallEdge dst pars args, FunctionEntry q, cont) ∈ calls (prog_cfg p)
        ⟹ ctx' ∈ adm u ctx (call_info_of (CallEdge dst pars args) q) s
              (call_enter pgs (CallEdge dst pars args) s)
        ⟹ route pgs u ctx
                (entered (call_info_of (CallEdge dst pars args) q)
                   (dg_local (sol_env pgs p (Inl (u, ctx)))))
                (CallEdge dst pars args) = ctx'
            ∧ (FunctionEntry q, ctx') ∈ sol_vars pgs p"
    and total_R: "⋀u ctx dst pars args q cont s.
        (u, ctx) ∈ sol_vars pgs p
        ⟹ (u, CallEdge dst pars args, FunctionEntry q, cont) ∈ calls (prog_cfg p)
        ⟹ s ∈ pgam (dg_local (sol_env pgs p (Inl (u, ctx)))) gsol
        ⟹ adm u ctx (call_info_of (CallEdge dst pars args) q) s
                      (call_enter pgs (CallEdge dst pars args) s) ≠ {}"
  shows "routed_analysis (analysis_spec pgs p) (λd e. pgam d e) pgs (prog_cfg p)
     buffer_key global_of
     (route pgs) Bot (Lifted init_st) (place_rg pgs (Lifted init_st)) (sol_env pgs p)
     (sol_vars pgs p) (root_query p)
     seed (λd. d = Bot) adm (λd e. map_lift (rd pgs) (place_cmb p d e)) γV emptyV
     classify"
  by (rule routed_analysis_sound_of_live [where adm = adm, OF solves _ comb_fwd_ok _ total_R])
     (blast intro: fwd_ok dest: cover_R)+

subsubsection ‹The published endpoint, under termination and coverage›

text ‹
  Under termination and closure of the solved keys, ‹activation_collect_sound_of›
  bounds every admitted activation by the solved table's entry at its context.
›

lemma cinit_le_init: "cinit_stores pgs ⊆ pgam (Lifted init_st) (init_env pgs p)"
  using init_sound[of p] place_init[of p] by auto

text ‹
  Every activation the activation-trace semantics admits at a context is described by the
  solved table's entry for that context. The coverage premises are the shape a
  solver's own reachable set supplies: an edge out of an unknown the solve
  visited lands on one it also visited.
›

lemma activation_collect_sound_of:
  fixes adm :: "'c context_policy"
  assumes solves: "terminates pgs p"
    and entry_cov: "(cfg_entry (prog_cfg p), root_ctx) ∈ sol_vars pgs p"
    and fwd_ok: "⋀u a v ctx. (u, ctx) ∈ sol_vars pgs p
        ⟹ (u, a, v) ∈ intra (prog_cfg p) ⟹ (v, ctx) ∈ sol_vars pgs p"
    and comb_fwd_ok: "⋀cl c1 dst pars args q cont. (cl, c1) ∈ sol_vars pgs p
        ⟹ (cl, CallEdge dst pars args, FunctionEntry q, cont) ∈ calls (prog_cfg p)
        ⟹ (cont, c1) ∈ sol_vars pgs p"
    and cover_R: "⋀u ctx dst pars args q cont s ctx'.
        (u, ctx) ∈ sol_vars pgs p
        ⟹ (u, CallEdge dst pars args, FunctionEntry q, cont) ∈ calls (prog_cfg p)
        ⟹ ctx' ∈ adm u ctx (call_info_of (CallEdge dst pars args) q) s
              (call_enter pgs (CallEdge dst pars args) s)
        ⟹ route pgs u ctx
                (entered (call_info_of (CallEdge dst pars args) q)
                   (dg_local (sol_env pgs p (Inl (u, ctx)))))
                (CallEdge dst pars args) = ctx'
            ∧ (FunctionEntry q, ctx') ∈ sol_vars pgs p"
    and total_R: "⋀u ctx dst pars args q cont s.
        (u, ctx) ∈ sol_vars pgs p
        ⟹ (u, CallEdge dst pars args, FunctionEntry q, cont) ∈ calls (prog_cfg p)
        ⟹ s ∈ pgam (dg_local (sol_env pgs p (Inl (u, ctx)))) gsol
        ⟹ adm u ctx (call_info_of (CallEdge dst pars args) q) s
                      (call_enter pgs (CallEdge dst pars args) s) ≠ {}"
  shows "𝒜⇘pgs,adm,root_ctx,prog_cfg p,cinit_stores pgs⇙ v ctx
           ⊆ pgam (reader pgs p (Inl (v, ctx))) gsol"
proof -
  interpret adapter: routed_analysis "analysis_spec pgs p" "λd e. pgam d e" pgs
      "prog_cfg p" buffer_key global_of "route pgs" Bot "Lifted init_st"
      "place_rg pgs (Lifted init_st)" "sol_env pgs p" "sol_vars pgs p" "root_query p" seed
      "λd. d = Bot" adm "λd e. map_lift (rd pgs) (place_cmb p d e)" γV emptyV classify
    by (rule routed_analysis_sound_of
          [where adm = adm, OF solves fwd_ok comb_fwd_ok cover_R total_R])
  have init_env_le: "init_env pgs p ≤ genv global_of (sol_env pgs p)"
    using init_env_le_sol[OF solves entry_cov adapter.pp_entry_init_global_bound[OF entry_cov]]
    by (simp add: sol_global_def)
  show ?thesis
    using adapter.activation_collect_dg_sound[OF entry_cov _ init_env_le, of "cinit_stores pgs" v
      ctx]
      cinit_le_init
    by (simp add: reader_def sol_global_def)
qed

subsubsection ‹The two context policies this assembly supports›

text ‹
  ‹entry_context_rel› is the relation an entry-state policy induces: a concrete
  call is admitted at every context some covering alternative of the entry answer
  routes to. With one alternative per call that is a single context per abstract
  caller state, but several concrete callers sharing an abstract state may still
  reach one context, which is exactly why a relation and not a function.
›

lemma admitted_contexts_alt:
  "admitted_contexts pgs p =
     routed_entry_context_rel (λci d. [entry_alt pgs p ci d gsol]) (λd e. pgam d e)
       (sol_env pgs p) global_of (route pgs)"
  by (simp add: admitted_contexts_def)

abbreviation entry_context_rel :: "'c context_policy" where
  "entry_context_rel ≡ admitted_contexts pgs p"

text ‹
  How a caller shows that this call is admitted at the context it computed: the
  concrete caller store is described by the recombined caller, and the entered
  store by the callee frame entered from it, each recombined against the solved
  global. The single alternative means a witness never has to name the
  alternatives list.
›

lemma admitted_contextsI:
  assumes caller: "s ∈ pgam (fst (entry_alt pgs p ci (dg_local (sol_env pgs p (Inl (u, ctx)))) gsol))
                     gsol"
    and entered_in: "s' ∈ pgam (entered ci (dg_local (sol_env pgs p (Inl (u, ctx))))) gsol"
  shows "route pgs u ctx (entered ci (dg_local (sol_env pgs p (Inl (u, ctx)))))
             (CallEdge (ci_dst ci) (ci_formals ci) (ci_args ci))
           ∈ entry_context_rel u ctx ci s s'"
  unfolding admitted_contexts_alt
  by (rule routed_entry_context_relI
        [where cont = "fst (entry_alt pgs p ci (dg_local (sol_env pgs p (Inl (u, ctx)))) gsol)"])
     (use caller entered_in in ‹simp_all add: entry_of_def sol_global_def›)

text ‹
  The same fact with the call action spelled as the caller has it. A witness
  names a literal const‹CallEdge›, while the rule above reconstructs one from
  the type‹call_info›'s three projections, and ⬚‹rule› does not reduce the
  projections. Every concrete instance wants this shape.
›

lemma admitted_contextsI_call:
  assumes caller: "s ∈ pgam (fst (entry_alt pgs p (call_info_of (CallEdge dst pars args) q)
                       (dg_local (sol_env pgs p (Inl (u, ctx)))) gsol)) gsol"
    and entered_in: "s' ∈ pgam (entered (call_info_of (CallEdge dst pars args) q)
                           (dg_local (sol_env pgs p (Inl (u, ctx))))) gsol"
  shows "route pgs u ctx
             (entered (call_info_of (CallEdge dst pars args) q)
                (dg_local (sol_env pgs p (Inl (u, ctx)))))
             (CallEdge dst pars args)
           ∈ entry_context_rel u ctx (call_info_of (CallEdge dst pars args) q) s s'"
  using admitted_contextsI[OF caller entered_in] by simp

lemma entry_state_routed_analysis_sound:
  assumes solves: "terminates pgs p"
    and fwd_ok: "⋀u a v ctx. (u, ctx) ∈ sol_vars pgs p
        ⟹ (u, a, v) ∈ intra (prog_cfg p) ⟹ (v, ctx) ∈ sol_vars pgs p"
    and call_fwd_ok: "⋀u ctx dst pars args q cont. (u, ctx) ∈ sol_vars pgs p
        ⟹ (u, CallEdge dst pars args, FunctionEntry q, cont) ∈ calls (prog_cfg p)
        ⟹ (FunctionEntry q,
               route pgs u ctx
                 (entered (call_info_of (CallEdge dst pars args) q)
                    (dg_local (sol_env pgs p (Inl (u, ctx)))))
                 (CallEdge dst pars args))
              ∈ sol_vars pgs p"
    and comb_fwd_ok: "⋀cl c1 dst pars args q cont. (cl, c1) ∈ sol_vars pgs p
        ⟹ (cl, CallEdge dst pars args, FunctionEntry q, cont) ∈ calls (prog_cfg p)
        ⟹ (cont, c1) ∈ sol_vars pgs p"
  shows "routed_analysis (analysis_spec pgs p) (λd e. pgam d e) pgs (prog_cfg p)
     buffer_key global_of
     (route pgs) Bot (Lifted init_st) (place_rg pgs (Lifted init_st)) (sol_env pgs p)
     (sol_vars pgs p) (root_query p)
     seed (λd. d = Bot) entry_context_rel
     (λd e. map_lift (rd pgs) (place_cmb p d e)) γV emptyV classify"
proof (rule routed_analysis_sound_of
    [where adm = entry_context_rel, OF solves fwd_ok comb_fwd_ok])
  fix u ctx dst pars args q cont and s :: store and ctx'
  assume covV: "(u, ctx) ∈ sol_vars pgs p"
    and ce: "(u, CallEdge dst pars args, FunctionEntry q, cont) ∈ calls (prog_cfg p)"
    and Rc: "ctx' ∈ entry_context_rel u ctx (call_info_of (CallEdge dst pars args) q) s
               (call_enter pgs (CallEdge dst pars args) s)"
  let ?ci = "call_info_of (CallEdge dst pars args) q"
  let ?caller = "dg_local (sol_env pgs p (Inl (u, ctx)))"
  from Rc[unfolded admitted_contexts_alt] obtain cont' entry
    where mem: "(cont', entry) ∈ set [entry_alt pgs p ?ci ?caller gsol]"
      and req0: "ctx' = route pgs u ctx entry
                   (CallEdge (ci_dst ?ci) (ci_formals ?ci) (ci_args ?ci))"
    by (rule routed_entry_context_relE)
  have "entry = entered ?ci ?caller" using mem by (simp add: entry_of_def) (metis snd_conv)
  with req0 have req: "route pgs u ctx (entered ?ci ?caller) (CallEdge dst pars args) = ctx'"
    by simp
  show "route pgs u ctx (entered ?ci ?caller) (CallEdge dst pars args) = ctx'
          ∧ (FunctionEntry q, ctx') ∈ sol_vars pgs p"
    using req call_fwd_ok[OF covV ce] by simp
next
  fix u ctx dst pars args q cont and s :: store
  assume covV: "(u, ctx) ∈ sol_vars pgs p"
    and ce: "(u, CallEdge dst pars args, FunctionEntry q, cont) ∈ calls (prog_cfg p)"
    and sin: "s ∈ pgam (dg_local (sol_env pgs p (Inl (u, ctx)))) gsol"
  show "entry_context_rel u ctx (call_info_of (CallEdge dst pars args) q) s
                 (call_enter pgs (CallEdge dst pars args) s) ≠ {}"
    unfolding admitted_contexts_alt
    by (rule routed_entry_context_rel_total)
       (use entry_cover[OF solves comb_fwd_ok[OF covV ce] ce sin]
         in ‹simp add: sol_global_def del: declared_global_iff›)
qed

text ‹
  The two endpoints an entry-state caller consumes: the per-context collecting
  bound, and the existence of a context for every valid activation trace, which a
  source-level caller needs to name a context witness at all. Both are the
  routed spine's own theorems at the interpretation just established.
›

context
  assumes solves: "terminates pgs p"
    and fwd_ok: "⋀u a v ctx. (u, ctx) ∈ sol_vars pgs p
        ⟹ (u, a, v) ∈ intra (prog_cfg p) ⟹ (v, ctx) ∈ sol_vars pgs p"
    and call_fwd_ok: "⋀u ctx dst pars args q cont. (u, ctx) ∈ sol_vars pgs p
        ⟹ (u, CallEdge dst pars args, FunctionEntry q, cont) ∈ calls (prog_cfg p)
        ⟹ (FunctionEntry q,
               route pgs u ctx
                 (entered (call_info_of (CallEdge dst pars args) q)
                    (dg_local (sol_env pgs p (Inl (u, ctx)))))
                 (CallEdge dst pars args))
              ∈ sol_vars pgs p"
    and comb_fwd_ok: "⋀cl c1 dst pars args q cont. (cl, c1) ∈ sol_vars pgs p
        ⟹ (cl, CallEdge dst pars args, FunctionEntry q, cont) ∈ calls (prog_cfg p)
        ⟹ (cont, c1) ∈ sol_vars pgs p"
begin

interpretation entry: routed_analysis "analysis_spec pgs p" "λd e. pgam d e" pgs
    "prog_cfg p" buffer_key global_of "route pgs" Bot "Lifted init_st"
    "place_rg pgs (Lifted init_st)" "sol_env pgs p" "sol_vars pgs p" "root_query p" seed
    "λd. d = Bot" entry_context_rel "λd e. map_lift (rd pgs) (place_cmb p d e)"
    γV emptyV classify
  by (rule entry_state_routed_analysis_sound [OF solves fwd_ok call_fwd_ok comb_fwd_ok])

lemma entry_init_env_le:
  assumes entry_cov: "(cfg_entry (prog_cfg p), root_ctx) ∈ sol_vars pgs p"
  shows "init_env pgs p ≤ genv global_of (sol_env pgs p)"
  using init_env_le_sol[OF solves entry_cov entry.pp_entry_init_global_bound[OF entry_cov]]
  by (simp add: sol_global_def)

corollary entry_state_activation_collect_sound:
  assumes entry_cov: "(cfg_entry (prog_cfg p), root_ctx) ∈ sol_vars pgs p"
  shows "𝒜⇘pgs,entry_context_rel,root_ctx,prog_cfg p,cinit_stores pgs⇙ v ctx
           ⊆ pgam (reader pgs p (Inl (v, ctx))) gsol"
  using entry.activation_collect_dg_sound
      [OF entry_cov _ entry_init_env_le[OF entry_cov], of "cinit_stores pgs" v ctx]
    cinit_le_init
  by (simp add: reader_def sol_global_def)

theorem entry_state_has_context:
  assumes entry_cov: "(cfg_entry (prog_cfg p), root_ctx) ∈ sol_vars pgs p"
    and trace: "t ∈ 𝒯⇘pgs,prog_cfg p,cinit_stores pgs⇙"
  shows "∃c. activation_context_rel pgs entry_context_rel root_ctx (prog_cfg p) t c"
  using entry.routed_valid_activation_trace_has_context
      [OF entry_cov _ entry_init_env_le[OF entry_cov] trace] cinit_le_init
  by simp

text ‹
  The two together: covering the entry is enough for the activation buckets to
  exhaust the context-insensitive collection, so a caller holding only a
  const‹node_collect› membership -- which is what a source run delivers -- can
  pass to the bucket its own call history produced.  Without this the per-context
  bounds above say nothing about a run whose context is not known in advance.
›

theorem entry_state_node_collect_eq_Union:
  assumes entry_cov: "(cfg_entry (prog_cfg p), root_ctx) ∈ sol_vars pgs p"
  shows "𝒞⇘pgs,prog_cfg p,cinit_stores pgs⇙ v
           = (⋃ctx. 𝒜⇘pgs,entry_context_rel,root_ctx,prog_cfg p,cinit_stores pgs⇙ v ctx)"
  by (rule node_collect_eq_Union_activation_of_has_context)
     (rule entry_state_has_context [OF entry_cov])

end

text ‹
  A route that never reads the state it is handed is a function of the call site
  and the caller's context alone, so the contexts it admits are the graph of a
  context function on concrete stores -- the shape the call-string and
  context-insensitive policies both publish. ‹route_const› is the whole of what
  such a policy owes; ‹ctx_fun› is its trace-semantic counterpart.
›

corollary fun_route_activation_collect_sound:
  fixes ctx_fun :: "cfg_node ⇒ 'c ⇒ store ⇒ 'c"
  assumes route_const: "⋀u ctx d ca s. route pgs u ctx d ca = ctx_fun u ctx s"
    and solves: "terminates pgs p"
    and fwd_ok: "⋀u a v ctx. (u, ctx) ∈ sol_vars pgs p
        ⟹ (u, a, v) ∈ intra (prog_cfg p) ⟹ (v, ctx) ∈ sol_vars pgs p"
    and call_fwd_ok: "⋀u ctx dst pars args q cont d. (u, ctx) ∈ sol_vars pgs p
        ⟹ (u, CallEdge dst pars args, FunctionEntry q, cont) ∈ calls (prog_cfg p)
        ⟹ (FunctionEntry q, route pgs u ctx d (CallEdge dst pars args))
              ∈ sol_vars pgs p"
    and comb_fwd_ok: "⋀cl c1 dst pars args q cont. (cl, c1) ∈ sol_vars pgs p
        ⟹ (cl, CallEdge dst pars args, FunctionEntry q, cont) ∈ calls (prog_cfg p)
        ⟹ (cont, c1) ∈ sol_vars pgs p"
    and entry_cov: "(cfg_entry (prog_cfg p), root_ctx) ∈ sol_vars pgs p"
  shows "𝒜⇘pgs,context_policy_of_fun ctx_fun,root_ctx,prog_cfg p,cinit_stores pgs⇙ v ctx
           ⊆ pgam (reader pgs p (Inl (v, ctx))) gsol"
proof (rule activation_collect_sound_of[OF solves entry_cov fwd_ok comb_fwd_ok])
  fix u ctx dst pars args q cont and s :: store and ctx'
  assume covV: "(u, ctx) ∈ sol_vars pgs p"
    and ce: "(u, CallEdge dst pars args, FunctionEntry q, cont) ∈ calls (prog_cfg p)"
    and Rc: "ctx' ∈ context_policy_of_fun ctx_fun u ctx
               (call_info_of (CallEdge dst pars args) q) s
               (call_enter pgs (CallEdge dst pars args) s)"
  have req: "route pgs u ctx d (CallEdge dst pars args) = ctx'" for d
    using Rc
    by (simp add: route_const[of _ _ _ _ "call_enter pgs (CallEdge dst pars args) s"])
  show "route pgs u ctx
          (entered (call_info_of (CallEdge dst pars args) q)
             (dg_local (sol_env pgs p (Inl (u, ctx)))))
          (CallEdge dst pars args) = ctx'
        ∧ (FunctionEntry q, ctx') ∈ sol_vars pgs p"
    using req call_fwd_ok[OF covV ce] by simp
next
  fix u ctx dst pars args q cont and s :: store
  show "context_policy_of_fun ctx_fun u ctx
                 (call_info_of (CallEdge dst pars args) q) s
                 (call_enter pgs (CallEdge dst pars args) s) ≠ {}"
    by simp
qed

text ‹
  The functional route's counterpart of the entry-state union. It needs nothing
  at all: const‹activation_context_of› is total, so every valid activation trace carries a context without
  any coverage having been established.
›

theorem fun_route_node_collect_eq_Union:
  "𝒞⇘pgs,prog_cfg p,cinit_stores pgs⇙ v
     = (⋃ctx. 𝒜⇘pgs,context_policy_of_fun ctx_fun,root_ctx,prog_cfg p,cinit_stores pgs⇙ v ctx)"
  by (rule node_collect_eq_Union_activation_of_fun)

end

end

subsection ‹Every variable in the local half›

text ‹
  The placement every analysis uses unless it shares program globals: the
  specification is the component's own const‹dg_spec_of›, a point's state is
  its local unknown alone, and nothing is published at the analysis-wide global.
  Its placement obligations follow from the component's soundness and its
  single-alternative entry, so an instance owes exactly what it owed before the
  placement was a parameter.
›

theorem dg_analysis_whole_stateI:
  fixes comp :: "(vname ⇒ bool) ⇒ imp_prog ⇒ 's::semilattice_sup lifted local_spec"
    and seed :: "pp ⇒ 'c ⇒ 'k"
  assumes solver: "certified_solver solve solve_dom solve_c"
    and comp_sound:
      "⋀p. sound_local_spec (declared_global p)
               (λd. gamma_lift γV (map_lift (rd (declared_global p)) d))
               (comp (declared_global p) p)"
    and enter_single:
      "⋀p ci d. ls_enter (comp (declared_global p) p) (ls_channel (comp (declared_global p) p) d)
                    ci (d, d)
                  = [(d, dg_pipeline.comp_entry comp (declared_global p) p ci d)]"
    and empty_rd: "⋀p s. emp p s ⟷ emptyV (rd (declared_global p) s)"
    and emptyV_sound: "sound_emptiness emptyV γV"
    and seed_ne: "⋀v ctx. seed v ctx ≠ buffer_key"
    and classify_proved:
      "⋀c d s. classify c d = Check_Proved ⟹ s ∈ γV d ⟹ truthy (⟦c⟧e s)"
    and classify_refuted:
      "⋀c d s. classify c d = Check_Refuted ⟹ s ∈ γV d
         ⟹ ¬ truthy (⟦c⟧e s)"
    and bot_state_empty: "γV bot_state = {}"
    and init_sound:
      "⋀p. cinit_stores (declared_global p) ⊆ γV (rd (declared_global p) init_st)"
    and seed_ne_global: "⋀v ctx n. seed v ctx ≠ global_of n"
  shows "dg_analysis comp emp rd init_st buffer_key global_of seed
           solve solve_dom bot_state classify γV emptyV solve_c
           (λ𝒢 c. dg_spec_of c) (λ𝒢 d e. d) (λ𝒢 d. d) (λ𝒢 d. Bot) (λ𝒢 p. [])
           (λp ci d e. d)"
proof (rule dg_analysis.intro[OF solver dg_analysis_axioms.intro], goal_cases CompSound
    EnterSingle Contract Runs Deps EntrySound CmbBot Init EmptyRd EmptyV SeedNe SeedNeGlobal
    ClProved ClRefuted BotState InitSound)
  case (CompSound p) show ?case by (rule comp_sound)
next
  case (EnterSingle p ci d) show ?case by (rule enter_single)
next
  case (Contract p) show ?case
    unfolding dg_pipeline.analysis_spec_def by (rule dg_spec_of_contract[OF comp_sound])
next
  case (Runs p ci d σ) show ?case
    by (rule exI, simp add: dg_pipeline.analysis_spec_def dg_spec_of_def enter_single
        dg_pipeline.entry_alt_def, rule enter_runs_local_enter_transfer_mk_dg_man)
next
  case (Deps p ci d σ) show ?case
    by (rule exI, simp add: dg_pipeline.analysis_spec_def dg_spec_of_def enter_single
        dg_pipeline.entry_alt_def, rule enter_deps_local_enter_transfer_mk_dg_man)
next
  case (EntrySound p ci d σ pub s)
  let ?c = "comp (declared_global p) p"
  obtain q where "q ∈ set (ls_enter ?c (ls_channel ?c d) ci (d, d))"
      "s ∈ gamma_lift γV (map_lift (rd (declared_global p)) (fst q))"
      "call_enter (declared_global p) (CallEdge (ci_dst ci) (ci_formals ci) (ci_args ci)) s
         ∈ gamma_lift γV (map_lift (rd (declared_global p)) (snd q))"
    using comp_sound[of p] EntrySound(3) ls_channel_sound[OF comp_sound[of p]]
    unfolding sound_local_spec_def by (metis fst_conv)
  then show ?case
    by (simp add: enter_single dg_pipeline.entry_alt_def)
next
  case (CmbBot p g) show ?case by simp
next
  case (Init p) show ?case by simp
next
  case (EmptyRd p s) show ?case by (rule empty_rd)
next
  case EmptyV show ?case by (rule emptyV_sound)
next
  case (SeedNe v ctx) show ?case by (rule seed_ne)
next
  case (SeedNeGlobal v ctx n) show ?case by (rule seed_ne_global)
next
  case (ClProved c d s) then show ?case by (rule classify_proved)
next
  case (ClRefuted c d s) then show ?case by (rule classify_refuted)
next
  case BotState show ?case by (rule bot_state_empty)
next
  case (InitSound p) show ?case by (rule init_sound)
qed

subsection ‹Program globals keyed by name›

text ‹
  The placement that keeps each program global at its own unknown
  ‹global_of x›. The specification is the component wrapped by the keyed lifter,
  and a point's state is its local half recombined with the solved environment
  over the program's declared globals. The root publishes each declared
  global's initial value at its key, and the node-owned buffer stays empty.

  Beyond the component's own soundness, the carrier owes monotone operations,
  that a name outside a read set is cut below ‹free› of that set, and two facts
  about concretization: the frame law ‹mix› of @{thm [source] keyed_split_contract},
  and that a state is described by its local half recombined with its own cuts.
›

theorem dg_analysis_keyedI:
  fixes comp :: "(vname ⇒ bool) ⇒ imp_prog ⇒ 's::semilattice_sup lifted local_spec"
    and seed :: "pp ⇒ 'c ⇒ 'k"
    and global_of :: "vname ⇒ 'k"
    and cmb :: "'s lifted ⇒ 's lifted ⇒ 's lifted"
    and rg :: "vname ⇒ 's lifted ⇒ 's lifted"
  assumes solver: "certified_solver solve solve_dom solve_c"
    and comp_sound:
      "⋀p. sound_local_spec (declared_global p)
               (λd. gamma_lift γV (map_lift (rd (declared_global p)) d))
               (comp (declared_global p) p)"
    and enter_single:
      "⋀p ci d. ls_enter (comp (declared_global p) p) (ls_channel (comp (declared_global p) p) d)
                    ci (d, d)
                  = [(d, dg_pipeline.comp_entry comp (declared_global p) p ci d)]"
    and cmb_mono: "⋀d d' g g'. d ≤ d' ⟹ g ≤ g' ⟹ cmb d g ≤ cmb d' g'"
    and cmb_rl: "⋀x g. cmb (rl x) g = cmb x g"
    and rl_cmb: "⋀d g. rl (cmb d g) = rl d"
    and cmb_bot: "⋀g. cmb Bot g = Bot"
    and rg_mono: "⋀x v v'. v ≤ v' ⟹ rg x v ≤ rg x v'"
    and rg_free: "⋀x R v. x ∉ set R ⟹ rg x v ≤ free R"
    and mix: "⋀p l0 e r s s' W.
        s ∈ gamma_lift γV (map_lift (rd (declared_global p))
               (cmb l0 (full_view rg (declared_global_vars p) e)))
        ⟹ s' ∈ gamma_lift γV (map_lift (rd (declared_global p)) r)
        ⟹ (∀x. declared_global p x ⟶ x ∉ set W ⟶ s' x = s x)
        ⟹ s' ∈ gamma_lift γV (map_lift (rd (declared_global p))
               (cmb (rl r) (full_view rg (declared_global_vars p)
                  (λx. if x ∈ set W then rg x r else e x))))"
    and init_view: "⋀p. gamma_lift γV (map_lift (rd (declared_global p)) (Lifted init_st))
        ⊆ gamma_lift γV (map_lift (rd (declared_global p))
             (cmb (Lifted init_st) (full_view rg (declared_global_vars p)
                (λx. rg x (Lifted init_st)))))"
    and empty_rd: "⋀p s. emp p s ⟷ emptyV (rd (declared_global p) s)"
    and emptyV_sound: "sound_emptiness emptyV γV"
    and seed_ne: "⋀v ctx. seed v ctx ≠ buffer_key"
    and seed_ne_global: "⋀v ctx n. seed v ctx ≠ global_of n"
    and classify_proved:
      "⋀c d s. classify c d = Check_Proved ⟹ s ∈ γV d ⟹ truthy (⟦c⟧e s)"
    and classify_refuted:
      "⋀c d s. classify c d = Check_Refuted ⟹ s ∈ γV d
         ⟹ ¬ truthy (⟦c⟧e s)"
    and bot_state_empty: "γV bot_state = {}"
    and init_sound:
      "⋀p. cinit_stores (declared_global p) ⊆ γV (rd (declared_global p) init_st)"
  shows "dg_analysis comp emp rd init_st buffer_key global_of seed
           solve solve_dom bot_state classify γV emptyV solve_c
           (λp c. keyed_split_spec (declared_global p) cmb rl rg free c)
           (λp d e. cmb d (full_view rg (declared_global_vars p) e)) (λ𝒢. rl) (λ𝒢 d. Bot)
           (λ𝒢 p. map (λx. (x, rg x (Lifted init_st))) (declared_global_vars p))
           (λp ci d e. cmb d (view_of rg (call_global_reads (declared_global p) (ci_args ci)) e
              (free (call_global_reads (declared_global p) (ci_args ci)))))"
proof -
  let ?gm = "λp d. gamma_lift γV (map_lift (rd (declared_global p)) d)"
  have gm_mono: "⋀p x y. x ≤ y ⟹ ?gm p x ⊆ ?gm p y"
    using comp_sound unfolding sound_local_spec_def by metis
  show ?thesis
  proof (rule dg_analysis.intro[OF solver dg_analysis_axioms.intro], goal_cases CompSound
      EnterSingle Contract Runs Deps EntrySound CmbBot Init EmptyRd EmptyV SeedNe SeedNeGlobal
      ClProved ClRefuted BotState InitSound)
    case (CompSound p) show ?case by (rule comp_sound)
  next
    case (EnterSingle p ci d) show ?case by (rule enter_single)
  next
    case (Contract p) show ?case
      unfolding dg_pipeline.analysis_spec_def
      using comp_sound[where p = p] cmb_mono rg_mono rg_free mix[where p = p]
      by (rule keyed_split_contract)
  next
    case (Runs p ci d σ) show ?case
      using enter_runs_keyed_enter_transfer[of cmb rl rg free
          "call_global_reads (declared_global p) (ci_args ci)"
          "global_names_in (declared_global p) (ci_formals ci)"
          "λd. ls_enter (comp (declared_global p) p) (ls_channel (comp (declared_global p) p) d)
             ci (d, d)" d global_of σ]
      by (auto simp: dg_pipeline.analysis_spec_def dg_pipeline.entry_alt_def enter_single Let_def)
  next
    case (Deps p ci d σ) show ?case
      using enter_deps_keyed_enter_transfer[of cmb rl rg free
          "call_global_reads (declared_global p) (ci_args ci)"
          "global_names_in (declared_global p) (ci_formals ci)"
          "λd. ls_enter (comp (declared_global p) p) (ls_channel (comp (declared_global p) p) d)
             ci (d, d)" d global_of σ]
      by (auto simp: dg_pipeline.analysis_spec_def dg_pipeline.entry_alt_def enter_single Let_def)
  next
    case (EntrySound p ci d σ pub s)
    let ?𝒢 = "declared_global p" and ?xs = "declared_global_vars p"
    let ?c = "comp ?𝒢 p"
    let ?e = "genv global_of σ"
    let ?R = "call_global_reads ?𝒢 (ci_args ci)"
    let ?v = "view_of rg ?R ?e (free ?R)"
    let ?w = "cmb d ?v"
    let ?E = "dg_pipeline.comp_entry comp ?𝒢 p ci ?w"
    let ?W = "global_names_in ?𝒢 (ci_formals ci)"
    let ?ce = "call_enter ?𝒢 (CallEdge (ci_dst ci) (ci_formals ci) (ci_args ci))"
    have alt: "dg_pipeline.entry_alt comp (λ𝒢. rl)
        (λp ci d e. cmb d (view_of rg (call_global_reads (declared_global p) (ci_args ci)) e
           (free (call_global_reads (declared_global p) (ci_args ci)))))
        ?𝒢 p ci d ?e = (rl ?w, rl ?E)"
      by (simp add: dg_pipeline.entry_alt_def Let_def)
    have R0: "enter_runs (enter♯ (dg_pipeline.analysis_spec comp
          (λp. keyed_split_spec (declared_global p) cmb rl rg free) ?𝒢 p) ci)
        (mk_dg_man d global_of) σ [(rl ?w, rl ?E)] (pub_sides global_of rg ?W (?E ⊔ bot))"
      using enter_runs_keyed_enter_transfer[of cmb rl rg free ?R ?W
          "λd. ls_enter ?c (ls_channel ?c d) ci (d, d)" d global_of σ]
      by (simp add: dg_pipeline.analysis_spec_def enter_single)
    have pub: "pub = pub_sides global_of rg ?W (?E ⊔ bot)"
      using enter_runsD_sides[OF EntrySound(1)[unfolded alt], of "λ_. Answer bot"]
        enter_runsD_sides[OF R0, of "λ_. Answer bot"]
      by (simp add: bot_fun_def[symmetric])
    have E_Bot: "?E ⊔ Bot = ?E" by (cases ?E) simp_all
    have le: "rg x ?E ≤ ?e x" if "x ∈ set ?W" for x
    proof -
      have "DG bot (rg x ?E) ≤ pub (Inr (global_of x))"
        using pub_sides_ge[OF that, of rg "?E ⊔ bot" global_of] by (simp add: pub E_Bot)
      then have "rg x ?E ≤ genv global_of pub x" by (simp add: less_eq_dg_state_def)
      also have "… ≤ ?e x" using EntrySound(2) by (simp add: le_fun_def)
      finally show ?thesis .
    qed
    have view_ge: "full_view rg ?xs ?e ≤ ?v"
      unfolding full_view_def
    proof (rule view_of_le)
      fix x assume "x ∈ set ?xs"
      show "rg x (?e x) ≤ ?v"
      proof (cases "x ∈ set ?R")
        case True then show ?thesis by (rule view_of_ge_mem)
      next
        case False
        then show ?thesis using rg_free view_of_ge_acc by (blast intro: order_trans)
      qed
    qed simp
    have s0: "s ∈ ?gm p (cmb d (full_view rg ?xs ?e))" using EntrySound(3) by simp
    have sw: "s ∈ ?gm p ?w"
      using s0 gm_mono[OF cmb_mono[OF order_refl view_ge]] by blast
    have sE: "?ce s ∈ ?gm p ?E"
    proof -
      obtain q where "q ∈ set (ls_enter ?c (ls_channel ?c ?w) ci (?w, ?w))" "?ce s ∈ ?gm p (snd q)"
        using comp_sound[where p = p] sw ls_channel_sound[OF comp_sound[where p = p] sw]
        unfolding sound_local_spec_def by (metis fst_conv)
      then show ?thesis by (simp add: enter_single)
    qed
    have fr: "∀x. ?𝒢 x ⟶ x ∉ set ?W ⟶ ?ce s x = s x"
      by (auto intro: call_enter_frame)
    have "?ce s ∈ ?gm p (cmb (rl ?E) (full_view rg ?xs (λx. if x ∈ set ?W then rg x ?E else ?e x)))"
      by (rule mix[OF s0 sE fr])
    moreover have "cmb (rl ?E) (full_view rg ?xs (λx. if x ∈ set ?W then rg x ?E else ?e x))
        ≤ cmb (rl ?E) (full_view rg ?xs ?e)"
    proof (intro cmb_mono order_refl full_view_mono[OF rg_mono])
      fix x
      show "(if x ∈ set ?W then rg x ?E else ?e x) ≤ ?e x"
        using le[of x] by (cases "x ∈ set ?W") simp_all
    qed
    ultimately have "?ce s ∈ ?gm p (cmb (rl ?E) (full_view rg ?xs ?e))"
      using gm_mono by blast
    moreover have "s ∈ ?gm p (cmb (rl ?w) (full_view rg ?xs ?e))"
      using s0 by (simp add: rl_cmb cmb_rl)
    ultimately show ?case by (simp add: alt)
  next
    case (CmbBot p g) show ?case by (simp add: cmb_bot)
  next
    case (Init p)
    let ?xs = "declared_global_vars p"
    let ?ie = "dg_pipeline.init_env init_st buffer_key global_of (λ𝒢 d. Bot)
      (λ𝒢 p. map (λx. (x, rg x (Lifted init_st))) (declared_global_vars p)) (declared_global p) p"
    have ie: "rg x (Lifted init_st) ≤ ?ie x" if x: "x ∈ set ?xs" for x
    proof -
      have "rg x (Lifted init_st)
          ≤ (⨆(m, d)←filter (λ(m, d). m = x) (map (λy. (y, rg y (Lifted init_st))) ys). d)"
        if "x ∈ set ys" for ys
        using that by (induction ys) (auto intro: le_supI1 le_supI2)
      then show ?thesis using x by (simp add: dg_pipeline.init_env_def le_supI2)
    qed
    have "full_view rg ?xs (λx. rg x (Lifted init_st)) ≤ full_view rg ?xs ?ie"
      unfolding full_view_def
    proof (rule view_of_le)
      fix x assume x: "x ∈ set ?xs"
      show "rg x (rg x (Lifted init_st)) ≤ view_of rg ?xs ?ie bot"
        using rg_mono[OF ie[OF x], of x] view_of_ge_mem[OF x, of rg ?ie bot] by (rule order_trans)
    qed simp
    then show ?case
      using init_view[where p = p] gm_mono[OF cmb_mono[OF order_refl], where p = p] by blast
  next
    case (EmptyRd p s) show ?case by (rule empty_rd)
  next
    case EmptyV show ?case by (rule emptyV_sound)
  next
    case (SeedNe v ctx) show ?case by (rule seed_ne)
  next
    case (SeedNeGlobal v ctx n) show ?case by (rule seed_ne_global)
  next
    case (ClProved c d s) then show ?case by (rule classify_proved)
  next
    case (ClRefuted c d s) then show ?case by (rule classify_refuted)
  next
    case BotState show ?case by (rule bot_state_empty)
  next
    case (InitSound p) show ?case by (rule init_sound)
  qed
qed

subsection ‹An executable analysis as the pipeline's component›

text ‹
  What an executable non-relational analysis owes, stated over its own
  transfers: the abstract transfer it implements is sound, its executable
  mirror represents that transfer, and its routing, solver, classifier and
  entry state satisfy the pipeline's contracts on the represented abstract store.
  Its component is const‹exec_local_spec›, so the generic pipeline runs
  exactly its local specification, and every theorem above holds of it.

  ‹route_abs› is the same routing decision taken on the abstract carrier, kept
  for the analyses that state it; the pipeline's soundness does not use it.
›

locale dg_analysis_exec = certified_solver solve solve_dom solve_c
  for tf_st :: "(vname ⇒ bool) ⇒ edge_action
                  ⇒ 'a::numeric_domain default_st ⇒ 'a default_st"
    and enter_st :: "(vname ⇒ bool) ⇒ call_info ⇒ 'a default_st ⇒ 'a default_st"
    and init_st :: "'a default_st"
    and buffer_key :: 'k
    and global_of :: "unit ⇒ 'k"
    and seed :: "pp ⇒ 'c ⇒ 'k"
    and route :: "(vname ⇒ bool) ⇒ pp ⇒ 'c ⇒ 'a default_st lifted ⇒ call_action ⇒ 'c"
    and root_ctx :: 'c
    and solve :: "(pp × 'c, 'k, ('a default_st lifted, 'a default_st lifted) dg_state) eqsT
                  ⇒ pp × 'c
                  ⇒ (pp × 'c) set
                       × (pp × 'c + 'k ⇒ ('a default_st lifted, 'a default_st lifted) dg_state)"
    and solve_dom :: "(pp × 'c, 'k, ('a default_st lifted, 'a default_st lifted) dg_state) eqsT
                      ⇒ pp × 'c ⇒ bool"
    and bot_state :: "'a abs_state"
    and classify :: "exp ⇒ 'a abs_state ⇒ check_result"
    and sk :: "'a abs_state ⇒ 'a abs_state"
    and asn :: "vname ⇒ exp ⇒ 'a abs_state ⇒ 'a abs_state"
    and spc :: "special_call ⇒ vname ⇒ 'a abs_state ⇒ 'a abs_state"
    and br :: "exp ⇒ bool ⇒ 'a abs_state ⇒ 'a abs_state"
    and bd :: "pname ⇒ 'a abs_state ⇒ 'a abs_state"
    and rt :: "exp option ⇒ pname ⇒ 'a abs_state ⇒ 'a abs_state"
    and en :: "(vname ⇒ bool) ⇒ call_info ⇒ 'a abs_state ⇒ 'a abs_state"
    and ev :: "analysis_event ⇒ 'a abs_state ⇒ 'a abs_state"
    and route_abs :: "(vname ⇒ bool) ⇒ pp ⇒ 'c ⇒ 'a abs_state lifted ⇒ call_action ⇒ 'c"
    and solve_c :: "(pp × 'c, 'k,
                       ('a default_st lifted, 'a default_st lifted) dg_state) eqsT
                    ⇒ pp × 'c
                    ⇒ ((pp × 'c) set
                          × (pp × 'c + 'k
                               ⇒ ('a default_st lifted, 'a default_st lifted) dg_state)) option" +
  assumes tf_sound: "⋀𝒢. sound_nonrelational_transfer 𝒢 sk asn spc br bd rt (en 𝒢) ev"
    and tf_commute:
      "⋀𝒢 a s. live_default_st 𝒢 s
         ⟹ ρ⇘𝒢⇙ (tf_st 𝒢 a s)
               = local_spec_step sk asn spc br bd rt ev a (ρ⇘𝒢⇙ s)"
    and enter_commute:
      "⋀𝒢 ci s. ρ⇘𝒢⇙ (enter_st 𝒢 ci s)
                    = en 𝒢 ci (ρ⇘𝒢⇙ s)"
    and route_agree:
      "⋀𝒢 u ctx d ca. route 𝒢 u ctx d ca
         = route_abs 𝒢 u ctx (ρ⇘𝒢⇙ d) ca"
    and exec_seed_ne_buffer_key: "⋀v ctx. seed v ctx ≠ buffer_key"
    and exec_classify_proved:
      "⋀c d s. classify c d = Check_Proved ⟹ s ∈ γ d ⟹ truthy (⟦c⟧e s)"
    and exec_classify_refuted:
      "⋀c d s. classify c d = Check_Refuted ⟹ s ∈ γ d
         ⟹ ¬ truthy (⟦c⟧e s)"
    and bot_state_eq: "bot_state = bot"
    and exec_init_sound: "⋀𝒢. cinit_stores 𝒢 ⊆ default_st_gamma 𝒢 init_st"
    and exec_own_key: "⋀n. global_of n = buffer_key"

sublocale dg_analysis_exec ⊆ dg_analysis
    "λ𝒢 p. exec_local_spec 𝒢 (default_st_is_bot_for (declared_global_vars p))
             (tf_st 𝒢) (enter_st 𝒢)"
    "λp. default_st_is_bot_for (declared_global_vars p)"
    default_st_to_fun init_st buffer_key global_of seed route root_ctx
      solve solve_dom bot_state classify
    gamma_state is_empty_state solve_c "λ𝒢 c. dg_spec_of c" "λ𝒢 d e. d" "λ𝒢 d. d"
      "λ𝒢 d. Bot" "λ𝒢 p. []" "λp ci d e. d"
proof (rule dg_analysis_whole_stateI[OF certified_solver_axioms],
    goal_cases CompSound EnterSingle EmptyExact EmptyVExact SeedNe ClProved ClRefuted BotState
    Init SeedNeGlobal)
  case (CompSound p)
  interpret dom: dg_domain_exec "declared_global p"
      "default_st_is_bot_for (declared_global_vars p)" "tf_st (declared_global p)"
      "enter_st (declared_global p)" sk asn spc br bd rt "en (declared_global p)" ev
    by unfold_locales
       (rule tf_commute, assumption,
        rule enter_commute,
        rule default_st_is_bot_for_iff[OF declared_global_iff])
  show ?case
    by (rule dom.exec_local_spec_sound[OF tf_sound, unfolded gamma_lift_default_st_gamma_to_fun])
next
  case (EnterSingle p ci d)
  then show ?case by (simp add: dg_pipeline.comp_entry_def)
next
  case (EmptyExact p s)
  then show ?case by (rule default_st_is_bot_for_iff[OF declared_global_iff])
next
  case EmptyVExact show ?case
    by (rule sound_emptinessI) (erule is_empty_state_gamma_state_empty)
next
  case (SeedNe v ctx) then show ?case by (rule exec_seed_ne_buffer_key)
next
  case (ClProved c d s) then show ?case by (rule exec_classify_proved)
next
  case (ClRefuted c d s) then show ?case by (rule exec_classify_refuted)
next
  case BotState then show ?case
    by (simp add: bot_state_eq is_empty_state_iff_gamma_state_empty[symmetric])
next
  case (Init p) then show ?case
    using exec_init_sound[of "declared_global p"] by (simp add: default_st_gamma_def)
next
  case (SeedNeGlobal v ctx n) show ?case
    unfolding exec_own_key by (rule exec_seed_ne_buffer_key)
qed

text ‹
  The component's soundness at the carrier's own concretization, which is
  what a caller composing components on the carrier needs.
›

lemma (in dg_analysis_exec) exec_comp_sound:
  "sound_local_spec (declared_global p) (gamma_lift (default_st_gamma (declared_global p)))
     (exec_local_spec (declared_global p) (default_st_is_bot_for (declared_global_vars p))
        (tf_st (declared_global p)) (enter_st (declared_global p)))"
  unfolding gamma_lift_default_st_gamma_to_fun by (rule comp_sound)

text ‹
  The state a call enters its callee with, at the executable component: the entry
  transfer, lifted over reachability.
›

lemma (in dg_analysis_exec) comp_entry_exec:
  "dg_pipeline.comp_entry
     (λ𝒢 p. exec_local_spec 𝒢 (default_st_is_bot_for (declared_global_vars p))
        (tf_st 𝒢) (enter_st 𝒢))
     𝒢 p ci d
   = transfer_lift (default_st_is_bot_for (declared_global_vars p)) (enter_st 𝒢 ci) d"
  by (simp add: dg_pipeline.comp_entry_def)

text ‹
  The same against the solved global, the shape const‹dg_pipeline.ctx_succ›
  reads, so a caller can fold an executable entry back into it.
›

lemma (in dg_analysis_exec) entry_of_exec:
  "entry_of 𝒢 p ci d (sol_global 𝒢 p)
   = transfer_lift (default_st_is_bot_for (declared_global_vars p)) (enter_st 𝒢 ci) d"
  by (simp add: dg_pipeline.entry_of_def dg_pipeline.entry_alt_def comp_entry_exec)

end