(* * 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. *) (* Proofs about a shallowly embedded concept of live variables *) open HolKernel boolLib bossLib Parse; open pred_setTheory; open settingsTheory miscTheory llvmTheory llvm_propTheory; new_theory "llvm_live"; numLib.prefer_num (); Definition inc_pc_def: inc_pc ip = ip with i := ip.i + 1 End (* The set of program counters the given instruction and starting point can * immediately reach, within a function *) Definition next_ips_def: (next_ips (Ret _) ip = {}) ∧ (next_ips (Br _ l1 l2) ip = { <| f := ip.f; b := Some l; i := 0 |> | l | l ∈ {l1; l2} }) ∧ (next_ips (Invoke _ _ _ _ l1 l2) ip = { <| f := ip.f; b := Some l; i := 0 |> | l | l ∈ {l1; l2} }) ∧ (next_ips Unreachable ip = {}) ∧ (next_ips (Sub _ _ _ _ _ _) ip = { inc_pc ip }) ∧ (next_ips (Extractvalue _ _ _) ip = { inc_pc ip }) ∧ (next_ips (Insertvalue _ _ _ _) ip = { inc_pc ip }) ∧ (next_ips (Alloca _ _ _) ip = { inc_pc ip }) ∧ (next_ips (Load _ _ _) ip = { inc_pc ip }) ∧ (next_ips (Store _ _) ip = { inc_pc ip }) ∧ (next_ips (Gep _ _ _ _) ip = { inc_pc ip }) ∧ (next_ips (Ptrtoint _ _ _) ip = { inc_pc ip }) ∧ (next_ips (Inttoptr _ _ _) ip = { inc_pc ip }) ∧ (next_ips (Icmp _ _ _ _ _) ip = { inc_pc ip }) ∧ (next_ips (Call _ _ _ _) ip = { inc_pc ip }) ∧ (next_ips (Cxa_allocate_exn _ _) ip = { inc_pc ip }) ∧ (* TODO: revisit throw when dealing with exceptions *) (next_ips (Cxa_throw _ _ _) ip = { }) ∧ (next_ips (Cxa_begin_catch _ _) ip = { inc_pc ip }) ∧ (next_ips (Cxa_end_catch) ip = { inc_pc ip }) ∧ (next_ips (Cxa_get_exception_ptr _ _) 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 []) ∧ (∀ip i. get_instr prog ip i ⇒ good_path prog [ip]) ∧ (∀prog path ip1 i1 ip2. get_instr prog ip1 i1 ∧ ip2 ∈ next_ips i1 ip1 ∧ good_path prog (ip2::path) ⇒ good_path prog (ip1::ip2::path)) End Definition arg_to_regs_def: (arg_to_regs (Constant _) = {}) ∧ (arg_to_regs (Variable r) = {r}) End (* The registers that an instruction uses *) Definition uses_def: (uses (Ret (_, a)) = arg_to_regs a) ∧ (uses (Br a _ _) = arg_to_regs a) ∧ (uses (Invoke _ _ a targs _ _) = arg_to_regs a ∪ BIGUNION (set (map (arg_to_regs o snd) targs))) ∧ (uses Unreachable = {}) ∧ (uses (Sub _ _ _ _ a1 a2) = arg_to_regs a1 ∪ arg_to_regs a2) ∧ (uses (Extractvalue _ (_, a) _) = arg_to_regs a) ∧ (uses (Insertvalue _ (_, a1) (_, a2) _) = arg_to_regs a1 ∪ arg_to_regs a2) ∧ (uses (Alloca _ _ (_, a)) = arg_to_regs a) ∧ (uses (Load _ _ (_, a)) = arg_to_regs a) ∧ (uses (Store (_, a1) (_, a2)) = arg_to_regs a1 ∪ arg_to_regs a2) ∧ (uses (Gep _ _ (_, a) targs) = arg_to_regs a ∪ BIGUNION (set (map (arg_to_regs o snd) targs))) ∧ (uses (Ptrtoint _ (_, a) _) = arg_to_regs a) ∧ (uses (Inttoptr _ (_, a) _) = arg_to_regs a) ∧ (uses (Icmp _ _ _ a1 a2) = arg_to_regs a1 ∪ arg_to_regs a2) ∧ (uses (Call _ _ _ targs) = BIGUNION (set (map (arg_to_regs o snd) targs))) ∧ (uses (Cxa_allocate_exn _ a) = arg_to_regs a) ∧ (uses (Cxa_throw a1 a2 a3) = arg_to_regs a1 ∪ arg_to_regs a2 ∪ arg_to_regs a3) ∧ (uses (Cxa_begin_catch _ a) = arg_to_regs a) ∧ (uses (Cxa_end_catch) = { }) ∧ (uses (Cxa_get_exception_ptr _ a) = arg_to_regs a) End (* The registers that an instruction assigns *) Definition assigns_def: (assigns (Invoke r _ _ _ _ _) = {r}) ∧ (assigns (Sub r _ _ _ _ _) = {r}) ∧ (assigns (Extractvalue r _ _) = {r}) ∧ (assigns (Insertvalue r _ _ _) = {r}) ∧ (assigns (Alloca r _ _) = {r}) ∧ (assigns (Load r _ _) = {r}) ∧ (assigns (Gep r _ _ _) = {r}) ∧ (assigns (Ptrtoint r _ _) = {r}) ∧ (assigns (Inttoptr r _ _) = {r}) ∧ (assigns (Icmp r _ _ _ _) = {r}) ∧ (assigns (Call r _ _ _) = {r}) ∧ (assigns (Cxa_allocate_exn r _) = {r}) ∧ (assigns (Cxa_begin_catch r _) = {r}) ∧ (assigns (Cxa_get_exception_ptr r _) = {r}) ∧ (assigns _ = {}) End Definition live_def: live prog ip ⇔ { r | ∃path instr. good_path prog (ip::path) ∧ get_instr prog (last (ip::path)) instr ∧ r ∈ uses instr ∧ ∀ip2 instr2. ip2 ∈ set (front (ip::path)) ∧ get_instr prog ip2 instr2 ⇒ r ∉ assigns instr2 } End Theorem get_instr_live: ∀prog ip instr. get_instr prog ip instr ⇒ uses instr ⊆ 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 instr ip'. get_instr prog ip instr ⇒ live prog ip = BIGUNION {live prog ip' | ip' ∈ next_ips instr ip} DIFF assigns instr ∪ uses instr Proof rw [live_def, EXTENSION] >> eq_tac >> rw [] >- ( Cases_on `path` >> fs [] >- metis_tac [get_instr_func] >> rename1 `ip::ip2::path` >> qpat_x_assum `good_path _ _` mp_tac >> simp [Once good_path_cases] >> rw [] >> Cases_on `x ∈ uses instr` >> fs [] >> simp [set_rw, PULL_EXISTS] >> qexists_tac `ip2` >> qexists_tac `path` >> qexists_tac `instr'` >> rw [] >> metis_tac [get_instr_func]) >- ( fs [] >> qexists_tac `ip'::path` >> qexists_tac `instr'` >> rw [] >- (simp [Once good_path_cases] >> metis_tac []) >> metis_tac [get_instr_func]) >- ( qexists_tac `[]` >> qexists_tac `instr` >> rw [] >> simp [Once good_path_cases] >> metis_tac []) QED export_theory ();