Timings for disk_interpreter.v
- /home/gitlab-runner/builds/gGKko-aj/0/coq/coq/_bench/opam.OLD/ocaml-OLD/.opam-switch/build/coq-perennial.dev/./src/goose_lang/interpreter/disk_interpreter.v.timing
- /home/gitlab-runner/builds/gGKko-aj/0/coq/coq/_bench/opam.NEW/ocaml-NEW/.opam-switch/build/coq-perennial.dev/./src/goose_lang/interpreter/disk_interpreter.v.timing
From RecordUpdate Require Import RecordSet.
From stdpp Require Export binders strings gmap pretty.
From Perennial.Helpers Require Import Integers Transitions.
From Perennial.goose_lang Require Import prelude.
From Perennial.goose_lang.interpreter Require Import interpret_types.
From Perennial.goose_lang.interpreter Require Import pretty_types.
From Perennial.goose_lang.interpreter Require Import interpreter.
From Perennial.goose_lang.ffi Require Import disk disk_prelude.
#[global]
Instance statet_disk_error_bind : MBind (StateT state Error) :=
StateT_bind Error Error_fmap Error_join Error_bind.
#[global]
Instance statet_disk_error_ret : MRet (StateT state Error) :=
StateT_ret Error Error_ret.
Definition free_val {A} (x: nonAtomic A) : option A :=
match x with
| (Reading 0, y) => Some y
| _ => None
end.
Definition byte_val (v: val) : option byte :=
match v with
| LitV (LitByte b) => Some b
| _ => None
end.
Definition read_block_from_heap (σ: state) (l: loc) : option Block :=
navs <- state_readn_vec σ l block_bytes;
(* list (nonAtomic val) -> list (option val) -> option (list val) -> list val *)
vs <- commute_option_vec _ (vmap free_val navs);
commute_option_vec _ (vmap byte_val vs).
(* If state_readn_vec succeeds, for each i in the range requested, the
heap at l + i should be some nonatomic value and that nonatomic value
should be at the return value in index i. *)
Lemma state_readn_vec_ok : forall n σ l v,
(state_readn_vec σ l n = Some v) ->
(forall (i:fin n),
match σ.(heap) !! (l +ₗ i) with
| Some nav => nav = v !!! i
| _ => False
end).
destruct (heap σ !! l) as [nav|] eqn:heap_at_l; try by inversion H.
destruct (state_readn_vec σ (l +ₗ 1) n) as [vtl|] eqn:srv_ind; try by inversion H.
assert (l +ₗ 0%nat = l) as plus_zero.
replace (Z.of_nat 0%nat) with 0%Z by auto.
rewrite plus_zero heap_at_l; by exact H1.
assert (l +ₗ FS i = (l +ₗ 1) +ₗ i) as l_plus_one.
replace (Z.of_nat (S i)) with (1 + i) by lia.
rewrite loc_add_assoc //=.
pose proof (IHn σ (l +ₗ 1) vtl srv_ind i).
destruct (heap σ !! (l +ₗ 1 +ₗ i)) as [navi|] eqn:heap_at_l1i; [|contradiction].
apply Eqdep_dec.inj_pair2_eq_dec in H2.
Lemma commute_option_vec_ok {A} : forall n (ov: vec (option A) n) (v: vec A n),
(commute_option_vec A ov = Some v) ->
(forall (i:fin n), ov !!! i = Some (v !!! i)).
dependent destruction ov.
destruct h as [a|]; [simpl in H|by inversion H].
destruct (commute_option_vec A ov) as [v'|] eqn:cov_tail; [unfold mret, option_ret in H; simpl in H|by inversion H].
pose proof (IHn _ _ cov_tail) as IH_tail.
Lemma read_block_from_heap_ok (σ: state) (l: loc) (b: Block) :
(read_block_from_heap σ l = Some b) ->
(forall (i:Z), 0 <= i -> i < 4096 ->
match σ.(heap) !! (l +ₗ i) with
| Some (Reading _, v) => Block_to_vals b !! Z.to_nat i = Some v
| _ => False
end).
unfold read_block_from_heap in H.
destruct (state_readn_vec σ l block_bytes) as [navs|] eqn:vec_at_l; try by inversion H.
destruct (commute_option_vec val (vmap free_val navs)) as [v|] eqn:all_free; try by inversion H.
(* Only way to convert (Z.to_nat i) to a (fin 4096) is to have
hypothesis that says exactly this. *)
assert ((Z.to_nat i < Z.to_nat 4096)%nat) as fin_i by lia.
pose proof (commute_option_vec_ok _ _ _ H (nat_to_fin fin_i)) as cov_ok_v.
pose proof (commute_option_vec_ok _ _ _ all_free (nat_to_fin fin_i)) as cov_ok_navs.
pose proof (state_readn_vec_ok _ _ _ _ vec_at_l (nat_to_fin fin_i)) as srv_ok_l.
destruct (heap σ !! (l +ₗ nat_to_fin fin_i)) as [nav|] eqn:heap_at_fin_i; [| contradiction].
assert ((l +ₗ i) = (l +ₗ nat_to_fin fin_i)).
replace (Z.of_nat (nat_to_fin fin_i)) with i; auto.
pose proof (fin_to_nat_to_fin _ _ fin_i).
rewrite -> (vlookup_map free_val navs (nat_to_fin fin_i)) in cov_ok_navs.
unfold free_val in cov_ok_navs.
destruct (navs !!! nat_to_fin fin_i) as ([|?], nav') eqn:navs_at_fin_i; inversion cov_ok_navs.
destruct n eqn:n_is_free; inversion cov_ok_navs.
clear cov_ok_navs navs_at_fin_i heap_at_fin_i H4 H5 H2 srv_ok_l nav'
n n_is_free nav all_free H navs vec_at_l.
unfold block_bytes in cov_ok_v.
(* block_bytes doesn't show, but is here and needs to be unfolded *)
rewrite -> (vlookup_map byte_val v (nat_to_fin fin_i)) in cov_ok_v.
destruct (v !!! nat_to_fin fin_i) eqn:v_at_fin_i; try by inversion cov_ok_v.
pose proof (list_lookup_fmap b2val b (Z.to_nat i)) as llf.
destruct l0; try by inversion cov_ok_v.
inversion cov_ok_v as [b_at_fin_i].
pose proof (vlookup_lookup' b (Z.to_nat i) n) as vll.
assert ((b : list u8) !! Z.to_nat i = Some n).
rewrite b_at_fin_i; reflexivity.
#[global]
Instance pretty_disk_op : Pretty DiskOp :=
fun x => match x with
| ReadOp => "ReadOp"
| WriteOp => "WriteOp"
| SizeOp => "SizeOp"
end.
(* Not imported from interpreter.v? *)
#[global]
Instance statet_error_bind : MBind (StateT btstate Error) :=
StateT_bind Error Error_fmap Error_join Error_bind.
#[global]
Instance statet_error_ret : MRet (StateT btstate Error) :=
StateT_ret Error Error_ret.
(* Single-step interpreter for external disk operations. *)
Definition disk_interpret_step (op: DiskOp) (v: val) : StateT btstate Error expr :=
match (op, v) with
| (ReadOp, (LitV (LitInt _))) =>
bts <- mget;
let 'σg := fst bts in
t <- mlift (Transitions.interpret [] (ffi_step ReadOp v) σg) "Transitions.interpret failed in ReadOp";
match t with
| (hints, σg', v') => _ <- mput (σg', snd bts); mret v'
end
| (WriteOp, (PairV (LitV (LitInt a)) (LitV (LitLoc l)))) =>
bts <- mget;
let '(σ,g) := fst bts in
_ <- mlift (σ.(world) !! int.Z a) ("Disk WriteOp failed: No block at write address " ++ (pretty a));
b <- mlift (read_block_from_heap σ l) ("Disk WriteOp failed: Read from heap failed at location " ++ (pretty l));
_ <- mput ((set world <[ int.Z a := b ]> σ, g), snd bts);
mret (Val $ LitV (LitUnit))
| (SizeOp, LitV LitUnit) =>
bts <- mget;
let '(σ,g) := fst (bts : btstate) in
mret (Val $ LitV $ LitInt (U64 (disk_size σ.(@world _ disk_model))))
| _ => mfail ("DiskOp failed: Invalid argument types for " ++ (pretty op))
end.
Lemma disk_interpret_ok : forall (eop : DiskOp) (arg : val) (result : expr) (σ σ': state) (g g': global_state) (ws ws': btval),
(runStateT (disk_interpret_step eop arg) ((σ,g), ws) = Works _ (result, ((σ',g'), ws'))) ->
exists m l, @language.nsteps goose_lang m ([ExternalOp eop (Val arg)], (σ,g)) l ([result], (σ',g')).
intros eop arg result σ σ' g g' ws ws' H.
destruct eop; [| inversion H | inversion H].
unfold disk_interpret_step in H.
destruct arg; try by inversion H.
destruct l; try by inversion H.
assert (runStateT mget ((σ,g), ws) = Works _ (((σ,g), ws), ((σ,g), ws))) by eauto.
rewrite (runStateT_Error_bind _ _ _ _ _ _ _ H0) in H.
destruct (Transitions.interpret [] (ffi_step ReadOp #n) ((σ,g), ws).1) eqn:interp_res; simpl in H; try by inversion H.
destruct p as ((l & s) & b).
pose proof (Transitions.relation.interpret_sound _ _ _ interp_res) as interp_ok.
(* TODO: rewrite using ltac (or a lemma) to handle adding a function at the end of a known relation (does that exist?) *)
econstructor; [exact H1|].
econstructor; [exact H3|].
destruct arg; try by inversion H1.
destruct arg1; try by inversion H1.
destruct l; try by inversion H1.
destruct arg2; try by inversion H1.
destruct l; try by inversion H1.
destruct (world σ !! int.Z n) eqn:disk_at_n; rewrite disk_at_n in H1; last by inversion H1.
destruct (read_block_from_heap σ l) eqn:block_at_l; try by inversion H1.
pose proof (read_block_from_heap_ok _ _ _ block_at_l) as rbfsok.
eapply relation.bind_suchThat; [exact rbfsok|].
destruct arg; try by inversion H1.
destruct l; try by inversion H1.
#[global]
Instance disk_interpretable : @ext_interpretable disk_op disk_model disk_semantics :=
{ ext_interpret_step := disk_interpret_step;
ext_interpret_ok := disk_interpret_ok }.