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.

965 lines
35 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

(*
* 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.
*)
(* Properties of the mini-LLVM model *)
open HolKernel boolLib bossLib Parse;
open pairTheory listTheory rich_listTheory arithmeticTheory wordsTheory;
open pred_setTheory finite_mapTheory relationTheory llistTheory pathTheory;
open logrootTheory numposrepTheory;
open settingsTheory miscTheory memory_modelTheory llvmTheory;
new_theory "llvm_prop";
numLib.prefer_num();
(* ----- Theorems about converting between values and byte lists ----- *)
Theorem value_type_is_fc:
∀t v. value_type t v first_class_type t
Proof
ho_match_mp_tac value_type_ind >> rw [first_class_type_def] >>
fs [LIST_REL_EL_EQN, EVERY_EL]
QED
Theorem sizeof_type_to_shape:
∀t. first_class_type t sizeof (type_to_shape t) = sizeof t
Proof
recInduct type_to_shape_ind >>
rw [type_to_shape_def, memory_modelTheory.sizeof_def, llvmTheory.sizeof_def,
first_class_type_def, EVERY_MEM] >>
qid_spec_tac `vs` >> Induct_on `ts` >> rw [] >> fs []
QED
Theorem value_type_to_shape:
∀t v.
value_type t v
∀s.
value_shape (\n t x. n = fst (unconvert_value x) value_type t (FlatV x)) (type_to_shape t) v
Proof
ho_match_mp_tac value_type_ind >>
rw [memory_modelTheory.sizeof_def, llvmTheory.sizeof_def, type_to_shape_def,
unconvert_value_def, value_shape_def] >>
fs [value_shapes_list_rel, LIST_REL_CONJ, ETA_THM, EVERY2_MAP] >>
metis_tac [value_type_rules]
QED
Theorem llvm_v2b_size:
∀t v. value_type t v length (llvm_value_to_bytes v) = sizeof t
Proof
rw [llvm_value_to_bytes_def] >>
drule value_type_to_shape >> rw [] >>
drule value_type_is_fc >> rw [] >>
drule sizeof_type_to_shape >>
disch_then (mp_tac o GSYM) >> rw [] >>
irule v2b_size >> rw [] >>
qmatch_assum_abbrev_tac `value_shape f _ _` >>
qexists_tac `f` >> rw [] >>
unabbrev_all_tac >> fs []
QED
Theorem b2llvm_v_size:
∀t bs. first_class_type t sizeof t length bs
∃v. bytes_to_llvm_value t bs = (v, drop (sizeof t) bs)
Proof
rw [bytes_to_llvm_value_def] >>
drule sizeof_type_to_shape >> disch_then (mp_tac o GSYM) >> rw [] >>
fs [PAIR_MAP] >>
metis_tac [SND, b2v_size]
QED
Theorem b2llvm_v_smaller:
∀t bs. first_class_type t sizeof t length bs
length (snd (bytes_to_llvm_value t bs)) = length bs - sizeof t
Proof
rw [bytes_to_llvm_value_def] >>
metis_tac [b2v_smaller, sizeof_type_to_shape]
QED
Theorem b2llvm_v_append:
∀t bs bs'. first_class_type t sizeof t length bs
bytes_to_llvm_value t (bs ++ bs') = (I ## (λx. x ++ bs')) (bytes_to_llvm_value t bs)
Proof
rw [bytes_to_llvm_value_def] >>
drule sizeof_type_to_shape >> disch_then (mp_tac o GSYM) >> rw [] >> fs [] >>
rw [PAIR_MAP, b2v_append]
QED
Theorem b2v_llvm_v2b:
∀v t bs. value_type t v bytes_to_llvm_value t (llvm_value_to_bytes v ++ bs) = (v, bs)
Proof
rw [bytes_to_llvm_value_def, llvm_value_to_bytes_def] >>
drule value_type_to_shape >> rw [] >>
qmatch_assum_abbrev_tac `value_shape f _ _` >>
irule b2v_v2b >>
qexists_tac `f` >> rw [] >>
unabbrev_all_tac >> fs [] >>
fs [unconvert_value_def, convert_value_def, value_type_cases, pointer_size_def] >>
wordsLib.WORD_DECIDE_TAC
QED
(* ----- Theorems about insert/extract value and get_offset ----- *)
Theorem can_extract:
∀v indices t.
indices_ok t indices value_type t v extract_value v indices None
Proof
recInduct extract_value_ind >> rw [extract_value_def]
>- (
pop_assum mp_tac >> rw [value_type_cases] >> fs [indices_ok_def] >>
metis_tac [LIST_REL_LENGTH])
>- (
pop_assum mp_tac >> rw [value_type_cases] >> fs [indices_ok_def] >>
metis_tac [EVERY_EL, LIST_REL_EL_EQN]) >>
Cases_on `t` >> fs [indices_ok_def] >> simp [value_type_cases]
QED
Theorem can_insert:
∀v v2 indices t.
indices_ok t indices value_type t v insert_value v v2 indices None
Proof
recInduct insert_value_ind >> rw [insert_value_def]
>- (
pop_assum mp_tac >> rw [value_type_cases] >> fs [indices_ok_def] >>
metis_tac [LIST_REL_LENGTH])
>- (
pop_assum mp_tac >> rw [value_type_cases] >> fs [indices_ok_def] >>
CASE_TAC >> fs [] >> rfs [] >>
metis_tac [EVERY_EL, LIST_REL_EL_EQN]) >>
Cases_on `t` >> fs [indices_ok_def] >> simp [value_type_cases]
QED
Theorem extract_insertvalue:
∀v1 v2 indices v3.
insert_value v1 v2 indices = Some v3
extract_value v3 indices = Some v2
Proof
recInduct insert_value_ind >> rw [insert_value_def, extract_value_def] >>
pop_assum mp_tac >> CASE_TAC >> fs [] >> rfs [] >>
rw [] >> simp [extract_value_def, EL_LUPDATE]
QED
Theorem extract_insertvalue_other:
∀v1 v2 indices1 indices2 v3.
insert_value v1 v2 indices1 = Some v3
¬(indices1 indices2) ¬(indices2 indices1)
extract_value v3 indices2 = extract_value v1 indices2
Proof
recInduct insert_value_ind >> rw [insert_value_def, extract_value_def] >>
qpat_x_assum `_ = SOME _` mp_tac >> CASE_TAC >> rw [] >> rfs [] >>
qpat_x_assum `¬case _ of [] => F | h::t => P h t` mp_tac >>
CASE_TAC >> fs [] >> rename1 `idx::is` >>
fs [extract_value_def] >> rw [EL_LUPDATE]
QED
Theorem insert_extractvalue:
∀v1 indices v2.
extract_value v1 indices = Some v2
insert_value v1 v2 indices = Some v1
Proof
recInduct extract_value_ind >> rw [insert_value_def, extract_value_def] >> fs [] >>
rw [LUPDATE_SAME]
QED
Definition indices_in_range_def:
(indices_in_range t [] T)
(indices_in_range (ArrT n t) (i::is)
i < n indices_in_range t is)
(indices_in_range (StrT ts) (i::is)
i < length ts indices_in_range (el i ts) is)
(indices_in_range _ _ F)
End
(* The strict inequality does not hold because of 0 length arrays *)
Theorem offset_size_leq:
∀t indices n.
indices_in_range t indices get_offset t indices = Some n
n sizeof t
Proof
recInduct get_offset_ind >> rw [get_offset_def, llvmTheory.sizeof_def, indices_in_range_def] >>
BasicProvers.EVERY_CASE_TAC >> fs [] >> rw [] >> rfs []
>- (
`x + i * sizeof t (i + 1) * sizeof t` by decide_tac >>
`i + 1 v1` by decide_tac >>
metis_tac [LESS_MONO_MULT, LESS_EQ_TRANS]) >>
rw [MAP_TAKE, ETA_THM] >>
`take (Suc i) (map sizeof ts) = take i (map sizeof ts) ++ [sizeof (el i ts)]`
by rw [GSYM SNOC_EL_TAKE, EL_MAP] >>
`take (Suc i) (map sizeof ts) (map sizeof ts)` by rw [take_is_prefix] >>
drule sum_prefix >> rw [SUM_APPEND]
QED
Theorem extract_type_fc:
∀t is t'. extract_type t is = Some t' first_class_type t first_class_type t'
Proof
recInduct extract_type_ind >> rw [extract_type_def, first_class_type_def] >>
rw [] >> fs [] >> fs [EVERY_EL]
QED
Theorem extract_offset_size:
∀t indices n t'.
extract_type t indices = Some t'
get_offset t indices = Some n
sizeof t' sizeof t - n
Proof
recInduct get_offset_ind >> rw [get_offset_def, extract_type_def] >>
BasicProvers.EVERY_CASE_TAC >> fs [llvmTheory.sizeof_def] >> rfs [] >> rw [ETA_THM]
>- (
`sizeof t (v1 i) * sizeof t` suffices_by decide_tac >>
`1 v1 - i` by decide_tac >>
rw []) >>
rw [MAP_TAKE] >>
`sizeof (el i ts) sum (map sizeof ts) (sum (take i (map sizeof ts)))`
suffices_by decide_tac >>
qpat_x_assum `_ < _` mp_tac >> rpt (pop_assum kall_tac) >> qid_spec_tac `i` >>
Induct_on `ts` >> rw [TAKE_def, EL_CONS, PRE_SUB1]
QED
Theorem llvm_value_to_bytes_agg:
∀vs. llvm_value_to_bytes (AggV vs) = flat (map llvm_value_to_bytes vs)
Proof
Induct >> rw [] >> fs [llvm_value_to_bytes_def, value_to_bytes_def]
QED
Theorem read_from_offset_extract:
∀t indices n v t'.
indices_in_range t indices
get_offset t indices = Some n
value_type t v
extract_type t indices = Some t'
extract_value v indices = Some (fst (bytes_to_llvm_value t' (drop n (llvm_value_to_bytes v))))
Proof
recInduct get_offset_ind >>
rw [extract_value_def, get_offset_def, extract_type_def, indices_in_range_def] >>
simp [DROP_0]
>- metis_tac [APPEND_NIL, FST, b2v_llvm_v2b] >>
qpat_x_assum `value_type _ _` mp_tac >>
simp [Once value_type_cases] >> rw [] >> simp [extract_value_def] >>
qpat_x_assum `_ = Some n` mp_tac >> CASE_TAC >> rw [] >> rfs [] >>
simp [llvm_value_to_bytes_agg]
>- (
`value_type t (el i vs)` by metis_tac [EVERY_EL] >>
first_x_assum drule >>
rw [] >> simp [GSYM DROP_DROP_T, ETA_THM] >>
`i * sizeof t = length (flat (take i (map llvm_value_to_bytes vs)))`
by (
simp [LENGTH_FLAT, MAP_TAKE, MAP_MAP_o, combinTheory.o_DEF] >>
`map (λx. length (llvm_value_to_bytes x)) vs = replicate (length vs) (sizeof t)`
by (
qpat_x_assum `every _ _` mp_tac >> rpt (pop_assum kall_tac) >>
Induct_on `vs` >> rw [llvm_v2b_size]) >>
rw [take_replicate, MIN_DEF]) >>
rw [GSYM flat_drop, GSYM MAP_DROP] >>
drule DROP_CONS_EL >> simp [DROP_APPEND] >> disch_then kall_tac >>
`first_class_type t'` by metis_tac [value_type_is_fc, extract_type_fc] >>
`sizeof t' length (drop x (llvm_value_to_bytes (el i vs)))`
by (simp [LENGTH_DROP] >> drule llvm_v2b_size >> rw [] >> metis_tac [extract_offset_size]) >>
simp [b2llvm_v_append])
>- metis_tac [LIST_REL_LENGTH]
>- (
`value_type (el i ts) (el i vs)` by metis_tac [LIST_REL_EL_EQN] >>
first_x_assum drule >>
rw [] >> simp [GSYM DROP_DROP_T, ETA_THM] >>
`sum (map sizeof (take i ts)) = length (flat (take i (map llvm_value_to_bytes vs)))`
by (
simp [LENGTH_FLAT, MAP_TAKE, MAP_MAP_o, combinTheory.o_DEF] >>
`map sizeof ts = map (\x. length (llvm_value_to_bytes x)) vs`
by (
qpat_x_assum `list_rel _ _ _` mp_tac >> rpt (pop_assum kall_tac) >>
qid_spec_tac `ts` >>
Induct_on `vs` >> rw [] >> rw [llvm_v2b_size]) >>
rw []) >>
rw [GSYM flat_drop, GSYM MAP_DROP] >>
`i < length vs` by metis_tac [LIST_REL_LENGTH] >>
drule DROP_CONS_EL >> simp [DROP_APPEND] >> disch_then kall_tac >>
`first_class_type t'` by metis_tac [value_type_is_fc, extract_type_fc] >>
`sizeof t' length (drop x (llvm_value_to_bytes (el i vs)))`
by (simp [LENGTH_DROP] >> drule llvm_v2b_size >> rw [] >> metis_tac [extract_offset_size]) >>
simp [b2llvm_v_append])
QED
(* ----- Theorems about the step function ----- *)
Theorem get_instr_func:
∀p ip i1 i2. get_instr p ip i1 get_instr p ip i2 i1 = i2
Proof
rw [get_instr_cases] >> fs [] >> rw [] >> fs [] >> rw [] >> fs []
QED
Theorem inc_pc_invariant:
∀p s i. prog_ok p get_instr p s.ip (Inl i) ¬terminator i state_invariant p s state_invariant p (inc_pc s)
Proof
rw [state_invariant_def, inc_pc_def, allocations_ok_def, globals_ok_def,
stack_ok_def, frame_ok_def, heap_ok_def, EVERY_EL, ip_ok_def, inc_bip_def,
METIS_PROVE [] ``x y ~x y``]
>- (
qexists_tac `dec` >> qexists_tac `block'` >> rw [] >>
fs [prog_ok_def, get_instr_cases] >> res_tac >> rw [] >>
Cases_on `s.ip.i` >> fs [] >> rw [] >> fs [inc_bip_def] >>
`idx length block'.body - 1` suffices_by decide_tac >>
CCONTR_TAC >> fs [] >> rfs [LAST_EL, PRE_SUB1]) >>
metis_tac []
QED
Theorem get_instr_update:
∀p s i r v. get_instr p (update_result r v s).ip i <=> get_instr p s.ip i
Proof
rw [get_instr_cases, update_result_def]
QED
Theorem update_invariant:
∀r v s. state_invariant p (update_result r v s) state_invariant p s
Proof
rw [update_result_def, state_invariant_def, ip_ok_def, allocations_ok_def,
globals_ok_def, stack_ok_def, heap_ok_def, EVERY_EL, frame_ok_def]
QED
Theorem allocate_invariant:
∀p s1 v1 t v2 h2.
state_invariant p s1 allocate s1.heap v1 t (v2,h2) state_invariant p (s1 with heap := h2)
Proof
rw [state_invariant_def, ip_ok_def, globals_ok_def, stack_ok_def,
METIS_PROVE [] ``x y ~x y``]
>- metis_tac [allocate_heap_ok]
>- (fs [is_allocated_def] >> metis_tac [allocate_unchanged, SUBSET_DEF])
>- (
fs [EVERY_EL, frame_ok_def, allocate_unchanged] >> rw [] >>
metis_tac [allocate_unchanged, SUBSET_DEF])
QED
Theorem set_bytes_invariant:
∀s poison bytes n prog b.
state_invariant prog s is_allocated (Interval b n (n + length bytes)) s.heap
state_invariant prog (s with heap := set_bytes poison bytes n s.heap)
Proof
rw [state_invariant_def]
>- metis_tac [set_bytes_heap_ok]
>- (fs [globals_ok_def, is_allocated_def, set_bytes_unchanged] >> metis_tac [])
>- (fs [stack_ok_def, EVERY_EL, frame_ok_def, set_bytes_unchanged])
QED
Triviality not_none_eq:
!x. x None ?y. x = Some y
Proof
Cases >> rw []
QED
Theorem step_instr_invariant:
∀p s1 i l s2.
step_instr p s1 i l s2 prog_ok p get_instr p s1.ip (Inl i) state_invariant p s1
state_invariant p s2
Proof
ho_match_mp_tac step_instr_ind >> rw []
>- ( (* Ret *)
rw [update_invariant] >> fs [state_invariant_def] >> rw []
>- (
fs [stack_ok_def] >> rfs [EVERY_EL, frame_ok_def] >>
first_x_assum (qspec_then `0` mp_tac) >> simp [])
>- (
fs [heap_ok_def, deallocate_def, allocations_ok_def] >> rw []
>- metis_tac []
>- metis_tac [] >>
fs [deallocate_def, heap_ok_def] >> rw [flookup_fdiff] >>
eq_tac >> rw []
>- metis_tac [optionTheory.NOT_IS_SOME_EQ_NONE]
>- metis_tac [optionTheory.NOT_IS_SOME_EQ_NONE] >>
fs [allocations_ok_def, stack_ok_def, EXTENSION] >> metis_tac [])
>- (
fs [globals_ok_def, deallocate_def] >> rw [] >>
first_x_assum drule >> rw [] >> fs [is_allocated_def] >>
qexists_tac `b2` >> rw [] >> CCONTR_TAC >> fs [interval_freeable_def])
>- (
fs [stack_ok_def, EVERY_MEM, frame_ok_def, deallocate_def] >> rfs [] >>
rw []
>- (
res_tac >> rw [] >> qexists_tac `stop` >> rw [] >>
fs [ALL_DISTINCT_APPEND, MEM_FLAT, MEM_MAP] >>
metis_tac [])
>- (
fs [ALL_DISTINCT_APPEND])))
>- ( (* Br *)
fs [state_invariant_def] >> rw []
>- (
rw [ip_ok_def] >> fs [prog_ok_def] >>
qpat_x_assum `alookup _ (Fn "main") = _` kall_tac >>
fs [get_instr_cases] >>
last_x_assum drule >> disch_then drule >> fs [] >> rw [] >>
`terminator (el idx b.body)` by metis_tac [terminator_def] >>
`last b.body = el idx b.body`
by (
Cases_on `idx = PRE (length b.body)` >> fs [EL_PRE_LENGTH] >>
`Suc idx < length b.body` by decide_tac >>
drule mem_el_front >> rw [] >> fs [EVERY_MEM] >>
metis_tac []) >>
qpat_x_assum `Br _ _ _ = _` (assume_tac o GSYM) >> fs [] >>
fs [instr_to_labs_def, not_none_eq] >>
metis_tac [])
>- (fs [globals_ok_def] >> metis_tac [])
>- (fs [stack_ok_def, frame_ok_def, EVERY_MEM] >> metis_tac []))
>- ( (* Br *)
fs [state_invariant_def] >> rw []
>- (
rw [ip_ok_def] >> fs [prog_ok_def] >>
qpat_x_assum `alookup _ (Fn "main") = _` kall_tac >>
fs [get_instr_cases] >>
last_x_assum drule >> disch_then drule >> fs [] >> rw [] >>
`terminator (el idx b.body)` by metis_tac [terminator_def] >>
`last b.body = el idx b.body`
by (
Cases_on `idx = PRE (length b.body)` >> fs [EL_PRE_LENGTH] >>
`Suc idx < length b.body` by decide_tac >>
drule mem_el_front >> rw [] >> fs [EVERY_MEM] >>
metis_tac []) >>
qpat_x_assum `Br _ _ _ = _` (assume_tac o GSYM) >> fs [] >>
fs [instr_to_labs_def, not_none_eq] >>
metis_tac [])
>- (fs [globals_ok_def] >> metis_tac [])
>- (fs [stack_ok_def, frame_ok_def, EVERY_MEM] >> metis_tac []))
>- (
fs [state_invariant_def, globals_ok_def, stack_ok_def, frame_ok_def,
EVERY_MEM] >>
metis_tac [])
>- (
irule inc_pc_invariant >> rw [get_instr_update, update_invariant]>>
metis_tac [terminator_def])
>- (
irule inc_pc_invariant >> rw [get_instr_update, update_invariant] >>
metis_tac [terminator_def])
>- (
irule inc_pc_invariant >> rw [get_instr_update, update_invariant] >>
metis_tac [terminator_def])
>- ( (* Allocation *)
irule inc_pc_invariant >> rw [get_instr_update, update_invariant]
>- metis_tac [allocate_invariant]
>- (fs [get_instr_cases, allocate_cases] >> metis_tac [terminator_def]))
>- (
irule inc_pc_invariant >> rw [get_instr_update, update_invariant] >>
fs [get_instr_cases] >>
metis_tac [terminator_def])
>- ( (* Store *)
irule inc_pc_invariant >> rw [get_instr_update, update_invariant]
>- (irule set_bytes_invariant >> rw [] >> metis_tac [])
>- (fs [get_instr_cases] >> metis_tac [terminator_def]))
>- (
irule inc_pc_invariant >> rw [get_instr_update, update_invariant] >>
metis_tac [terminator_def])
>- (
irule inc_pc_invariant >> rw [get_instr_update, update_invariant] >>
metis_tac [terminator_def])
>- (
irule inc_pc_invariant >> rw [get_instr_update, update_invariant] >>
metis_tac [terminator_def])
>- (
irule inc_pc_invariant >> rw [get_instr_update, update_invariant] >>
metis_tac [terminator_def])
>- ( (* Call *)
rw [state_invariant_def]
>- (fs [prog_ok_def, ip_ok_def] >> metis_tac [NOT_NIL_EQ_LENGTH_NOT_0])
>- (fs [state_invariant_def, heap_ok_def] >> metis_tac [])
>- (fs [state_invariant_def, globals_ok_def] >> metis_tac [])
>- (
fs [state_invariant_def, stack_ok_def] >> rw []
>- (
rw [frame_ok_def] >> fs [ip_ok_def, prog_ok_def, inc_bip_def] >>
last_x_assum drule >> disch_then drule >> rw [] >>
CCONTR_TAC >> fs [] >> rfs [LAST_EL] >>
Cases_on `length block'.body = idx + 1` >> fs [PRE_SUB1] >>
fs [get_instr_cases] >>
metis_tac [terminator_def])
>- (fs [EVERY_MEM, frame_ok_def] >> metis_tac [])))
QED
Theorem step_invariant:
∀p s1 l s2.
prog_ok p step p s1 l s2 state_invariant p s1
state_invariant p s2
Proof
rw [step_cases]
>- metis_tac [step_instr_invariant] >>
fs [get_instr_cases, inc_pc_def, inc_bip_def, state_invariant_def] >>
rw []
>- (
fs [ip_ok_def, prog_ok_def] >>
metis_tac [NOT_NIL_EQ_LENGTH_NOT_0])
>- (fs [globals_ok_def] >> metis_tac [])
>- (fs [stack_ok_def, frame_ok_def, EVERY_MEM] >> metis_tac [])
QED
Definition is_call_def:
(is_call (Call _ _ _ _) T)
(is_call _ F)
End
Theorem step_same_block:
∀p s1 l s2.
get_instr p s1.ip i step p s1 l s2
(∀i'. i = Inl i' ¬terminator i' ¬is_call i')
s1.ip.f = s2.ip.f
s1.ip.b = s2.ip.b
s2.ip.i = inc_bip s1.ip.i
Proof
simp [step_cases] >>
rpt gen_tac >> disch_tac >> fs [inc_pc_def] >>
`i = Inl i'` by metis_tac [get_instr_func] >>
fs [step_instr_cases] >> rfs [] >>
fs [terminator_def, is_call_def, inc_pc_def, update_result_def]
QED
(* ----- Initial state is ok ----- *)
Theorem init_invariant:
∀p s init. prog_ok p is_init_state s init state_invariant p s
Proof
rw [is_init_state_def, state_invariant_def]
>- (rw [ip_ok_def] >> fs [prog_ok_def] >> metis_tac [NOT_NIL_EQ_LENGTH_NOT_0])
>- rw [stack_ok_def]
QED
(* ----- A bigger-step semantics ----- *)
Inductive last_step:
(∀p s1 l s2 i.
step p s1 l s2 get_instr p s1.ip i
((∃x. i = Inr x) (∃i'. i = Inl i' (terminator i' is_call i')))
last_step p s1 l s2)
(∀p s1.
(¬∃l s2. step p s1 l s2)
last_step p s1 Error (s1 with status := Stuck))
End
(* Run all of the instructions up-to-and-including the next Call or terminator.
* Stop after the phis too.
* *)
Inductive multi_step:
(∀p s1 s2 l.
last_step p s1 l s2
s1.status = Partial
multi_step p s1 [l] s2)
(∀p s1 s2 s3 i l ls.
step p s1 l s2
s1.status = Partial
get_instr p s1.ip (Inl i)
¬(terminator i is_call i)
multi_step p s2 ls s3
multi_step p s1 (l::ls) s3)
End
Definition multi_step_sem_def:
multi_step_sem p s1 =
{ l1 | ∃path l2. l1 observation_prefixes ((last path).status, flat l2)
toList (labels path) = Some l2
finite path okpath (multi_step p) path first path = s1 }
End
Theorem multi_step_to_step_path:
∀p s1 l s2.
multi_step p s1 l s2
∃path.
finite path okpath (sem_step p) path first path = s1 last path = s2
toList (labels path) = Some l
Proof
ho_match_mp_tac multi_step_ind >> conj_tac
>- (rw [] >> qexists_tac `pcons s1 l (stopped_at s2)` >> fs [sem_step_cases, toList_THM, last_step_cases]) >>
rw [] >>
qexists_tac `pcons s1 l path` >> rw [toList_THM] >>
`LFINITE (labels path)` by metis_tac [finite_labels] >>
simp [sem_step_cases]
QED
Theorem expand_multi_step_path:
∀path. okpath (multi_step prog) path finite path
!l. toList (labels path) = Some l
∃path'.
toList (labels path') = Some (flat l) finite path'
okpath (sem_step prog) path' first path' = first path last path' = last path
Proof
ho_match_mp_tac finite_okpath_ind >> rw []
>- (qexists_tac `stopped_at x` >> fs [toList_THM] >> rw []) >>
fs [toList_THM] >> rw [] >>
first_x_assum drule >> rw [] >>
drule multi_step_to_step_path >> rw [] >>
qexists_tac `plink path'' path'` >> rw [] >>
simp [toList_THM, labels_plink] >>
`LFINITE (LAPPEND (labels path'') (labels path'))` by metis_tac [LFINITE_APPEND, finite_labels] >>
drule LFINITE_toList >> rw [] >> drule toList_LAPPEND_APPEND >> rw []
QED
Theorem contract_step_path:
∀path. okpath (sem_step prog) path finite path
∀l1 l s.
last_step prog (last path) l s
(last path).status = Partial
toList (labels path) = Some l1
∃path' l2.
toList (labels path') = Some l2
flat l2 = l1 ++ [l]
finite path'
okpath (multi_step prog) path' first path' = first path last path' = s
Proof
ho_match_mp_tac finite_okpath_ind >> rw []
>- (
qexists_tac `pcons x [l] (stopped_at s)` >> fs [] >> simp [toList_THM] >>
simp [Once multi_step_cases] >>
fs [toList_THM]) >>
fs [toList_THM] >>
first_x_assum drule >> disch_then drule >> rw [] >>
Cases_on `last_step prog x r (first path)`
>- (
qexists_tac `pcons x [r] path'` >> simp [] >>
fs [sem_step_cases] >>
simp [Once multi_step_cases, toList_THM] >>
simp [last_step_cases])
>- (
qpat_x_assum `okpath (multi_step _) _` mp_tac >>
simp [Once okpath_cases] >> rw [] >> fs [toList_THM] >> rw [] >> fs [] >>
qexists_tac `pcons x (r::r') p` >> fs [toList_THM] >> rw [Once multi_step_cases] >>
disj2_tac >> qexists_tac `first path` >> rw [] >> fs [sem_step_cases]
>- (fs [last_step_cases, step_cases, get_instr_cases] >> metis_tac []) >>
qpat_x_assum `okpath (sem_step _) _` mp_tac >>
simp [Once okpath_cases, sem_step_cases] >> CCONTR_TAC >> fs [] >> rw [] >>
fs [first_def, last_thm] >> rw [] >> fs [])
QED
Definition get_next_step_def:
get_next_step p s1 =
some (s2, l). sem_step p s1 l s2 ¬last_step p s1 l s2
End
Triviality finite_plink_trivial:
∀path. finite path path = plink path (stopped_at (last path))
Proof
ho_match_mp_tac finite_path_ind >> rw []
QED
Definition instrs_left_def:
instrs_left prog s =
case alookup prog s.ip.f of
| None => 0
| Some d =>
case alookup d.blocks s.ip.b of
| None => 0
| Some b =>
case s.ip.i of
| Phi_ip _ => length b.body + 1
| Offset idx => length b.body - idx
End
Theorem sem_step_stuck:
∀p s1. (∀l s2. ¬sem_step p s1 l s2) s1.status Partial
Proof
rw [sem_step_cases] >> metis_tac []
QED
Theorem sem_step_then_stuck:
∀p s1 l1 s2.
sem_step p s1 l1 s2 (∀l2 s3. ¬sem_step p s2 l2 s3)
(l1 = Error s2 = s1 with status := Stuck ∀l2 s3. ¬step p s1 l2 s3)
(∃i e. l1 = Exit i s2 = s1 with status := Complete i
get_instr p s1.ip (Inl (Exit e)))
Proof
rw [sem_step_stuck] >>
fs [sem_step_cases] >>
disj2_tac >> fs [step_cases] >> rfs [inc_pc_def] >>
fs [step_instr_cases] >> rfs [update_result_def, inc_pc_def] >>
metis_tac []
QED
Theorem sem_step_not_last:
∀p s1 l1 s2.
sem_step p s1 l1 s2 ¬last_step p s1 l1 s2
∃l2 s3. sem_step p s2 l2 s3
Proof
rw [] >> CCONTR_TAC >> fs [] >> drule sem_step_then_stuck >>
simp [] >>
CCONTR_TAC >> fs [] >> rw []
>- fs [last_step_cases] >>
fs [last_step_cases, sem_step_cases] >> rw [] >>
first_x_assum (qspec_then `Inl (Exit e)` mp_tac) >>
rw [terminator_def]
QED
Triviality some_lemma:
∀P a b. (some (x, y). P x y) = Some (a, b) P a b
Proof
rw [optionTheory.some_def] >>
qmatch_assum_abbrev_tac `(@x. Q x) = _` >>
`Q (@x. Q x)` suffices_by (rw [Abbr `Q`]) >>
`?x. Q x` suffices_by rw [SELECT_THM] >>
unabbrev_all_tac >> rw [] >>
pairarg_tac >> fs [] >> rw [EXISTS_PROD] >>
metis_tac []
QED
Theorem extend_step_path:
∀path.
okpath (sem_step p) path finite path
(∀s. path = stopped_at s ∃s' l. sem_step p s l s')
∃path' l s n.
finite path' okpath (sem_step p) path' (last path').status = Partial
last_step p (last path') l s
length path = Some (Suc n) n PL (pconcat path' l (stopped_at s))
path = take n (pconcat path' l (stopped_at s))
Proof
rw [] >>
Cases_on `get_next_step p (last path) = None ∀s. path stopped_at s`
>- (
fs [get_next_step_def, optionTheory.some_def, FORALL_PROD, METIS_PROVE [] ``~x y (x y)``] >>
Cases_on `∃l2 s2. sem_step p (last path) l2 s2` >> fs []
>- ( (* Can take a last step from the end of the path *)
first_x_assum drule >> rw [] >>
qexists_tac `path` >> qexists_tac `l2` >> qexists_tac `s2` >> rw [] >>
fs [finite_length] >>
qexists_tac `n - 1` >>
`n 0` by metis_tac [length_never_zero] >>
rw [PL_def] >>
`length (pconcat path l2 (stopped_at s2)) = Some (n + 1)`
by metis_tac [length_pconcat, alt_length_thm] >>
rw [take_pconcat]
>- fs [sem_step_cases]
>- metis_tac [take_all] >>
fs [PL_def] >> rfs [])
>- ( (* The path is stuck, so we need to extract the last step from it *)
drule finite_path_end_cases >>
rw [] >> fs [] >> rfs [] >>
qexists_tac `p'` >> rw [] >>
qexists_tac `l` >> qexists_tac `s` >> rw [] >>
fs [finite_length] >>
qexists_tac `n` >> rw [] >>
`length (plink p' (pcons (last p') l (stopped_at s))) = Some (n + Suc 1 - 1)`
by metis_tac [length_plink, alt_length_thm, optionTheory.OPTION_MAP_DEF] >>
rw []
>- fs [sem_step_cases]
>- metis_tac [sem_step_not_last]
>- (
rw [PL_def] >> fs [finite_length] >>
`length (pconcat p' l (stopped_at s)) = Some (n + 1)`
by metis_tac [length_pconcat, alt_length_thm] >>
fs [])
>- (
rw [take_pconcat]
>- (fs [PL_def, finite_length] >> rfs []) >>
metis_tac [finite_length, pconcat_to_plink_finite]))) >>
qexists_tac `plink path (unfold I (get_next_step p) (last path))` >> rw [] >>
qmatch_goalsub_abbrev_tac `finite path1` >>
`∃m. length path = Some (Suc m)`
by (fs [finite_length] >> Cases_on `n` >> fs [length_never_zero]) >>
simp [GSYM PULL_EXISTS] >>
conj_asm1_tac
>- (
simp [Abbr `path1`] >> irule unfold_finite >>
WF_REL_TAC `measure (instrs_left p)` >>
rpt gen_tac >>
rw [instrs_left_def, get_next_step_def] >>
qabbrev_tac `P = (\s3 l. sem_step p s2 l s3 ¬last_step p s2 l s3)` >>
`P s3 l` by (irule some_lemma >> simp [Abbr `P`]) >>
pop_assum mp_tac >> simp [Abbr `P`] >> strip_tac >>
drule sem_step_not_last >> simp [] >> strip_tac >>
qpat_x_assum `sem_step p s2 l s3` mp_tac >> rw [Once sem_step_cases]
>- (
`?i. get_instr p s2.ip i` by metis_tac [get_instr_cases, step_cases] >>
`?x. i = Inl x` by (fs [last_step_cases] >> metis_tac [sumTheory.sum_CASES]) >>
drule step_same_block >> disch_then drule >> simp [] >>
impl_tac
>- (fs [last_step_cases] >> metis_tac []) >>
fs [step_cases, get_instr_cases, inc_bip_def] >> rw [] >> fs [] >>
rw [inc_bip_def] >> fs [])
>- fs [last_step_cases]) >>
`last path = first path1`
by (
unabbrev_all_tac >> simp [Once unfold_thm] >>
CASE_TAC >> rw [] >> split_pair_case_tac >> rw []) >>
simp [last_plink] >>
conj_asm1_tac
>- (
unabbrev_all_tac >>
irule okpath_unfold >> rw [] >>
qexists_tac `\x.T` >> rw [get_next_step_def] >>
qabbrev_tac `P = (\s2 l. sem_step p s l s2 ¬last_step p s l s2)` >>
`P s' l` by (irule some_lemma >> simp [Abbr `P`]) >>
pop_assum mp_tac >> simp [Abbr `P`]) >>
`?n. length path1 = Some n` by fs [finite_length] >>
`n 0` by metis_tac [length_never_zero] >>
`length (plink path path1) = Some (Suc m + n - 1)` by metis_tac [length_plink] >>
simp [take_pconcat, PL_def, finite_pconcat, length_plink] >>
`!l s. length (pconcat (plink path path1) l (stopped_at s)) = Some ((Suc m + n 1) + 1)`
by metis_tac [length_pconcat, alt_length_thm] >>
simp [GSYM PULL_EXISTS] >>
unabbrev_all_tac >> drule unfold_last >>
qmatch_goalsub_abbrev_tac `last_step _ (last path1) _ _` >>
simp [Once get_next_step_def, optionTheory.some_def, FORALL_PROD] >>
strip_tac >>
simp [CONJ_ASSOC, Once CONJ_SYM] >>
simp [GSYM CONJ_ASSOC] >>
conj_tac
>- (
rw [take_plink]
>- (imp_res_tac take_all >> fs []) >>
metis_tac [finite_plink_trivial]) >>
pop_assum mp_tac >>
Cases_on `1 PL path1` >> simp []
>- (
simp [get_next_step_def] >> strip_tac >>
qabbrev_tac `P = (\s2 l. sem_step p x l s2 ¬last_step p x l s2)` >>
`P (last path1) l` by (irule some_lemma >> simp [Abbr `P`]) >>
pop_assum mp_tac >> simp [Abbr `P`] >>
strip_tac >>
drule sem_step_not_last >> rw []
>- fs [sem_step_cases] >>
metis_tac [])
>- (
`n = 1` by (rfs [PL_def, finite_length] >> decide_tac) >>
qspec_then `path1` strip_assume_tac path_cases
>- (
unabbrev_all_tac >> simp [] >>
fs [] >> fs [Once unfold_thm] >>
Cases_on `get_next_step p (last path)` >> simp [] >> fs [] >> rw [] >>
fs [get_next_step_def, optionTheory.some_def, FORALL_PROD] >>
TRY split_pair_case_tac >> fs [sem_step_cases] >>
metis_tac [])
>- fs [alt_length_thm, length_never_zero])
QED
Theorem find_path_prefix:
∀path.
okpath (sem_step p) path finite path
!obs l1. toList (labels path) = Some l1
obs observation_prefixes ((last path).status, l1)
∃n l2. n PL path toList (labels (take n path)) = Some l2
obs = ((last (take n path)).status, filter ($ Tau) l2)
Proof
ho_match_mp_tac finite_okpath_ind >> rw [toList_THM]
>- fs [observation_prefixes_cases, IN_DEF] >>
`?s ls. obs = (s, ls)` by metis_tac [pairTheory.pair_CASES] >>
fs [] >>
`∃l. length path = Some l l 0` by metis_tac [finite_length, length_never_zero] >>
`take (l-1) path = path` by metis_tac [take_all] >>
Cases_on `s` >> fs []
>- (
qexists_tac `l` >> rw [toList_THM] >>
Cases_on `l` >> fs [toList_THM] >>
fs [observation_prefixes_cases, IN_DEF, PL_def])
>- (
qexists_tac `l` >> rw [toList_THM] >>
Cases_on `l` >> fs [toList_THM] >>
fs [observation_prefixes_cases, IN_DEF, PL_def]) >>
qpat_x_assum `(Partial, _) _` mp_tac >>
simp [observation_prefixes_cases, Once IN_DEF] >> rw [] >>
rename1 `short_l first_l::long_l` >>
Cases_on `short_l` >> fs []
>- (
qexists_tac `0` >> rw [toList_THM] >>
fs [sem_step_cases]) >>
rename1 `short_l long_l` >>
rfs [] >>
`(Partial, filter ($ Tau) short_l) observation_prefixes ((last path).status,long_l)`
by (simp [observation_prefixes_cases, IN_DEF] >> metis_tac []) >>
first_x_assum drule >> strip_tac >>
qexists_tac `Suc n` >> simp [toList_THM] >> rw [] >> rfs [last_take]
QED
Triviality is_prefix_lem:
∀l1 l2 l3. l1 l2 l1 l2 ++ l3
Proof
Induct >> rw [] >> fs [] >>
Cases_on `l2` >> fs []
QED
Theorem big_sem_equiv:
∀p s1. multi_step_sem p s1 = sem p s1
Proof
rw [multi_step_sem_def, sem_def, EXTENSION] >> eq_tac >> rw []
>- (
drule expand_multi_step_path >> rw [] >>
rename [`toList (labels m_path) = Some m_l`, `toList (labels s_path) = Some (flat m_l)`] >>
`?n short_l.
n PL s_path
toList (labels (take n s_path)) = Some short_l
x = ((last (take n s_path)).status, filter ($ Tau) short_l)`
by metis_tac [find_path_prefix] >>
qexists_tac `take n s_path` >> rw [])
>- (
Cases_on `¬∀s. path = stopped_at s ∃s' l. sem_step p s l s'`
>- (
fs [] >> rw [] >> fs [toList_THM] >> rw [] >>
qexists_tac `stopped_at s` >> rw [toList_THM] >>
rw [observation_prefixes_cases, IN_DEF] >>
metis_tac [trace_type_nchotomy]) >>
drule extend_step_path >> disch_then drule >>
impl_tac >> rw []
>- metis_tac [] >>
rename1 `last_step _ (last s_ext_path) last_l last_s` >>
`?s_ext_l. toList (labels s_ext_path) = Some s_ext_l` by metis_tac [LFINITE_toList, finite_labels] >>
qabbrev_tac `orig_path = take n (pconcat s_ext_path last_l (stopped_at last_s))` >>
drule contract_step_path >> simp [] >> disch_then drule >> rw [] >>
rename [`toList (labels m_path) = Some m_l`,
`toList (labels s_ext_path) = Some s_ext_l`,
`first m_path = first s_ext_path`,
`okpath (multi_step _) m_path`] >>
qexists_tac `m_path` >> rw [] >>
TRY (rw [Abbr `orig_path`] >> NO_TAC) >>
rfs [last_take, take_pconcat] >>
Cases_on `length s_ext_path = Some n`
>- (
rfs [PL_def] >> fs [] >>
rw [observation_prefixes_cases, IN_DEF] >> rw [] >>
unabbrev_all_tac >> rw [last_pconcat] >> fs [] >>
drule toList_LAPPEND_APPEND >> rw [toList_THM] >>
Cases_on `(last m_path).status` >> simp [] >>
qexists_tac `s_ext_l ++ [last_l]` >> rw []) >>
fs [PL_def, finite_pconcat] >> rfs [] >>
`?m. length s_ext_path = Some m` by metis_tac [finite_length] >>
`length s_ext_path = Some m` by metis_tac [finite_length] >>
`length (pconcat s_ext_path last_l (stopped_at (last m_path))) = Some (m + 1)`
by metis_tac [length_pconcat, alt_length_thm] >>
fs [] >>
`n < m` by decide_tac >> fs [] >> rw [] >>
`n PL s_ext_path` by rw [PL_def] >>
Cases_on `(last orig_path).status = Partial`
>- (
rw [observation_prefixes_cases, IN_DEF] >> rw [] >>
unabbrev_all_tac >> fs [] >>
`LTAKE n (labels s_ext_path) = Some l` by metis_tac [LTAKE_labels] >>
fs [toList_some] >> rfs [] >>
Cases_on `m` >> fs [length_labels] >>
qexists_tac `l` >> rw [] >> rfs []
>- (
irule is_prefix_lem >>
`n length s_ext_l` by decide_tac >>
fs [ltake_fromList2] >>
rw [take_is_prefix])
>- (drule LTAKE_LENGTH >> rw [])) >>
unabbrev_all_tac >> rfs [last_take] >>
fs [okpath_pointwise] >>
Cases_on `Suc n PL s_ext_path` >> rw []
>- (last_x_assum (qspec_then `n` mp_tac) >> rw [sem_step_cases]) >>
`n = m - 1` by (fs [PL_def] >> rfs []) >>
rw [] >>
`el (m - 1) s_ext_path = last s_ext_path` by metis_tac [take_all, pathTheory.last_take] >>
fs [last_step_cases])
QED
export_theory ();