You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

838 lines
26 KiB

(*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*)
(* Define SSA form and the concept of variable liveness, and then show how SSA
* simplifies it *)
open HolKernel boolLib bossLib Parse;
open pred_setTheory listTheory rich_listTheory pairTheory arithmeticTheory;
open settingsTheory miscTheory llvmTheory llvm_propTheory;
new_theory "llvm_ssa";
numLib.prefer_num ();
(* ----- Static paths through a program ----- *)
Definition inc_pc_def:
inc_pc ip = ip with i := inc_bip ip.i
End
(* The set of program counters the given instruction and starting point can
* immediately reach, within a function *)
Definition instr_next_ips_def:
(instr_next_ips (Ret _) ip = {})
(instr_next_ips (Br _ l1 l2) ip =
{ <| f := ip.f; b := Some l; i := Phi_ip ip.b |> | l | l {l1; l2} })
(instr_next_ips (Invoke _ _ _ _ l1 l2) ip =
{ <| f := ip.f; b := Some l; i := Phi_ip ip.b |> | l | l {l1; l2} })
(instr_next_ips Unreachable ip = {})
(instr_next_ips (Exit _) ip = {})
(instr_next_ips (Sub _ _ _ _ _ _) ip = { inc_pc ip })
(instr_next_ips (Extractvalue _ _ _) ip = { inc_pc ip })
(instr_next_ips (Insertvalue _ _ _ _) ip = { inc_pc ip })
(instr_next_ips (Alloca _ _ _) ip = { inc_pc ip })
(instr_next_ips (Load _ _ _) ip = { inc_pc ip })
(instr_next_ips (Store _ _) ip = { inc_pc ip })
(instr_next_ips (Gep _ _ _ _) ip = { inc_pc ip })
(instr_next_ips (Cast _ _ _ _) ip = { inc_pc ip })
(instr_next_ips (Icmp _ _ _ _ _) ip = { inc_pc ip })
(instr_next_ips (Call _ _ _ _) ip = { inc_pc ip })
(instr_next_ips (Cxa_allocate_exn _ _) ip = { inc_pc ip })
(* TODO: revisit throw when dealing with exceptions *)
(instr_next_ips (Cxa_throw _ _ _) ip = { })
(instr_next_ips (Cxa_begin_catch _ _) ip = { inc_pc ip })
(instr_next_ips (Cxa_end_catch) ip = { inc_pc ip })
(instr_next_ips (Cxa_get_exception_ptr _ _) ip = { inc_pc ip })
End
Inductive next_ips:
(∀prog ip i l i2.
get_instr prog ip (Inl i)
l instr_next_ips i ip
get_instr prog l i2
next_ips prog ip l)
(∀prog ip from_l phis i2.
get_instr prog ip (Inr (from_l, phis))
get_instr prog (inc_pc ip) i2
next_ips prog ip (inc_pc ip))
End
(* The path is a list of program counters that represent a statically feasible
* path through a function *)
Inductive good_path:
(∀prog. good_path prog [])
(∀prog ip i.
get_instr prog ip i
good_path prog [ip])
(∀prog path ip1 ip2.
ip2 next_ips prog ip1
good_path prog (ip2::path)
good_path prog (ip1::ip2::path))
End
Theorem next_ips_same_func:
∀prog ip1 ip2. ip2 next_ips prog ip1 ip1.f = ip2.f
Proof
rw [next_ips_cases, IN_DEF, get_instr_cases, inc_pc_def, inc_bip_def] >> rw [] >>
Cases_on `el idx b.body` >> fs [instr_next_ips_def, inc_pc_def, inc_bip_def]
QED
Theorem good_path_same_func:
∀prog path. good_path prog path ∀ip1 ip2. mem ip1 path mem ip2 path ip1.f = ip2.f
Proof
ho_match_mp_tac good_path_ind >> rw [] >>
metis_tac [next_ips_same_func]
QED
Theorem good_path_prefix:
∀prog path path'. good_path prog path path' path good_path prog path'
Proof
Induct_on `path'` >> rw []
>- simp [Once good_path_cases] >>
pop_assum mp_tac >> CASE_TAC >> rw [] >>
qpat_x_assum `good_path _ _` mp_tac >>
simp [Once good_path_cases] >> rw [] >> fs []
>- (simp [Once good_path_cases] >> metis_tac []) >>
first_x_assum drule >> rw [] >>
simp [Once good_path_cases] >>
Cases_on `path'` >> fs [next_ips_cases, IN_DEF] >>
metis_tac []
QED
Theorem good_path_append:
!prog p1 p2.
good_path prog (p1++p2)
good_path prog p1 good_path prog p2
(p1 [] p2 [] HD p2 next_ips prog (last p1))
Proof
Induct_on `p1` >> rw []
>- metis_tac [good_path_rules] >>
Cases_on `p1` >> Cases_on `p2` >> rw []
>- metis_tac [good_path_rules]
>- (
simp [Once good_path_cases] >>
metis_tac [good_path_rules, next_ips_cases, IN_DEF])
>- metis_tac [good_path_rules] >>
rename1 `ip1::ip2::(ips1++ip3::ips2)` >>
first_x_assum (qspecl_then [`prog`, `[ip3]++ips2`] mp_tac) >>
rw [] >> simp [Once good_path_cases, LAST_DEF] >> rw [] >>
eq_tac >> rw []
>- metis_tac [good_path_rules]
>- (qpat_x_assum `good_path _ [_;_]` mp_tac >> simp [Once good_path_cases])
>- metis_tac [good_path_rules, next_ips_cases, IN_DEF]
>- metis_tac [good_path_rules]
>- (qpat_x_assum `good_path _ (ip1::ip2::ips1)` mp_tac >> simp [Once good_path_cases])
>- (qpat_x_assum `good_path _ (ip1::ip2::ips1)` mp_tac >> simp [Once good_path_cases])
QED
(* ----- Helper functions to get registers out of instructions ----- *)
Definition arg_to_regs_def:
(arg_to_regs (Constant _) = {})
(arg_to_regs (Variable r) = {r})
End
(* The registers that an instruction uses *)
Definition instr_uses_def:
(instr_uses (Ret (_, a)) = arg_to_regs a)
(instr_uses (Br a _ _) = arg_to_regs a)
(instr_uses (Invoke _ _ a targs _ _) =
arg_to_regs a BIGUNION (set (map (arg_to_regs o snd) targs)))
(instr_uses Unreachable = {})
(instr_uses (Exit a) = arg_to_regs a)
(instr_uses (Sub _ _ _ _ a1 a2) =
arg_to_regs a1 arg_to_regs a2)
(instr_uses (Extractvalue _ (_, a) _) = arg_to_regs a)
(instr_uses (Insertvalue _ (_, a1) (_, a2) _) =
arg_to_regs a1 arg_to_regs a2)
(instr_uses (Alloca _ _ (_, a)) = arg_to_regs a)
(instr_uses (Load _ _ (_, a)) = arg_to_regs a)
(instr_uses (Store (_, a1) (_, a2)) =
arg_to_regs a1 arg_to_regs a2)
(instr_uses (Gep _ _ (_, a) targs) =
arg_to_regs a BIGUNION (set (map (arg_to_regs o snd) targs)))
(instr_uses (Cast _ _ (_, a) _) = arg_to_regs a)
(instr_uses (Icmp _ _ _ a1 a2) =
arg_to_regs a1 arg_to_regs a2)
(instr_uses (Call _ _ _ targs) =
BIGUNION (set (map (arg_to_regs o snd) targs)))
(instr_uses (Cxa_allocate_exn _ a) = arg_to_regs a)
(instr_uses (Cxa_throw a1 a2 a3) =
arg_to_regs a1 arg_to_regs a2 arg_to_regs a3)
(instr_uses (Cxa_begin_catch _ a) = arg_to_regs a)
(instr_uses (Cxa_end_catch) = { })
(instr_uses (Cxa_get_exception_ptr _ a) = arg_to_regs a)
End
Definition phi_uses_def:
phi_uses from_l (Phi _ _ entries) =
case alookup entries from_l of
| None => {}
| Some a => arg_to_regs a
End
Inductive uses:
(∀prog ip i r.
get_instr prog ip (Inl i)
r instr_uses i
uses prog ip r)
(∀prog ip from_l phis r.
get_instr prog ip (Inr (from_l, phis))
r BIGUNION (set (map (phi_uses from_l) phis))
uses prog ip r)
End
Definition cidx_to_num_def:
(cidx_to_num (IntC _ n) = Num (ABS n))
(cidx_to_num _ = 0)
End
(* Convert index lists as for GEP into number lists, for the purpose of
* calculating types. Everything goes to 0 but for positive integer constants,
* because those things can't be used to index anything but arrays, and the type
* for the array contents doesn't depend on the index's value. *)
Definition idx_to_num_def:
(idx_to_num (_, (Constant (IntC _ n))) = Num (ABS n))
(idx_to_num (_, _) = 0)
End
(* The registers that an instruction assigns *)
Definition instr_assigns_def:
(instr_assigns (Invoke r t _ _ _ _) = {(r,t)})
(instr_assigns (Sub r _ _ t _ _) = {(r,t)})
(instr_assigns (Extractvalue r (t,_) idx) = {(r,THE (extract_type t (map cidx_to_num idx)))})
(instr_assigns (Insertvalue r (t,_) _ _) = {(r, t)})
(instr_assigns (Alloca r t _) = {(r,PtrT t)})
(instr_assigns (Load r t _) = {(r,t)})
(instr_assigns (Gep r t _ idx) = {(r,PtrT (THE (extract_type t (map idx_to_num idx))))})
(instr_assigns (Cast r _ _ t) = {(r,t)})
(instr_assigns (Icmp r _ _ _ _) = {(r, IntT W1)})
(instr_assigns (Call r t _ _) = {(r,t)})
(instr_assigns (Cxa_allocate_exn r _) = {(r,ARB)})
(instr_assigns (Cxa_begin_catch r _) = {(r,ARB)})
(instr_assigns (Cxa_get_exception_ptr r _) = {(r,ARB)})
(instr_assigns _ = {})
End
Definition phi_assigns_def:
phi_assigns (Phi r t _) = (r,t)
End
Inductive assigns:
(∀prog ip i r.
get_instr prog ip (Inl i)
r instr_assigns i
assigns prog ip r)
(∀prog ip from_l phis r.
get_instr prog ip (Inr (from_l, phis))
r set (map phi_assigns phis)
assigns prog ip r)
End
(* ----- SSA form ----- *)
Definition entry_ip_def:
entry_ip fname = <| f := fname; b := None; i := Offset 0 |>
End
Definition reachable_def:
reachable prog ip
∃path. good_path prog (entry_ip ip.f :: path) last (entry_ip ip.f :: path) = ip
End
(* To get to ip2 from the entry, you must go through ip1 *)
Definition dominates_def:
dominates prog ip1 ip2
∀path.
good_path prog (entry_ip ip2.f :: path)
last (entry_ip ip2.f :: path) = ip2
mem ip1 (front (entry_ip ip2.f :: path))
End
Definition is_ssa_def:
is_ssa prog
(* Operate function by function *)
(∀fname.
(* No register is assigned in two different instructions *)
(∀r ip1 ip2.
r image fst (assigns prog ip1) r image fst (assigns prog ip2)
ip1.f = fname ip2.f = fname
ip1 = ip2))
(* Each use is dominated by its assignment *)
(∀ip1 r. r uses prog ip1
∃ip2. ip2.f = ip1.f r image fst (assigns prog ip2) dominates prog ip2 ip1)
End
Theorem dominates_trans:
∀prog ip1 ip2 ip3.
dominates prog ip1 ip2 dominates prog ip2 ip3 dominates prog ip1 ip3
Proof
rw [dominates_def] >> simp [FRONT_DEF] >> rw []
>- (first_x_assum (qspec_then `[]` mp_tac) >> rw []) >>
first_x_assum drule >> rw [] >>
qpat_x_assum `mem _ (front _)` mp_tac >>
simp [Once MEM_EL] >> rw [] >> fs [EL_FRONT] >>
first_x_assum (qspec_then `take n path` mp_tac) >> simp [LAST_DEF] >>
rw [] >> fs [entry_ip_def]
>- (fs [Once good_path_cases] >> rw [] >> fs [next_ips_cases, IN_DEF]) >>
rfs [EL_CONS] >>
`?m. n = Suc m` by (Cases_on `n` >> rw []) >>
rw [] >> rfs [] >>
`(el m path).f = ip3.f`
by (
irule good_path_same_func >>
qexists_tac `<| f:= ip3.f; b := NONE; i := Offset 0|> :: path` >>
qexists_tac `prog` >>
conj_tac >- rw [EL_MEM] >>
metis_tac [MEM_LAST]) >>
fs [] >> qpat_x_assum `_ _` mp_tac >> impl_tac
>- (
irule good_path_prefix >> HINT_EXISTS_TAC >> rw [] >>
metis_tac [take_is_prefix]) >>
rw [] >> drule MEM_FRONT >> rw [] >>
fs [MEM_EL, LENGTH_FRONT] >> rfs [EL_TAKE] >> rw [] >>
disj2_tac >> qexists_tac `n'` >> rw [] >>
irule (GSYM EL_FRONT) >>
rw [NULL_EQ, LENGTH_FRONT]
QED
Theorem dominates_unreachable:
∀prog ip1 ip2. ¬reachable prog ip2 dominates prog ip1 ip2
Proof
rw [dominates_def, reachable_def] >>
metis_tac []
QED
Theorem dominates_antisym_lem:
∀prog ip1 ip2. dominates prog ip1 ip2 dominates prog ip2 ip1 ¬reachable prog ip1
Proof
rw [dominates_def, reachable_def] >> CCONTR_TAC >> fs [] >>
Cases_on `ip1 = entry_ip ip1.f` >> fs []
>- (
first_x_assum (qspec_then `[]` mp_tac) >> rw [] >>
fs [Once good_path_cases, IN_DEF, next_ips_cases] >>
metis_tac []) >>
`path []` by (Cases_on `path` >> fs []) >>
`(OLEAST n. n < length path el n path = ip1) None`
by (
rw [whileTheory.OLEAST_EQ_NONE] >>
qexists_tac `PRE (length path)` >> rw [] >>
fs [LAST_DEF, LAST_EL] >>
Cases_on `path` >> fs []) >>
qabbrev_tac `path1 = splitAtPki (\n ip. ip = ip1) (\x y. x) path` >>
first_x_assum (qspec_then `path1 ++ [ip1]` mp_tac) >>
simp [] >>
conj_asm1_tac >> rw []
>- (
irule good_path_prefix >>
HINT_EXISTS_TAC >> rw [] >>
unabbrev_all_tac >> rw [splitAtPki_EQN] >>
CASE_TAC >> rw [] >>
fs [whileTheory.OLEAST_EQ_SOME] >>
rw [GSYM SNOC_APPEND, SNOC_EL_TAKE] >>
metis_tac [take_is_prefix])
>- rw [LAST_DEF] >>
simp [GSYM SNOC_APPEND, FRONT_SNOC, FRONT_DEF] >>
CCONTR_TAC >> fs [MEM_EL]
>- (
first_x_assum (qspec_then `[]` mp_tac) >>
fs [entry_ip_def, Once good_path_cases, IN_DEF, next_ips_cases] >>
metis_tac []) >>
rw [] >>
rename [`n1 < length _`, `last _ = el n path`] >>
first_x_assum (qspec_then `take (Suc n1) path1` mp_tac) >> rw []
>- (
irule good_path_prefix >> HINT_EXISTS_TAC >> rw [entry_ip_def]
>- (
irule good_path_same_func >>
qexists_tac `entry_ip (el n path).f::(path1 ++ [el n path])` >>
qexists_tac `prog` >> rw [EL_MEM]) >>
metis_tac [IS_PREFIX_APPEND3, take_is_prefix, IS_PREFIX_TRANS])
>- (rw [LAST_DEF] >> fs []) >>
rw [METIS_PROVE [] ``~x y (x y)``] >>
simp [EL_FRONT] >>
rename [`n2 < Suc _`] >>
Cases_on `¬(0 < n2)` >> rw [EL_CONS]
>- (
fs [entry_ip_def] >>
`(el n path).f = (el n1 path1).f` suffices_by metis_tac [] >>
irule good_path_same_func >>
qexists_tac `<|f := (el n path).f; b := None; i := Offset 0|> ::(path1 ++ [el n path])` >>
qexists_tac `prog` >>
rw [EL_MEM]) >>
fs [EL_TAKE, Abbr `path1`, splitAtPki_EQN] >>
CASE_TAC >> rw [] >> fs []
>- metis_tac [] >>
fs [whileTheory.OLEAST_EQ_SOME] >>
rfs [LENGTH_TAKE] >>
`PRE n2 < x` by decide_tac >>
first_x_assum drule >>
rw [EL_TAKE]
QED
Theorem dominates_antisym:
∀prog ip1 ip2. reachable prog ip1 dominates prog ip1 ip2 ¬dominates prog ip2 ip1
Proof
metis_tac [dominates_antisym_lem]
QED
Theorem dominates_irrefl:
∀prog ip. reachable prog ip ¬dominates prog ip ip
Proof
metis_tac [dominates_antisym]
QED
(* ----- Liveness ----- *)
Definition live_def:
live prog ip =
{ r | ∃path.
good_path prog (ip::path)
r uses prog (last (ip::path))
∀ip2. ip2 set (front (ip::path)) r image fst (assigns prog ip2) }
End
Theorem get_instr_live:
∀prog ip instr.
get_instr prog ip instr
uses prog ip live prog ip
Proof
rw [live_def, SUBSET_DEF] >>
qexists_tac `[]` >> rw [Once good_path_cases] >>
qexists_tac `instr` >> simp [] >> metis_tac [IN_DEF]
QED
Triviality set_rw:
∀s P. (∀x. x s P x) s = P
Proof
rw [] >> eq_tac >> rw [IN_DEF] >> metis_tac []
QED
Theorem live_gen_kill:
∀prog ip ip'.
live prog ip =
BIGUNION {live prog ip' | ip' | ip' next_ips prog ip} DIFF image fst (assigns prog ip) uses prog ip
Proof
rw [live_def, EXTENSION] >> eq_tac >> rw []
>- (
Cases_on `path` >> fs [] >>
rename1 `ip::ip2::path` >>
qpat_x_assum `good_path _ _` mp_tac >> simp [Once good_path_cases] >> rw [] >>
Cases_on `x uses prog ip` >> fs [] >> simp [set_rw, PULL_EXISTS] >>
qexists_tac `ip2` >> qexists_tac `path` >> rw [])
>- (
fs [] >>
qexists_tac `ip'::path` >> rw [] >>
simp [Once good_path_cases])
>- (
qexists_tac `[]` >> rw [] >>
fs [Once good_path_cases, uses_cases, IN_DEF] >>
metis_tac [])
QED
Theorem ssa_dominates_live_range_lem:
∀prog r ip1 ip2.
is_ssa prog ip1.f = ip2.f r image fst (assigns prog ip1) r live prog ip2
dominates prog ip1 ip2
Proof
rw [dominates_def, is_ssa_def, live_def] >>
`path [] (last path).f = ip2.f`
by (
rw [] >>
irule good_path_same_func >>
qexists_tac `ip2::path` >> rw [] >>
Cases_on `path` >> fs [MEM_LAST] >> metis_tac []) >>
first_x_assum drule >> rw [] >>
first_x_assum (qspec_then `path'++path` mp_tac) >>
impl_tac
>- (
fs [LAST_DEF] >> rw [] >> fs []
>- (
simp_tac std_ss [GSYM APPEND, good_path_append] >> rw []
>- (
qpat_x_assum `good_path _ (_::_)` mp_tac >>
qpat_x_assum `good_path _ (_::_)` mp_tac >>
simp [Once good_path_cases] >>
metis_tac [])
>- (
simp [LAST_DEF] >>
qpat_x_assum `good_path _ (_::_)` mp_tac >>
qpat_x_assum `good_path _ (_::_)` mp_tac >>
simp [Once good_path_cases] >>
rw [] >> rw []))
>- (Cases_on `path` >> fs [])) >>
rw [] >>
`ip2'.f = ip2.f`
by (
irule good_path_same_func >>
qexists_tac `entry_ip ip2.f::path'` >>
qexists_tac `prog` >>
conj_tac
>- (
Cases_on `path` >>
full_simp_tac std_ss [GSYM APPEND, FRONT_APPEND, APPEND_NIL, LAST_CONS]
>- metis_tac [MEM_FRONT] >>
fs [] >> metis_tac [])
>- metis_tac [MEM_LAST]) >>
`ip2' = ip1` by metis_tac [] >>
rw [] >>
Cases_on `path` >> fs [] >>
full_simp_tac std_ss [GSYM APPEND, FRONT_APPEND] >> fs [] >> rw [FRONT_DEF] >> fs []
>- metis_tac []
>- (
`mem ip1 path' = mem ip1 (front path' ++ [last path'])` by metis_tac [APPEND_FRONT_LAST] >>
fs [LAST_DEF] >>
metis_tac [])
>- metis_tac []
>- metis_tac []
QED
Theorem ssa_dominates_live_range:
∀prog r ip.
is_ssa prog r uses prog ip
∃ip1. ip1.f = ip.f r image fst (assigns prog ip1)
∀ip2. ip2.f = ip.f r live prog ip2
dominates prog ip1 ip2
Proof
rw [] >> drule ssa_dominates_live_range_lem >> rw [] >>
fs [is_ssa_def] >>
first_assum drule >> rw [] >> metis_tac []
QED
Theorem reachable_dominates_same_func:
∀prog ip1 ip2. reachable prog ip2 dominates prog ip1 ip2 ip1.f = ip2.f
Proof
rw [reachable_def, dominates_def] >> res_tac >>
irule good_path_same_func >>
metis_tac [MEM_LAST, MEM_FRONT]
QED
Theorem next_ips_reachable:
∀prog ip1 ip2. reachable prog ip1 ip2 next_ips prog ip1 reachable prog ip2
Proof
rw [] >> imp_res_tac next_ips_same_func >>
fs [reachable_def] >>
qexists_tac `path ++ [ip2]` >>
simp_tac std_ss [GSYM APPEND, LAST_APPEND_CONS, LAST_CONS] >>
simp [good_path_append] >>
simp [Once good_path_cases] >>
fs [next_ips_cases, IN_DEF] >>
metis_tac []
QED
(* ----- A theory of *dominator ordered* programs ------ *)
(* A list of basic blocks is dominator ordered if each variable use occurs after
* the assignment to that variable. We can also define a notion of variable
* liveness that follows the list structure, rather than the CFG structure, and
* show that for dominator ordered lists, the live set is empty at the entry
* point *)
Definition instrs_live_def:
(instrs_live [] = ({}, {}))
(instrs_live (i::is) =
let (gen, kill) = instrs_live is in
(instr_uses i (gen DIFF image fst (instr_assigns i)),
(image fst (instr_assigns i) (kill DIFF instr_uses i))))
End
Definition header_uses_def:
(header_uses (Head phis land) =
bigunion { phi_uses from_l p | from_l,p | mem p phis })
(header_uses Entry = {})
End
Definition header_assigns_def:
(header_assigns (Head phis land) = set (map (fst o phi_assigns) phis))
(header_assigns Entry = {})
End
Definition linear_live_def:
(linear_live [] = {})
(linear_live (b::bs) =
let (gen,kill) = instrs_live b.body in
header_uses b.h (gen (linear_live bs DIFF kill) DIFF header_assigns b.h))
End
Definition bip_less_def:
(bip_less (Phi_ip _) (Offset _) T)
(bip_less (Offset m) (Offset n) m < n)
(bip_less _ _ F)
End
Definition linear_pc_less_def:
linear_pc_less = $< LEX bip_less
End
Inductive lpc_get_instr:
(∀i idx bs.
i < length bs
idx < length (el i bs).body
lpc_get_instr bs (i, Offset idx) (Inl (el idx (el i bs).body)))
(∀i from_l phis bs landing.
i < length bs
(el i bs).h = Head phis landing
lpc_get_instr bs (i, Phi_ip from_l) (Inr (from_l, phis)))
End
Inductive lpc_assigns:
(∀bs ip i r.
lpc_get_instr bs ip (Inl i)
r instr_assigns i
lpc_assigns bs ip r)
(∀bs ip from_l phis r.
lpc_get_instr bs ip (Inr (from_l, phis))
r set (map phi_assigns phis)
lpc_assigns bs ip r)
End
Inductive lpc_uses:
(∀bs ip i r.
lpc_get_instr bs ip (Inl i)
r instr_uses i
lpc_uses bs ip r)
(∀bs ip from_l phis r.
lpc_get_instr bs ip (Inr (from_l, phis))
r BIGUNION (set (map (phi_uses from_l) phis))
lpc_uses bs ip r)
End
Definition dominator_ordered_def:
dominator_ordered p
∀f d lip1 r.
alookup p (Fn f) = Some d
r lpc_uses (map snd d.blocks) lip1
∃lip2. linear_pc_less lip2 lip1 r image fst (lpc_assigns (map snd d.blocks) lip2)
End
Theorem instrs_kill_subset_assigns:
snd (instrs_live is) bigunion (image (λi. image fst (instr_assigns i)) (set is))
Proof
Induct_on `is` >> rw [instrs_live_def] >>
pairarg_tac >> rw [] >>
fs [SUBSET_DEF]
QED
Theorem instrs_gen_subset_uses:
fst (instrs_live is) bigunion (image instr_uses (set is))
Proof
Induct_on `is` >> rw [instrs_live_def] >>
pairarg_tac >> rw [] >>
fs [SUBSET_DEF]
QED
Theorem instrs_subset_assigns_subset_kill_gen:
bigunion (image (λi. image fst (instr_assigns i)) (set is))
snd (instrs_live is) fst (instrs_live is)
Proof
Induct_on `is` >> rw [instrs_live_def] >>
pairarg_tac >> rw [] >> fs [SUBSET_DEF] >> rw [] >>
metis_tac []
QED
Theorem use_assign_in_gen_kill:
∀n is r.
n < length is (r image fst (instr_assigns (el n is)) r instr_uses (el n is))
r fst (instrs_live is) r snd (instrs_live is)
Proof
Induct_on `n` >> rw [] >> Cases_on `is` >> rw [] >> fs [] >>
rw [instrs_live_def] >>
pairarg_tac >> rw [] >>
metis_tac [FST, SND, pair_CASES]
QED
Theorem instrs_live_uses:
∀is r.
r fst (instrs_live is)
∃i. i < length is r instr_uses (el i is)
∀j. j < i r instr_uses (el j is) r image fst (instr_assigns (el j is))
Proof
Induct >> rw [instrs_live_def] >> pairarg_tac >> fs []
>- (qexists_tac `0` >> rw []) >>
rename1 `(i1::is)` >>
Cases_on `r instr_uses i1`
>- (qexists_tac `0` >> rw []) >>
first_x_assum drule >> rw [] >>
qexists_tac `Suc i` >> rw [] >>
Cases_on `j` >> fs []
QED
Theorem lpc_get_instr_cons:
∀b bs i bip.
lpc_get_instr (b::bs) (i + 1, bip) = lpc_get_instr bs (i, bip)
Proof
rw [lpc_get_instr_cases, EXTENSION, IN_DEF, EL_CONS] >>
`PRE (i + 1) = i` by decide_tac >>
rw [ADD1]
QED
Theorem lpc_uses_cons:
∀b bs i bip.
lpc_uses (b::bs) (i + 1, bip) = lpc_uses bs (i, bip)
Proof
rw [lpc_uses_cases, EXTENSION, IN_DEF, lpc_get_instr_cons]
QED
Theorem lpc_uses_0_head:
∀b bs. header_uses b.h = bigunion { lpc_uses (b::bs) (0, Phi_ip from_l) | from_l | T}
Proof
rw [EXTENSION, IN_DEF] >>
rw [lpc_uses_cases, lpc_get_instr_cases, PULL_EXISTS] >>
Cases_on `b.h` >> rw [header_uses_def, MEM_MAP, PULL_EXISTS]
>- metis_tac [] >>
eq_tac >> rw []
>- (
qexists_tac `(\x'. ∃y. x' phi_uses from_l y mem y l)` >>
qexists_tac `from_l` >>
rw [] >>
metis_tac []) >>
metis_tac []
QED
Theorem lpc_uses_0_body:
∀b bs. lpc_uses (b::bs) (0, Offset n) fst (instrs_live b.body) snd (instrs_live b.body)
Proof
rw [SUBSET_DEF, IN_DEF] >>
fs [lpc_uses_cases, lpc_get_instr_cases, PULL_EXISTS] >>
metis_tac [use_assign_in_gen_kill, IN_DEF]
QED
Theorem lpc_assigns_cons:
∀b bs i bip.
lpc_assigns (b::bs) (i + 1, bip) = lpc_assigns bs (i, bip)
Proof
rw [lpc_assigns_cases, EXTENSION, IN_DEF, lpc_get_instr_cons]
QED
Theorem lpc_assigns_0_head:
∀b bs from_l.
image fst (lpc_assigns (b::bs) (0, Phi_ip from_l)) = header_assigns b.h
Proof
rw [EXTENSION, Once IN_DEF] >>
rw [lpc_assigns_cases, lpc_get_instr_cases, PULL_EXISTS] >>
Cases_on `b.h` >> rw [header_assigns_def, MEM_MAP] >>
metis_tac []
QED
Theorem lpc_assigns_0_body:
∀b bs. image fst (lpc_assigns (b::bs) (0, Offset n)) fst (instrs_live b.body) snd (instrs_live b.body)
Proof
rw [SUBSET_DEF, IN_DEF] >>
fs [lpc_assigns_cases, lpc_get_instr_cases, PULL_EXISTS] >>
drule use_assign_in_gen_kill >>
rw [] >>
metis_tac [IN_DEF]
QED
Theorem linear_live_uses:
∀bs r. r linear_live bs
∃lip. r lpc_uses bs lip
∀lip2. linear_pc_less lip2 lip r lpc_uses bs lip2 r image fst (lpc_assigns bs lip2)
Proof
Induct >> rw [linear_live_def] >>
rename1 `header_uses b.h` >>
Cases_on `r header_uses b.h`
>- (
fs [header_uses_def] >> pairarg_tac >> fs [] >>
Cases_on `b.h` >> fs [header_uses_def] >>
qexists_tac `(0, Phi_ip from_l)` >> fs [header_uses_def] >>
conj_tac
>- (
simp [IN_DEF] >>
rw [lpc_uses_cases, lpc_get_instr_cases, PULL_EXISTS] >>
rw [MEM_MAP] >> metis_tac [])
>- (
gen_tac >> simp [linear_pc_less_def, LEX_DEF] >>
pairarg_tac >> simp [bip_less_def])) >>
pairarg_tac >> Cases_on `r gen` >> fs []
>- (
`r fst (instrs_live b.body)` by metis_tac [FST] >>
drule instrs_live_uses >> rw [] >>
qexists_tac `(0, Offset i)` >>
conj_tac
>- (
simp [IN_DEF] >>
rw [lpc_uses_cases, lpc_get_instr_cases, PULL_EXISTS] >>
rw [MEM_MAP] >> metis_tac [])
>- (
gen_tac >> strip_tac >>
PairCases_on `lip2` >> fs [linear_pc_less_def, LEX_DEF_THM] >>
Cases_on `lip21` >> fs [bip_less_def]
>- (
Cases_on `b.h` >> fs [header_assigns_def, header_uses_def] >>
simp [IN_DEF] >>
rw [lpc_uses_cases, lpc_assigns_cases, lpc_get_instr_cases, PULL_EXISTS] >>
fs [MEM_MAP] >>
metis_tac [FST])
>- (
first_x_assum drule >>
simp [IN_DEF] >>
rw [lpc_uses_cases, lpc_assigns_cases, lpc_get_instr_cases, PULL_EXISTS] >>
rw [IN_DEF])))
>- (
first_x_assum drule >> rw [] >>
PairCases_on `lip` >>
qexists_tac `lip0+1,lip1` >> simp [IN_DEF] >>
conj_tac
>- fs [lpc_uses_cons, IN_DEF] >>
gen_tac >> disch_tac >>
PairCases_on `lip2` >>
Cases_on `lip20` >> fs [ADD1]
>- (
Cases_on `lip21`
>- (
rename1 `Phi_ip from_l` >>
`r bigunion {lpc_uses (b::bs) (0,Phi_ip from_l) | from_l | T}
r image fst (lpc_assigns (b::bs) (0,Phi_ip from_l))`
by metis_tac [lpc_assigns_0_head, lpc_uses_0_head] >>
fs [IN_DEF] >> metis_tac [])
>- (
`r image fst (lpc_assigns (b::bs) (0,Offset n))
r lpc_uses (b::bs) (0,Offset n)`
by metis_tac [IN_UNION, lpc_assigns_0_body, lpc_uses_0_body, FST, SND, SUBSET_DEF] >>
fs [IN_DEF]))
>- (
`linear_pc_less (n, lip21) (lip0, lip1)` by fs [linear_pc_less_def, LEX_DEF] >>
first_x_assum drule >>
rw [lpc_uses_cons, lpc_assigns_cons] >> fs [IN_DEF]))
QED
Theorem dominator_ordered_linear_live:
∀p f d.
dominator_ordered p
alookup p (Fn f) = Some d
linear_live (map snd d.blocks) = {}
Proof
rw [dominator_ordered_def] >> first_x_assum drule >> rw [EXTENSION] >>
CCONTR_TAC >> fs [] >> drule linear_live_uses >> rw [] >>
metis_tac []
QED
export_theory ();