Theory Voblint_Routing.Compiled_Routed_Equations
theory Compiled_Routed_Equations
imports
"Voblint_Framework.Routed_Context"
"Voblint_Compile.Compile_Wellformed"
"HOL-Library.RBT"
begin
section ‹Executable routed equations for a compiled program›
text ‹
A routed configuration chooses the key a node buffers its own global part at,
the key each global name is read and published at, a seed-key constructor, a
routing function, a D/G specification, an initial local state and an initial
global state. The remaining equation-system wiring is fixed: predecessor
enumeration, edge execution, static call resolution, seed publication, and
bottom initialization of every other unknown.
A context-insensitive analysis is this constructor at the unit context:
‹Analysis_Global ()› as the only global key, ‹Activation_Seed› as the seed
constructor and ‹route_unit› as the routing function. There is no second
unit-context generator.
Solver policy is deliberately outside this constructor. Always-join,
per-origin, and warrowing instances can solve the same equations without
hiding that choice here.
The enumerations the equations read are stated over the edge relations and
answer every query by re-enumerating a whole relation. The code equation at
the end answers them from indexes built once per graph instead, so a solver
step costs a lookup rather than a scan and sort of every edge.
›
definition compiled_routed_eqs_for ::
"'K
⇒ ('N ⇒ 'K)
⇒ (pp ⇒ 'C ⇒ 'K)
⇒ (pp ⇒ 'C ⇒ 'D ⇒ call_action ⇒ 'C)
⇒ (pp × 'C, 'K, 'N,
'D::bounded_semilattice_sup_bot, 'G::bounded_semilattice_sup_bot) dg_spec
⇒ cfg
⇒ 'D
⇒ 'G
⇒ (pp × 'C, 'K, ('D, 'G) dg_state) eqsT"
where
"compiled_routed_eqs_for global global_of seed route S g initial initial_global =
routed_node_rhs_buffered intra_predecessor_addr_list call_site_list
(λ_. global) route
(λctx' src a. dg_spec_edge_program S a src global_of)
(routed_call_program S global_of seed (static_resolve g) (λd. d = bot))
(routed_entry_seed_programs seed)
g bot initial initial_global"
subsection ‹Per-node edge indexes›
text ‹
‹group_by_key key_of f xs› files each element ‹f› accepts under its key,
keeping list order within a key. A lookup is therefore the order-preserving
filter of ‹xs› at that key, which is what lets it replace an enumeration
inside a fold that observes order.
›
definition group_lookup :: "('k::linorder, 'y list) rbt ⇒ 'k ⇒ 'y list" where
"group_lookup m k = (case RBT.lookup m k of None ⇒ [] | Some ys ⇒ ys)"
definition group_by_key ::
"('x ⇒ 'k::linorder) ⇒ ('x ⇒ 'y option) ⇒ 'x list ⇒ ('k, 'y list) rbt" where
"group_by_key key_of f xs =
foldr (λx m. case f x of
None ⇒ m
| Some y ⇒ RBT.insert (key_of x) (y # group_lookup m (key_of x)) m)
xs RBT.empty"
lemma group_lookup_group_by_key:
"group_lookup (group_by_key key_of f xs) k
= List.map_filter (λx. if key_of x = k then f x else None) xs"
by (induction xs) (auto simp: group_by_key_def group_lookup_def split: option.splits)
definition intra_predecessor_index ::
"cfg ⇒ (cfg_node, (cfg_node × edge_action) list) rbt" where
"intra_predecessor_index g =
group_by_key (λ(u, a, w). w) (λ(u, a, w). Some (u, a)) (cfg_intra_list g)"
lemma group_lookup_intra_predecessor_index:
"group_lookup (intra_predecessor_index g) v = intra_predecessor_list g v"
proof -
have "List.map_filter
(λx. if (λ(u, a, w). w) x = v then (λ(u, a, w). Some (u, a)) x else None) es
= map (λ(u, a, w). (u, a)) (filter (λ(u, a, w). w = v) es)"
for es :: "(cfg_node × edge_action × cfg_node) list"
by (induction es) auto
then show ?thesis
unfolding intra_predecessor_index_def intra_predecessor_list_def group_lookup_group_by_key
by simp
qed
lemma map_group_lookup_intra_predecessor_index:
"map (λ(u, a). (Inl (u, ctx), a)) (group_lookup (intra_predecessor_index g) v)
= intra_predecessor_addr_list g v ctx"
by (simp add: intra_predecessor_addr_list_def group_lookup_intra_predecessor_index)
definition call_target_index ::
"cfg ⇒ (cfg_node, (cfg_node × call_action × pname) list) rbt" where
"call_target_index g =
group_by_key (λ(c, ca, ce, k). k) (λ(c, ca, ce, k). call_target_at k (c, ca, ce, k))
(cfg_calls_list g)"
lemma group_lookup_call_target_index:
"group_lookup (call_target_index g) v = call_target_list g v"
proof -
have "(if (λ(c, ca, ce, k). k) e = v
then (λ(c, ca, ce, k). call_target_at k (c, ca, ce, k)) e else None)
= call_target_at v e" for e :: "cfg_node × call_action × cfg_node × cfg_node"
proof -
obtain c ca ce k where e: "e = (c, ca, ce, k)" by (cases e) auto
show ?thesis by (cases ce) (auto simp: e)
qed
then show ?thesis
unfolding call_target_index_def call_target_list_def group_lookup_group_by_key
by simp
qed
text ‹
The indexes are let-bound outside the equation system, so a solver holding
the system evaluates them once. The three selectors restate
\<^const>‹intra_predecessor_addr_list›, \<^const>‹call_site_list› and
\<^const>‹static_resolve› over a lookup in place of the enumeration. They
ignore their graph argument, which is sound only because the generator
applies every selector at the one graph it is given.
›
lemma compiled_routed_eqs_for_code [code]:
"compiled_routed_eqs_for global global_of seed route S g initial initial_global =
(let preds = intra_predecessor_index g; targets = call_target_index g
in routed_node_rhs_buffered
(λ_ v ctx. map (λ(u, a). (Inl (u, ctx), a)) (group_lookup preds v))
(λ_ v. remdups (map (λ(c, ca, p). (c, ca)) (group_lookup targets v)))
(λ_. global) route
(λctx' src a. dg_spec_edge_program S a src global_of)
(routed_call_program S global_of seed
(λv cc ca d. map (λ(c, a, p). p)
(filter (λ(c, a, p). c = cc ∧ a = ca) (group_lookup targets v)))
(λd. d = bot))
(routed_entry_seed_programs seed)
g bot initial initial_global)"
proof -
note preds = map_group_lookup_intra_predecessor_index
have sites: "remdups (map (λ(c, ca, p). (c, ca)) (group_lookup (call_target_index g) v))
= call_site_list g v" for v
by (simp add: call_site_list_def group_lookup_call_target_index)
have resolve: "(λv cc ca d. map (λ(c, a, p). p)
(filter (λ(c, a, p). c = cc ∧ a = ca) (group_lookup (call_target_index g) v)))
= static_resolve g"
by (intro ext) (simp add: static_resolve_def static_targets_def group_lookup_call_target_index)
show ?thesis
unfolding compiled_routed_eqs_for_def routed_node_rhs_buffered_def
routed_contribution_programs_def Let_def
by (simp only: preds sites resolve)
qed
end