|
|
(*
|
|
|
* 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;
|
|
|
open logrootTheory numposrepTheory;
|
|
|
open settingsTheory llvmTheory;
|
|
|
|
|
|
new_theory "llvm_prop";
|
|
|
|
|
|
numLib.prefer_num();
|
|
|
|
|
|
(* ----- Theorems about list library functions ----- *)
|
|
|
(* Could be upstreamed to HOL *)
|
|
|
|
|
|
Theorem dropWhile_map:
|
|
|
∀P f l. dropWhile P (map f l) = map f (dropWhile (P o f) l)
|
|
|
Proof
|
|
|
Induct_on `l` >> rw []
|
|
|
QED
|
|
|
|
|
|
Theorem dropWhile_prop:
|
|
|
∀P l x. x < length l - length (dropWhile P l) ⇒ P (el x l)
|
|
|
Proof
|
|
|
Induct_on `l` >> rw [] >>
|
|
|
Cases_on `x` >> fs []
|
|
|
QED
|
|
|
|
|
|
Theorem dropWhile_rev_take:
|
|
|
∀P n l x.
|
|
|
let len = length (dropWhile P (reverse (take n l))) in
|
|
|
x + len < n ∧ n ≤ length l ⇒ P (el (x + len) l)
|
|
|
Proof
|
|
|
rw [] >>
|
|
|
`P (el ((n - 1 - x - length (dropWhile P (reverse (take n l))))) (reverse (take n l)))`
|
|
|
by (irule dropWhile_prop >> simp [LENGTH_REVERSE]) >>
|
|
|
rfs [EL_REVERSE, EL_TAKE, PRE_SUB1]
|
|
|
QED
|
|
|
|
|
|
Theorem take_replicate:
|
|
|
∀m n x. take m (replicate n x) = replicate (min m n) x
|
|
|
Proof
|
|
|
Induct_on `n` >> rw [TAKE_def, MIN_DEF] >> fs [] >>
|
|
|
Cases_on `m` >> rw []
|
|
|
QED
|
|
|
|
|
|
Theorem length_take_less_eq:
|
|
|
∀n l. length (take n l) ≤ n
|
|
|
Proof
|
|
|
Induct_on `l` >> rw [TAKE_def] >>
|
|
|
Cases_on `n` >> fs []
|
|
|
QED
|
|
|
|
|
|
Theorem flat_drop:
|
|
|
∀n m ls. flat (drop m ls) = drop (length (flat (take m ls))) (flat ls)
|
|
|
Proof
|
|
|
Induct_on `ls` >> rw [DROP_def, DROP_APPEND] >>
|
|
|
irule (GSYM DROP_LENGTH_TOO_LONG) >> simp []
|
|
|
QED
|
|
|
|
|
|
Theorem take_is_prefix:
|
|
|
∀n l. take n l ≼ l
|
|
|
Proof
|
|
|
Induct_on `l` >> rw [TAKE_def]
|
|
|
QED
|
|
|
|
|
|
Theorem sum_prefix:
|
|
|
∀l1 l2. l1 ≼ l2 ⇒ sum l1 ≤ sum l2
|
|
|
Proof
|
|
|
Induct >> rw [] >> Cases_on `l2` >> fs []
|
|
|
QED
|
|
|
|
|
|
Theorem flookup_fdiff:
|
|
|
∀m s k.
|
|
|
flookup (fdiff m s) k =
|
|
|
if k ∈ s then None else flookup m k
|
|
|
Proof
|
|
|
rw [FDIFF_def, FLOOKUP_DRESTRICT] >> fs []
|
|
|
QED
|
|
|
|
|
|
(* ----- Theorems about log ----- *)
|
|
|
|
|
|
(* Could be upstreamed to HOL *)
|
|
|
Theorem mul_div_bound:
|
|
|
∀m n. n ≠ 0 ⇒ m - (n - 1) ≤ n * (m DIV n) ∧ n * (m DIV n) ≤ m
|
|
|
Proof
|
|
|
rw [] >>
|
|
|
`0 < n` by decide_tac >>
|
|
|
drule DIVISION >> disch_then (qspec_then `m` mp_tac) >>
|
|
|
decide_tac
|
|
|
QED
|
|
|
|
|
|
Theorem exp_log_bound:
|
|
|
∀b n. 1 < b ∧ n ≠ 0 ⇒ n DIV b + 1 ≤ b ** (log b n) ∧ b ** (log b n) ≤ n
|
|
|
Proof
|
|
|
rw [] >> `0 < n` by decide_tac >>
|
|
|
drule LOG >> disch_then drule >> rw [] >>
|
|
|
fs [ADD1, EXP_ADD] >>
|
|
|
simp [DECIDE ``∀x y. x + 1 ≤ y ⇔ x < y``] >>
|
|
|
`∃x. b = Suc x` by intLib.COOPER_TAC >>
|
|
|
`b * (n DIV b) < b * b ** log b n` suffices_by metis_tac [LESS_MULT_MONO] >>
|
|
|
pop_assum kall_tac >>
|
|
|
`b ≠ 0` by decide_tac >>
|
|
|
drule mul_div_bound >> disch_then (qspec_then `n` mp_tac) >>
|
|
|
decide_tac
|
|
|
QED
|
|
|
|
|
|
Theorem log_base_power:
|
|
|
∀n b. 1 < b ⇒ log b (b ** n) = n
|
|
|
Proof
|
|
|
Induct >> rw [EXP, LOG_1] >>
|
|
|
Cases_on `n` >> rw [LOG_BASE] >>
|
|
|
first_x_assum drule >> rw [] >>
|
|
|
simp [Once EXP, LOG_MULT]
|
|
|
QED
|
|
|
|
|
|
Theorem log_change_base_power:
|
|
|
∀m n b. 1 < b ∧ m ≠ 0 ∧ n ≠ 0 ⇒ log (b ** n) m = log b m DIV n
|
|
|
Proof
|
|
|
rw [] >> irule LOG_UNIQUE >>
|
|
|
rw [ADD1, EXP_MUL, LEFT_ADD_DISTRIB] >>
|
|
|
qmatch_goalsub_abbrev_tac `x DIV _` >>
|
|
|
drule mul_div_bound >> disch_then (qspec_then `x` mp_tac) >> rw []
|
|
|
>- (
|
|
|
irule LESS_LESS_EQ_TRANS >>
|
|
|
qexists_tac `b ** (x+1)` >> rw [] >>
|
|
|
unabbrev_all_tac >>
|
|
|
simp [EXP_ADD] >>
|
|
|
`b * (m DIV b + 1) ≤ b * b ** log b m`
|
|
|
by metis_tac [exp_log_bound, LESS_MONO_MULT, MULT_COMM] >>
|
|
|
`m < b * (m DIV b + 1)` suffices_by decide_tac >>
|
|
|
simp [LEFT_ADD_DISTRIB] >>
|
|
|
`b ≠ 0` by decide_tac >>
|
|
|
`m - (b - 1) ≤ b * (m DIV b)` by metis_tac [mul_div_bound] >>
|
|
|
fs [])
|
|
|
>- (
|
|
|
irule LESS_EQ_TRANS >>
|
|
|
qexists_tac `b ** (log b m)` >> rw [] >>
|
|
|
unabbrev_all_tac >>
|
|
|
metis_tac [exp_log_bound])
|
|
|
QED
|
|
|
|
|
|
(* ----- Theorems about word stuff ----- *)
|
|
|
|
|
|
Theorem l2n_padding:
|
|
|
∀ws n. l2n 256 (ws ++ map w2n (replicate n 0w)) = l2n 256 ws
|
|
|
Proof
|
|
|
Induct >> rw [l2n_def] >>
|
|
|
Induct_on `n` >> rw [l2n_def]
|
|
|
QED
|
|
|
|
|
|
Theorem l2n_0:
|
|
|
∀l b. b ≠ 0 ∧ every ($> b) l⇒ (l2n b l = 0 ⇔ every ($= 0) l)
|
|
|
Proof
|
|
|
Induct >> rw [l2n_def] >>
|
|
|
eq_tac >> rw []
|
|
|
QED
|
|
|
|
|
|
Theorem mod_n2l:
|
|
|
∀d n. 0 < d ⇒ map (\x. x MOD d) (n2l d n) = n2l d n
|
|
|
Proof
|
|
|
rw [] >> drule n2l_BOUND >> disch_then (qspec_then `n` mp_tac) >>
|
|
|
qspec_tac (`n2l d n`, `l`) >>
|
|
|
Induct >> rw []
|
|
|
QED
|
|
|
|
|
|
(* ----- Theorems about converting between values and byte lists ----- *)
|
|
|
|
|
|
Theorem le_write_w_length:
|
|
|
∀l x. length (le_write_w l w) = l
|
|
|
Proof
|
|
|
rw [le_write_w_def]
|
|
|
QED
|
|
|
|
|
|
Theorem v2b_size:
|
|
|
∀t v. value_type t v ⇒ length (value_to_bytes v) = sizeof t
|
|
|
Proof
|
|
|
ho_match_mp_tac value_type_ind >>
|
|
|
rw [value_to_bytes_def, sizeof_def]
|
|
|
>- metis_tac [le_write_w_length]
|
|
|
>- metis_tac [le_write_w_length]
|
|
|
>- metis_tac [le_write_w_length]
|
|
|
>- (Induct_on `vs` >> rw [ADD1] >> fs [])
|
|
|
>- (
|
|
|
pop_assum mp_tac >>
|
|
|
qid_spec_tac `vs` >> qid_spec_tac `ts` >>
|
|
|
ho_match_mp_tac LIST_REL_ind >> rw [])
|
|
|
QED
|
|
|
|
|
|
Theorem b2v_size:
|
|
|
(∀t bs. first_class_type t ∧ sizeof t ≤ length bs ⇒
|
|
|
∃v. bytes_to_value t bs = (v, drop (sizeof t) bs)) ∧
|
|
|
(∀n t bs. first_class_type t ∧ n * sizeof t ≤ length bs ⇒
|
|
|
∃vs. read_array n t bs = (vs, drop (n * sizeof t) bs)) ∧
|
|
|
(∀ts bs. every first_class_type ts ∧ sum (map sizeof ts) ≤ length bs ⇒
|
|
|
∃vs. read_str ts bs = (vs, drop (sum (map sizeof ts)) bs))
|
|
|
Proof
|
|
|
ho_match_mp_tac bytes_to_value_ind >>
|
|
|
rw [sizeof_def, bytes_to_value_def, le_read_w_def] >>
|
|
|
fs [first_class_type_def]
|
|
|
>- (simp [PAIR_MAP] >> metis_tac [SND])
|
|
|
>- (
|
|
|
pairarg_tac >> rw [] >> pairarg_tac >> rw [] >>
|
|
|
fs [ADD1] >> rw [] >> fs [DROP_DROP_T, LEFT_ADD_DISTRIB])
|
|
|
>- fs [DROP_DROP_T, LEFT_ADD_DISTRIB]
|
|
|
QED
|
|
|
|
|
|
Theorem b2v_smaller:
|
|
|
∀t bs. first_class_type t ∧ sizeof t ≤ length bs ⇒
|
|
|
length (snd (bytes_to_value t bs)) = length bs - sizeof t
|
|
|
Proof
|
|
|
rw [] >> imp_res_tac b2v_size >>
|
|
|
Cases_on `bytes_to_value t bs` >> fs []
|
|
|
QED
|
|
|
|
|
|
Theorem b2v_append:
|
|
|
(∀t bs. first_class_type t ∧ sizeof t ≤ length bs ⇒
|
|
|
bytes_to_value t (bs ++ bs') = (I ## (λx. x ++ bs')) (bytes_to_value t bs)) ∧
|
|
|
(∀n t bs. first_class_type t ∧ n * sizeof t ≤ length bs ⇒
|
|
|
∃vs. read_array n t (bs ++ bs') = (I ## (λx. x ++ bs')) (read_array n t bs)) ∧
|
|
|
(∀ts bs. every first_class_type ts ∧ sum (map sizeof ts) ≤ length bs ⇒
|
|
|
∃vs. read_str ts (bs ++ bs') = (I ## (λx. x ++ bs')) (read_str ts bs))
|
|
|
Proof
|
|
|
ho_match_mp_tac bytes_to_value_ind >>
|
|
|
rw [sizeof_def, bytes_to_value_def, le_read_w_def] >>
|
|
|
fs [first_class_type_def, TAKE_APPEND, DROP_APPEND,
|
|
|
DECIDE ``!x y. x ≤ y ⇒ x - y = 0n``, ETA_THM]
|
|
|
>- (simp [PAIR_MAP] >> metis_tac [SND])
|
|
|
>- (simp [PAIR_MAP] >> metis_tac [SND])
|
|
|
>- (
|
|
|
rpt (pairarg_tac >> simp []) >> fs [ADD1] >>
|
|
|
BasicProvers.VAR_EQ_TAC >> fs [LEFT_ADD_DISTRIB] >>
|
|
|
first_x_assum irule >>
|
|
|
`sizeof t ≤ length bs` by decide_tac >>
|
|
|
imp_res_tac b2v_smaller >> rfs [])
|
|
|
>- (
|
|
|
rpt (pairarg_tac >> simp []) >> fs [ADD1] >>
|
|
|
BasicProvers.VAR_EQ_TAC >> fs [LEFT_ADD_DISTRIB] >>
|
|
|
first_x_assum irule >>
|
|
|
`sizeof t ≤ length bs` by decide_tac >>
|
|
|
imp_res_tac b2v_smaller >> rfs [])
|
|
|
QED
|
|
|
|
|
|
Theorem le_read_write:
|
|
|
∀n w bs.
|
|
|
n ≠ 0 ∧ dimword (:'a) ≤ 256 ** n ⇒ le_read_w n (le_write_w n (w :'a word) ⧺ bs) = (w, bs)
|
|
|
Proof
|
|
|
rw [le_read_w_def, le_write_w_length]
|
|
|
>- (
|
|
|
`take n (le_write_w n w ⧺ bs) = le_write_w n w`
|
|
|
by metis_tac [le_write_w_length, TAKE_LENGTH_APPEND] >>
|
|
|
simp [le_write_w_def, w2l_def, l2w_def] >>
|
|
|
Cases_on `w` >> simp [] >> fs [l2n_padding, TAKE_APPEND, take_replicate] >>
|
|
|
simp [MAP_TAKE, MAP_MAP_o, combinTheory.o_DEF, mod_n2l] >>
|
|
|
rename1 `n2l 256 m` >>
|
|
|
`length (n2l 256 m) ≤ n`
|
|
|
by (
|
|
|
rw [LENGTH_n2l] >>
|
|
|
`256 = 2 ** 8` by EVAL_TAC >>
|
|
|
ASM_REWRITE_TAC [] >> simp [log_change_base_power, GSYM LESS_EQ] >>
|
|
|
`n2w m ≠ 0w` by simp [] >>
|
|
|
drule LOG2_w2n_lt >> rw [] >> fs [bitTheory.LOG2_def, dimword_def] >>
|
|
|
`8 * (log 2 m DIV 8) ≤ log 2 m` by metis_tac [mul_div_bound, EVAL ``8 ≠ 0n``] >>
|
|
|
`LOG 2 (2 ** dimindex (:'a)) ≤ LOG 2 (256 ** n)` by simp [LOG_LE_MONO] >>
|
|
|
pop_assum mp_tac >>
|
|
|
`256 = 2 ** 8` by EVAL_TAC >>
|
|
|
ASM_REWRITE_TAC [EXP_MUL] >> simp [log_base_power]) >>
|
|
|
simp [mod_n2l, l2n_n2l, TAKE_LENGTH_TOO_LONG])
|
|
|
>- metis_tac [le_write_w_length, DROP_LENGTH_APPEND]
|
|
|
QED
|
|
|
|
|
|
Theorem le_write_read:
|
|
|
∀n w bs bs'.
|
|
|
256 ** n ≤ dimword (:'a) ∧
|
|
|
n ≤ length bs ∧
|
|
|
le_read_w n bs = (w:'a word, bs')
|
|
|
⇒
|
|
|
le_write_w n w ++ bs' = bs
|
|
|
Proof
|
|
|
rw [le_read_w_def] >>
|
|
|
qmatch_goalsub_abbrev_tac `l2w _ l` >>
|
|
|
`le_write_w n (l2w 256 l) = take n bs` suffices_by metis_tac [TAKE_DROP] >>
|
|
|
simp [le_write_w_def, w2l_l2w] >>
|
|
|
`l2n 256 l < 256 ** n`
|
|
|
by (
|
|
|
`n ≤ length bs` by decide_tac >>
|
|
|
metis_tac [l2n_lt, LENGTH_TAKE, LENGTH_MAP, EVAL ``0n < 256``]) >>
|
|
|
fs [] >>
|
|
|
`every ($> 256) l`
|
|
|
by (
|
|
|
simp [EVERY_MAP, Abbr `l`] >> irule EVERY_TAKE >> simp [] >>
|
|
|
rpt (pop_assum kall_tac) >>
|
|
|
Induct_on `bs` >> rw [] >>
|
|
|
Cases_on `h` >> fs []) >>
|
|
|
rw [n2l_l2n]
|
|
|
>- (
|
|
|
rw [TAKE_def, take_replicate] >>
|
|
|
Cases_on `n` >> fs [] >>
|
|
|
rfs [l2n_0] >> unabbrev_all_tac >> fs [EVERY_MAP] >>
|
|
|
ONCE_REWRITE_TAC [GSYM REPLICATE] >>
|
|
|
qmatch_goalsub_abbrev_tac `take n _` >>
|
|
|
qpat_assum `¬(_ < _)` mp_tac >>
|
|
|
qpat_assum `every (\x. 0 = w2n x) _` mp_tac >>
|
|
|
rpt (pop_assum kall_tac) >>
|
|
|
qid_spec_tac `bs` >>
|
|
|
Induct_on `n` >> rw [] >>
|
|
|
Cases_on `bs` >> rw [] >> fs [] >>
|
|
|
Cases_on `h` >> fs [] >>
|
|
|
first_x_assum irule >> rw [] >>
|
|
|
irule MONO_EVERY >>
|
|
|
qexists_tac `(λx. 0 = w2n x)` >> rw []) >>
|
|
|
fs [MAP_TAKE, MAP_MAP_o, combinTheory.o_DEF] >>
|
|
|
`exists (\y. 0 ≠ y) l`
|
|
|
by (
|
|
|
fs [l2n_eq_0, combinTheory.o_DEF] >> fs [EXISTS_MEM, EVERY_MEM] >>
|
|
|
qexists_tac `x` >> rfs [MOD_MOD, GREATER_DEF]) >>
|
|
|
simp [LOG_l2n_dropWhile] >>
|
|
|
`length (dropWhile ($= 0) (reverse l)) ≠ 0`
|
|
|
by (
|
|
|
Cases_on `l` >> fs [dropWhile_eq_nil, combinTheory.o_DEF, EXISTS_REVERSE]) >>
|
|
|
`0 < length (dropWhile ($= 0) (reverse l))` by decide_tac >>
|
|
|
fs [SUC_PRE] >>
|
|
|
`map n2w l = take n bs`
|
|
|
by (simp [Abbr `l`, MAP_TAKE, MAP_MAP_o, combinTheory.o_DEF, n2w_w2n]) >>
|
|
|
simp [TAKE_TAKE_MIN] >>
|
|
|
`length l = n` by simp [Abbr `l`] >>
|
|
|
`length (dropWhile ($= 0) (reverse l)) ≤ n`
|
|
|
by metis_tac [LESS_EQ_TRANS, LENGTH_dropWhile_LESS_EQ, LENGTH_REVERSE] >>
|
|
|
rw [MIN_DEF] >> fs []
|
|
|
>- (
|
|
|
simp [TAKE_APPEND, TAKE_TAKE_MIN, MIN_DEF, take_replicate] >>
|
|
|
`replicate (length l − length (dropWhile ($= 0) (reverse l))) 0w =
|
|
|
take (length l − (length (dropWhile ($= 0) (reverse l)))) (drop (length (dropWhile ($= 0) (reverse l))) bs)`
|
|
|
suffices_by (rw [] >> irule take_drop_partition >> simp []) >>
|
|
|
rw [LIST_EQ_REWRITE, EL_REPLICATE, EL_TAKE, EL_DROP] >>
|
|
|
`length (dropWhile ($= 0) (reverse l)) =
|
|
|
length (dropWhile (λx. 0 = w2n x) (reverse (take (length l) bs)))`
|
|
|
by (
|
|
|
`reverse l = reverse (take (length l) (map w2n bs))` by metis_tac [] >>
|
|
|
ONCE_ASM_REWRITE_TAC [] >>
|
|
|
qpat_x_assum `Abbrev (l = _)` kall_tac >>
|
|
|
simp [GSYM MAP_TAKE, GSYM MAP_REVERSE, dropWhile_map, combinTheory.o_DEF]) >>
|
|
|
fs [] >>
|
|
|
`x + length (dropWhile (λx. 0 = w2n x) (reverse (take (length l) bs))) < length l` by decide_tac >>
|
|
|
drule (SIMP_RULE std_ss [LET_THM] dropWhile_rev_take) >>
|
|
|
rw [] >>
|
|
|
REWRITE_TAC [GSYM w2n_11, word_0_n2w] >>
|
|
|
simp [])
|
|
|
>- rw [TAKE_APPEND, TAKE_TAKE]
|
|
|
QED
|
|
|
|
|
|
Theorem b2v_v2b:
|
|
|
∀v t bs. value_type t v ⇒ bytes_to_value t (value_to_bytes v ++ bs) = (v, bs)
|
|
|
Proof
|
|
|
gen_tac >> completeInduct_on `v_size v` >>
|
|
|
rw [] >>
|
|
|
pop_assum mp_tac >> simp [value_type_cases] >>
|
|
|
rw [] >>
|
|
|
rw [bytes_to_value_def, value_to_bytes_def, le_read_write]
|
|
|
>- wordsLib.WORD_DECIDE_TAC
|
|
|
>- (
|
|
|
qmatch_abbrev_tac `_ x = _` >>
|
|
|
`x = (vs, bs)` suffices_by (simp [PAIR_MAP] >> metis_tac [PAIR_EQ, FST, SND]) >>
|
|
|
unabbrev_all_tac >>
|
|
|
qid_spec_tac `bs` >> Induct_on `vs` >> simp [bytes_to_value_def] >>
|
|
|
rw [] >> fs [v_size_def] >>
|
|
|
pairarg_tac >> fs [] >>
|
|
|
pairarg_tac >> fs [] >>
|
|
|
rename1 `value_type t v1` >>
|
|
|
first_x_assum (qspec_then `v_size v1` mp_tac) >> simp [] >>
|
|
|
disch_then (qspec_then `v1` mp_tac) >> simp [] >>
|
|
|
disch_then (qspec_then `t` mp_tac) >> simp [] >>
|
|
|
qmatch_assum_abbrev_tac `bytes_to_value _ (_ ++ bs1 ++ _) = _` >>
|
|
|
disch_then (qspec_then `bs1++bs` mp_tac) >> simp [] >>
|
|
|
unabbrev_all_tac >> strip_tac >> fs [] >>
|
|
|
first_x_assum (qspec_then `bs` mp_tac) >> rw [])
|
|
|
>- (
|
|
|
qmatch_abbrev_tac `_ x = _` >>
|
|
|
`x = (vs, bs)` suffices_by (simp [PAIR_MAP] >> metis_tac [PAIR_EQ, FST, SND]) >>
|
|
|
unabbrev_all_tac >>
|
|
|
pop_assum mp_tac >>
|
|
|
qid_spec_tac `bs` >> qid_spec_tac `ts` >> Induct_on `vs` >> simp [bytes_to_value_def] >>
|
|
|
rw [] >> fs [v_size_def, bytes_to_value_def] >>
|
|
|
pairarg_tac >> fs [] >>
|
|
|
pairarg_tac >> fs [] >>
|
|
|
rename1 `value_type t v1` >>
|
|
|
first_x_assum (qspec_then `v_size v1` mp_tac) >> simp [] >>
|
|
|
disch_then (qspec_then `v1` mp_tac) >> simp [] >>
|
|
|
disch_then (qspec_then `t` mp_tac) >> simp [] >>
|
|
|
qmatch_assum_abbrev_tac `bytes_to_value _ (_ ++ bs1 ++ _) = _` >>
|
|
|
disch_then (qspec_then `bs1++bs` mp_tac) >> simp [] >>
|
|
|
unabbrev_all_tac >> strip_tac >> fs [] >>
|
|
|
first_x_assum drule >> metis_tac [PAIR_EQ])
|
|
|
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
|
|
|
|
|
|
Definition extract_type_def:
|
|
|
(extract_type t [] = Some t) ∧
|
|
|
(extract_type (ArrT n t) (i::idx) =
|
|
|
if i < n then
|
|
|
extract_type t idx
|
|
|
else
|
|
|
None) ∧
|
|
|
(extract_type (StrT ts) (i::idx) =
|
|
|
if i < length ts then
|
|
|
extract_type (el i ts) idx
|
|
|
else
|
|
|
None) ∧
|
|
|
(extract_type _ _ = None)
|
|
|
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, 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 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 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 [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 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_value t' (drop n (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_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 [value_to_bytes_def]
|
|
|
>- (
|
|
|
`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 value_to_bytes vs)))`
|
|
|
by (
|
|
|
simp [LENGTH_FLAT, MAP_TAKE, MAP_MAP_o, combinTheory.o_DEF] >>
|
|
|
`map (λx. length (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 [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 (value_to_bytes (el i vs)))`
|
|
|
by (simp [LENGTH_DROP] >> drule v2b_size >> rw [] >> metis_tac [extract_offset_size]) >>
|
|
|
simp [b2v_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 value_to_bytes vs)))`
|
|
|
by (
|
|
|
simp [LENGTH_FLAT, MAP_TAKE, MAP_MAP_o, combinTheory.o_DEF] >>
|
|
|
`map sizeof ts = map (\x. length (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 [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 (value_to_bytes (el i vs)))`
|
|
|
by (simp [LENGTH_DROP] >> drule v2b_size >> rw [] >> metis_tac [extract_offset_size]) >>
|
|
|
simp [b2v_append])
|
|
|
QED
|
|
|
|
|
|
(* ----- Theorems about the step function ----- *)
|
|
|
|
|
|
Theorem inc_pc_invariant:
|
|
|
∀p s i. prog_ok p ∧ next_instr p s 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]
|
|
|
>- (
|
|
|
qexists_tac `dec` >> qexists_tac `block'` >> rw [] >>
|
|
|
fs [prog_ok_def, next_instr_cases] >> res_tac >> rw [] >>
|
|
|
`s.ip.i ≠ length block'.body - 1` suffices_by decide_tac >>
|
|
|
CCONTR_TAC >> fs [] >> rfs [LAST_EL, PRE_SUB1]) >>
|
|
|
metis_tac []
|
|
|
QED
|
|
|
|
|
|
Theorem next_instr_update:
|
|
|
∀p s i r v. next_instr p (update_result r v s) i <=> next_instr p s i
|
|
|
Proof
|
|
|
rw [next_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 flookup_set_bytes:
|
|
|
∀poison bytes n h n'.
|
|
|
flookup (set_bytes poison bytes n h) (A n') =
|
|
|
if n ≤ n' ∧ n' < n + length bytes then
|
|
|
Some (poison, el (n' - n) bytes)
|
|
|
else
|
|
|
flookup h (A n')
|
|
|
Proof
|
|
|
Induct_on `bytes` >> rw [set_bytes_def, EL_CONS, PRE_SUB1] >>
|
|
|
fs [ADD1, FLOOKUP_UPDATE] >>
|
|
|
`n = n'` by decide_tac >>
|
|
|
rw []
|
|
|
QED
|
|
|
|
|
|
Theorem allocate_invariant:
|
|
|
∀p s1 v1 t v2 s2.
|
|
|
state_invariant p s1 ∧ allocate s1 v1 t (v2,s2) ⇒ state_invariant p s2
|
|
|
Proof
|
|
|
rw [allocate_cases, state_invariant_def, ip_ok_def, heap_ok_def,
|
|
|
globals_ok_def, stack_ok_def] >>
|
|
|
rw [] >> fs []
|
|
|
>- (
|
|
|
fs [allocations_ok_def] >> rpt gen_tac >> disch_tac >> fs [is_free_def] >> rw [] >>
|
|
|
metis_tac [INTER_COMM])
|
|
|
>- (
|
|
|
rw [flookup_set_bytes]
|
|
|
>- rw [RIGHT_AND_OVER_OR, EXISTS_OR_THM, interval_to_set_def] >>
|
|
|
eq_tac >> rw [] >> fs [interval_to_set_def] >>
|
|
|
metis_tac [])
|
|
|
>- (fs [is_allocated_def] >> metis_tac [])
|
|
|
>- (fs [EVERY_EL, frame_ok_def] >> rw [] >> metis_tac [])
|
|
|
QED
|
|
|
|
|
|
Theorem set_bytes_invariant:
|
|
|
∀s poison bytes n prog b.
|
|
|
state_invariant prog s ∧ is_allocated (b, n, n + length bytes) s.allocations
|
|
|
⇒
|
|
|
state_invariant prog (s with heap := set_bytes poison bytes n s.heap)
|
|
|
Proof
|
|
|
rw [state_invariant_def]
|
|
|
>- (fs [allocations_ok_def] >> rw [] >> metis_tac [])
|
|
|
>- (
|
|
|
fs [heap_ok_def, flookup_set_bytes] >> rw [] >>
|
|
|
fs [is_allocated_def, interval_to_set_def, SUBSET_DEF] >>
|
|
|
metis_tac [LESS_EQ_REFL, DECIDE ``!x y. x < x + SUC y``])
|
|
|
>- (fs [globals_ok_def] >> metis_tac [])
|
|
|
>- (fs [stack_ok_def, EVERY_EL, frame_ok_def])
|
|
|
QED
|
|
|
|
|
|
Theorem step_instr_invariant:
|
|
|
∀i s2.
|
|
|
step_instr p s1 i s2 ⇒ prog_ok p ∧ next_instr p s1 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 [deallocate_def, allocations_ok_def] >> rw [] >> 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 [])
|
|
|
>- (
|
|
|
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, NOT_NIL_EQ_LENGTH_NOT_0] >>
|
|
|
qpat_x_assum `flookup _ (Fn "main") = _` kall_tac >>
|
|
|
last_x_assum drule >> disch_then drule >> fs [])
|
|
|
>- (fs [allocations_ok_def] >> metis_tac [])
|
|
|
>- (fs [heap_ok_def] >> 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, NOT_NIL_EQ_LENGTH_NOT_0] >>
|
|
|
qpat_x_assum `flookup _ (Fn "main") = _` kall_tac >>
|
|
|
last_x_assum drule >> disch_then drule >> fs [])
|
|
|
>- (fs [allocations_ok_def] >> metis_tac [])
|
|
|
>- (fs [heap_ok_def] >> metis_tac [])
|
|
|
>- (fs [globals_ok_def] >> metis_tac [])
|
|
|
>- (fs [stack_ok_def, frame_ok_def, EVERY_MEM] >> metis_tac []))
|
|
|
>- (
|
|
|
irule inc_pc_invariant >> rw [next_instr_update, update_invariant]>>
|
|
|
metis_tac [terminator_def])
|
|
|
>- (
|
|
|
irule inc_pc_invariant >> rw [next_instr_update, update_invariant] >>
|
|
|
metis_tac [terminator_def])
|
|
|
>- (
|
|
|
irule inc_pc_invariant >> rw [next_instr_update, update_invariant] >>
|
|
|
metis_tac [terminator_def])
|
|
|
>- ( (* Allocation *)
|
|
|
irule inc_pc_invariant >> rw [next_instr_update, update_invariant]
|
|
|
>- metis_tac [allocate_invariant]
|
|
|
>- (fs [next_instr_cases, allocate_cases] >> metis_tac [terminator_def]))
|
|
|
>- (
|
|
|
irule inc_pc_invariant >> rw [next_instr_update, update_invariant] >>
|
|
|
fs [next_instr_cases] >>
|
|
|
metis_tac [terminator_def])
|
|
|
>- ( (* Store *)
|
|
|
irule inc_pc_invariant >> rw [next_instr_update, update_invariant]
|
|
|
>- (irule set_bytes_invariant >> rw [] >> metis_tac [])
|
|
|
>- (fs [next_instr_cases] >> metis_tac [terminator_def]))
|
|
|
>- (
|
|
|
irule inc_pc_invariant >> rw [next_instr_update, update_invariant] >>
|
|
|
metis_tac [terminator_def])
|
|
|
>- (
|
|
|
irule inc_pc_invariant >> rw [next_instr_update, update_invariant] >>
|
|
|
metis_tac [terminator_def])
|
|
|
>- (
|
|
|
irule inc_pc_invariant >> rw [next_instr_update, update_invariant] >>
|
|
|
metis_tac [terminator_def])
|
|
|
>- (
|
|
|
irule inc_pc_invariant >> rw [next_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, allocations_ok_def] >> metis_tac [])
|
|
|
>- (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] >>
|
|
|
last_x_assum drule >> disch_then drule >> rw [] >>
|
|
|
CCONTR_TAC >> fs [] >> rfs [LAST_EL] >>
|
|
|
Cases_on `length block'.body = s1.ip.i + 1` >> fs [PRE_SUB1] >>
|
|
|
fs [next_instr_cases] >>
|
|
|
metis_tac [terminator_def])
|
|
|
>- (fs [EVERY_MEM, frame_ok_def] >> metis_tac [])))
|
|
|
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
|
|
|
|
|
|
export_theory ();
|