From 2ae43be7b9d4118335c9d2cef6e098f9b9f807fe Mon Sep 17 00:00:00 2001 From: xleroy Date: Thu, 9 Feb 2006 14:55:48 +0000 Subject: Initial import of compcert git-svn-id: https://yquem.inria.fr/compcert/svn/compcert/trunk@1 fca1b0fc-160b-0410-b1d3-a4f43f01ea2e --- backend/Machabstr2mach.v | 1120 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1120 insertions(+) create mode 100644 backend/Machabstr2mach.v (limited to 'backend/Machabstr2mach.v') diff --git a/backend/Machabstr2mach.v b/backend/Machabstr2mach.v new file mode 100644 index 00000000..8549cefc --- /dev/null +++ b/backend/Machabstr2mach.v @@ -0,0 +1,1120 @@ +(** Simulation between the two semantics for the Mach language. *) + +Require Import Coqlib. +Require Import Maps. +Require Import AST. +Require Import Integers. +Require Import Values. +Require Import Mem. +Require Import Globalenvs. +Require Import Op. +Require Import Locations. +Require Import Machabstr. +Require Import Mach. +Require Import Machtyping. +Require Import Stackingproof. + +(** Two semantics were defined for the Mach intermediate language: +- The concrete semantics (file [Mach]), where the whole activation + record resides in memory and the [Mgetstack], [Msetstack] and + [Mgetparent] are interpreted as [sp]-relative memory accesses. +- The abstract semantics (file [Machabstr]), where the activation + record is split in two parts: the Cminor stack data, resident in + memory, and the frame information, residing not in memory but + in additional evaluation environments. + + In this file, we show a simulation result between these + semantics: if a program executes to some result [r] in the + abstract semantics, it also executes to the same result in + the concrete semantics. This result bridges the correctness proof + in file [Stackingproof] (which uses the abstract Mach semantics + as output) and that in file [PPCgenproof] (which uses the concrete + Mach semantics as input). +*) + +Remark align_16_top_ge: + forall lo hi, + hi <= align_16_top lo hi. +Proof. + intros. unfold align_16_top. apply Zmax_bound_r. + assert (forall x, x <= (x + 15) / 16 * 16). + intro. assert (16 > 0). omega. + generalize (Z_div_mod_eq (x + 15) 16 H). intro. + replace ((x + 15) / 16 * 16) with ((x + 15) - (x + 15) mod 16). + generalize (Z_mod_lt (x + 15) 16 H). omega. + rewrite Zmult_comm. omega. + generalize (H (hi - lo)). omega. +Qed. + +Remark align_16_top_pos: + forall lo hi, + 0 <= align_16_top lo hi. +Proof. + intros. unfold align_16_top. apply Zmax_bound_l. omega. +Qed. + +Remark size_mem_pos: + forall sz, size_mem sz > 0. +Proof. + destruct sz; simpl; compute; auto. +Qed. + +Remark size_type_chunk: + forall ty, size_chunk (chunk_of_type ty) = 4 * typesize ty. +Proof. + destruct ty; reflexivity. +Qed. + +Remark mem_chunk_type: + forall ty, mem_chunk (chunk_of_type ty) = mem_type ty. +Proof. + destruct ty; reflexivity. +Qed. + +Remark size_mem_type: + forall ty, size_mem (mem_type ty) = 4 * typesize ty. +Proof. + destruct ty; reflexivity. +Qed. + +(** * Agreement between frames and memory-resident activation records *) + +(** ** Agreement for one frame *) + +(** The core idea of the simulation proof is that for all active + functions, the memory-allocated activation record, in the concrete + semantics, contains the same data as the Cminor stack block + (at positive offsets) and the frame of the function (at negative + offsets) in the abstract semantics. + + This intuition (activation record = Cminor stack data + frame) + is formalized by the following predicate [frame_match fr sp base mm ms]. + [fr] is a frame and [mm] the current memory state in the abstract + semantics. [ms] is the current memory state in the concrete semantics. + The stack pointer is [Vptr sp base] in both semantics. *) + +Inductive frame_match: frame -> block -> int -> mem -> mem -> Prop := + frame_match_intro: + forall fr sp base mm ms, + valid_block ms sp -> + low_bound mm sp = 0 -> + low_bound ms sp = fr.(low) -> + high_bound ms sp = align_16_top fr.(low) (high_bound mm sp) -> + fr.(low) <= 0 -> + Int.min_signed <= fr.(low) -> + base = Int.repr fr.(low) -> + block_contents_agree fr.(low) 0 fr (ms.(blocks) sp) -> + block_contents_agree 0 (high_bound ms sp) + (mm.(blocks) sp) (ms.(blocks) sp) -> + frame_match fr sp base mm ms. + +(** [frame_match], while presented as a relation for convenience, + is actually a function: it fully determines the contents of the + activation record [ms.(blocks) sp]. *) + +Lemma frame_match_exten: + forall fr sp base mm ms1 ms2, + frame_match fr sp base mm ms1 -> + frame_match fr sp base mm ms2 -> + ms1.(blocks) sp = ms2.(blocks) sp. +Proof. + intros. inversion H. inversion H0. + unfold low_bound, high_bound in *. + apply block_contents_exten. + congruence. + congruence. + hnf; intros. + elim H29. rewrite H3; rewrite H4; intros. + case (zlt ofs 0); intro. + assert (low fr <= ofs < 0). tauto. + transitivity (contents fr ofs). + symmetry. apply H8; auto. + apply H22; auto. + transitivity (contents (blocks mm sp) ofs). + symmetry. apply H9. rewrite H4. omega. + apply H23. rewrite H18. omega. +Qed. + +(** The following two innocuous-looking lemmas are the key results + showing that [sp]-relative memory accesses in the concrete + semantics behave like the direct frame accesses in the abstract + semantics. First, a value [v] that has type [ty] is preserved + when stored in memory with chunk [chunk_of_type ty], then read + back with the same chunk. The typing hypothesis is crucial here: + for instance, a float value reads back as [Vundef] when stored + and load with chunk [Mint32]. *) + +Lemma load_result_ty: + forall v ty, + Val.has_type v ty -> Val.load_result (chunk_of_type ty) v = v. +Proof. + destruct v; destruct ty; simpl; contradiction || reflexivity. +Qed. + +(** Second, computations of [sp]-relative offsets using machine + arithmetic (as done in the concrete semantics) never overflows + and behaves identically to the offset computations using exact + arithmetic (as done in the abstract semantics). *) + +Lemma int_add_no_overflow: + forall x y, + Int.min_signed <= Int.signed x + Int.signed y <= Int.max_signed -> + Int.signed (Int.add x y) = Int.signed x + Int.signed y. +Proof. + intros. rewrite Int.add_signed. rewrite Int.signed_repr. auto. auto. +Qed. + +(** Given matching frames and activation records, loading from the + activation record (in the concrete semantics) behaves identically + to reading the corresponding slot from the frame + (in the abstract semantics). *) + +Lemma frame_match_get_slot: + forall fr sp base mm ms ty ofs v, + frame_match fr sp base mm ms -> + get_slot fr ty (Int.signed ofs) v -> + Val.has_type v ty -> + load_stack ms (Vptr sp base) ty ofs = Some v. +Proof. + intros. inversion H. inversion H0. subst v. + unfold load_stack, Val.add, loadv. + assert (Int.signed base = low fr). + rewrite H8. apply Int.signed_repr. + split. auto. apply Zle_trans with 0. auto. compute; congruence. + assert (Int.signed (Int.add base ofs) = low fr + Int.signed ofs). + rewrite int_add_no_overflow. rewrite H18. auto. + rewrite H18. split. omega. apply Zle_trans with 0. + generalize (typesize_pos ty). omega. compute. congruence. + rewrite H23. + assert (BND1: low_bound ms sp <= low fr + Int.signed ofs). + omega. + assert (BND2: (low fr + Int.signed ofs) + size_chunk (chunk_of_type ty) <= high_bound ms sp). + rewrite size_type_chunk. apply Zle_trans with 0. + assumption. rewrite H5. apply align_16_top_pos. + generalize (load_in_bounds (chunk_of_type ty) ms sp (low fr + Int.signed ofs) H2 BND1 BND2). + intros [v' LOAD]. + generalize (load_inv _ _ _ _ _ LOAD). + intros [A [B [C D]]]. + rewrite LOAD. rewrite <- D. + decEq. rewrite mem_chunk_type. + rewrite <- size_mem_type in H17. + assert (low fr <= low fr + Int.signed ofs). omega. + generalize (load_contentmap_agree _ _ _ _ _ _ H9 H24 H17). + intro. rewrite H25. + apply load_result_ty. + assumption. +Qed. + +(** Loads from [sp], corresponding to accesses to Cminor stack data + in the abstract semantics, give the same results in the concrete + semantics. This is because the offset from [sp] must be positive or + null for the original load to succeed, and because the part + of the activation record at positive offsets matches the Cminor + stack data block. *) + +Lemma frame_match_load: + forall fr sp base mm ms chunk ofs v, + frame_match fr sp base mm ms -> + load chunk mm sp ofs = Some v -> + load chunk ms sp ofs = Some v. +Proof. + intros. inversion H. + generalize (load_inv _ _ _ _ _ H0). intros [A [B [C D]]]. + change (low (blocks mm sp)) with (low_bound mm sp) in B. + change (high (blocks mm sp)) with (high_bound mm sp) in C. + unfold load. rewrite zlt_true; auto. + rewrite in_bounds_holds. + rewrite <- D. decEq. decEq. eapply load_contentmap_agree. + red in H9. eexact H9. + omega. + unfold size_chunk in C. rewrite H4. + apply Zle_trans with (high_bound mm sp). auto. + apply align_16_top_ge. + change (low (blocks ms sp)) with (low_bound ms sp). + rewrite H3. omega. + change (high (blocks ms sp)) with (high_bound ms sp). + rewrite H4. apply Zle_trans with (high_bound mm sp). auto. + apply align_16_top_ge. +Qed. + +(** Assigning a value to a frame slot (in the abstract semantics) + corresponds to storing this value in the activation record + (in the concrete semantics). Moreover, agreement between frames + and activation records is preserved. *) + +Lemma frame_match_set_slot: + forall fr sp base mm ms ty ofs v fr', + frame_match fr sp base mm ms -> + set_slot fr ty (Int.signed ofs) v fr' -> + exists ms', + store_stack ms (Vptr sp base) ty ofs v = Some ms' /\ + frame_match fr' sp base mm ms'. +Proof. + intros. inversion H. inversion H0. subst ty0. + unfold store_stack, Val.add, storev. + assert (Int.signed base = low fr). + rewrite H7. apply Int.signed_repr. + split. auto. apply Zle_trans with 0. auto. compute; congruence. + assert (Int.signed (Int.add base ofs) = low fr + Int.signed ofs). + rewrite int_add_no_overflow. rewrite H16. auto. + rewrite H16. split. omega. apply Zle_trans with 0. + generalize (typesize_pos ty). omega. compute. congruence. + rewrite H20. + assert (BND1: low_bound ms sp <= low fr + Int.signed ofs). + omega. + assert (BND2: (low fr + Int.signed ofs) + size_chunk (chunk_of_type ty) <= high_bound ms sp). + rewrite size_type_chunk. rewrite H4. + apply Zle_trans with 0. subst ofs0. auto. apply align_16_top_pos. + generalize (store_in_bounds _ _ _ _ v H1 BND1 BND2). + intros [ms' STORE]. + generalize (store_inv _ _ _ _ _ _ STORE). intros [P [Q [R [S [x T]]]]]. + generalize (low_bound_store _ _ _ _ sp _ _ STORE). intro LB. + generalize (high_bound_store _ _ _ _ sp _ _ STORE). intro HB. + exists ms'. + split. exact STORE. + apply frame_match_intro; auto. + eapply valid_block_store; eauto. + rewrite LB. auto. + rewrite HB. auto. + red. rewrite T; rewrite update_s; simpl. + rewrite mem_chunk_type. + subst ofs0. eapply store_contentmap_agree; eauto. + rewrite HB; rewrite T; rewrite update_s. + red. simpl. apply store_contentmap_outside_agree. + assumption. left. rewrite mem_chunk_type. + rewrite size_mem_type. subst ofs0. auto. +Qed. + +(** Agreement is preserved by stores within blocks other than the + one pointed to by [sp]. *) + +Lemma frame_match_store_stack_other: + forall fr sp base mm ms sp' base' ty ofs v ms', + frame_match fr sp base mm ms -> + store_stack ms (Vptr sp' base') ty ofs v = Some ms' -> + sp <> sp' -> + frame_match fr sp base mm ms'. +Proof. + unfold store_stack, Val.add, storev. intros. inversion H. + generalize (store_inv _ _ _ _ _ _ H0). intros [P [Q [R [S [x T]]]]]. + generalize (low_bound_store _ _ _ _ sp _ _ H0). intro LB. + generalize (high_bound_store _ _ _ _ sp _ _ H0). intro HB. + apply frame_match_intro; auto. + eapply valid_block_store; eauto. + rewrite LB; auto. + rewrite HB; auto. + rewrite T; rewrite update_o; auto. + rewrite HB; rewrite T; rewrite update_o; auto. +Qed. + +(** Stores relative to [sp], corresponding to accesses to Cminor stack data + in the abstract semantics, give the same results in the concrete + semantics. Moreover, agreement between frames and activation + records is preserved. *) + +Lemma frame_match_store_ok: + forall fr sp base mm ms chunk ofs v mm', + frame_match fr sp base mm ms -> + store chunk mm sp ofs v = Some mm' -> + exists ms', store chunk ms sp ofs v = Some ms'. +Proof. + intros. inversion H. + generalize (store_inv _ _ _ _ _ _ H0). intros [P [Q [R [S [x T]]]]]. + change (low (blocks mm sp)) with (low_bound mm sp) in Q. + change (high (blocks mm sp)) with (high_bound mm sp) in R. + apply store_in_bounds. + auto. + omega. + apply Zle_trans with (high_bound mm sp). + auto. rewrite H4. apply align_16_top_ge. +Qed. + +Lemma frame_match_store: + forall fr sp base mm ms chunk b ofs v mm' ms', + frame_match fr sp base mm ms -> + store chunk mm b ofs v = Some mm' -> + store chunk ms b ofs v = Some ms' -> + frame_match fr sp base mm' ms'. +Proof. + intros. inversion H. + generalize (store_inv _ _ _ _ _ _ H1). intros [A [B [C [D [x1 E]]]]]. + generalize (store_inv _ _ _ _ _ _ H0). intros [I [J [K [L [x2 M]]]]]. + generalize (low_bound_store _ _ _ _ sp _ _ H0). intro LBm. + generalize (low_bound_store _ _ _ _ sp _ _ H1). intro LBs. + generalize (high_bound_store _ _ _ _ sp _ _ H0). intro HBm. + generalize (high_bound_store _ _ _ _ sp _ _ H1). intro HBs. + apply frame_match_intro; auto. + eapply valid_block_store; eauto. + congruence. congruence. congruence. + rewrite E. unfold update. case (zeq sp b); intro. + subst b. red; simpl. + apply store_contentmap_outside_agree; auto. + right. unfold low_bound in H3. omega. + assumption. + rewrite HBs; rewrite E; rewrite M; unfold update. + case (zeq sp b); intro. + subst b. red; simpl. + apply store_contentmap_agree; auto. + assumption. +Qed. + +(** Memory allocation of the Cminor stack data block (in the abstract + semantics) and of the whole activation record (in the concrete + semantics) return memory states that agree according to [frame_match]. + Moreover, [frame_match] relations over already allocated blocks + remain true. *) + +Lemma frame_match_new: + forall mm ms mm' ms' sp sp' f, + mm.(nextblock) = ms.(nextblock) -> + alloc mm 0 f.(fn_stacksize) = (mm', sp) -> + alloc ms (- f.(fn_framesize)) (align_16_top (- f.(fn_framesize)) f.(fn_stacksize)) = (ms', sp') -> + f.(fn_framesize) >= 0 -> + f.(fn_framesize) <= -Int.min_signed -> + frame_match (init_frame f) sp (Int.repr (-f.(fn_framesize))) mm' ms'. +Proof. + intros. + injection H0; intros. injection H1; intros. + assert (sp = sp'). congruence. rewrite <- H8 in H6. subst sp'. + generalize (low_bound_alloc _ _ sp _ _ _ H0). rewrite zeq_true. intro LBm. + generalize (low_bound_alloc _ _ sp _ _ _ H1). rewrite zeq_true. intro LBs. + generalize (high_bound_alloc _ _ sp _ _ _ H0). rewrite zeq_true. intro HBm. + generalize (high_bound_alloc _ _ sp _ _ _ H1). rewrite zeq_true. intro HBs. + apply frame_match_intro; auto. + eapply valid_new_block; eauto. + simpl. congruence. + simpl. omega. + simpl. omega. + rewrite <- H7. simpl. rewrite H6; simpl. rewrite update_s. + hnf; simpl; auto. + rewrite HBs; rewrite <- H5; simpl; rewrite H4; rewrite <- H7; simpl; rewrite H6; simpl; + repeat (rewrite update_s). + hnf; simpl; auto. +Qed. + +Lemma frame_match_alloc: + forall mm ms fr sp base lom him los his mm' ms' bm bs, + mm.(nextblock) = ms.(nextblock) -> + frame_match fr sp base mm ms -> + alloc mm lom him = (mm', bm) -> + alloc ms los his = (ms', bs) -> + frame_match fr sp base mm' ms'. +Proof. + intros. inversion H0. + assert (sp <> bm). + apply valid_not_valid_diff with mm. + red. rewrite H. exact H3. + eapply fresh_block_alloc; eauto. + assert (sp <> bs). + apply valid_not_valid_diff with ms. auto. + eapply fresh_block_alloc; eauto. + generalize (low_bound_alloc _ _ sp _ _ _ H1). + rewrite zeq_false; auto; intro LBm. + generalize (low_bound_alloc _ _ sp _ _ _ H2). + rewrite zeq_false; auto; intro LBs. + generalize (high_bound_alloc _ _ sp _ _ _ H1). + rewrite zeq_false; auto; intro HBm. + generalize (high_bound_alloc _ _ sp _ _ _ H2). + rewrite zeq_false; auto; intro HBs. + apply frame_match_intro. + eapply valid_block_alloc; eauto. + congruence. congruence. congruence. auto. auto. auto. + injection H2; intros. rewrite <- H20; simpl; rewrite H19; simpl. + rewrite update_o; auto. + rewrite HBs; + injection H2; intros. rewrite <- H20; simpl; rewrite H19; simpl. + injection H1; intros. rewrite <- H22; simpl; rewrite H21; simpl. + repeat (rewrite update_o; auto). +Qed. + +(** [frame_match] relations are preserved by freeing a block + other than the one pointed to by [sp]. *) + +Lemma frame_match_free: + forall fr sp base mm ms b, + frame_match fr sp base mm ms -> + sp <> b -> + frame_match fr sp base (free mm b) (free ms b). +Proof. + intros. inversion H. + generalize (low_bound_free mm _ _ H0); intro LBm. + generalize (low_bound_free ms _ _ H0); intro LBs. + generalize (high_bound_free mm _ _ H0); intro HBm. + generalize (high_bound_free ms _ _ H0); intro HBs. + apply frame_match_intro; auto. + congruence. congruence. congruence. + unfold free; simpl. rewrite update_o; auto. + rewrite HBs. + unfold free; simpl. repeat (rewrite update_o; auto). +Qed. + +(** ** Agreement for all the frames in a call stack *) + +(** We need to reason about all the frames and activation records + active at any given time during the executions: not just + about those for the currently executing function, but also + those for the callers. These collections of + active frames are called ``call stacks''. They are lists + of triples representing a frame and a stack pointer + (reference and offset) in the abstract semantics. *) + +Definition callstack : Set := list (frame * block * int). + +(** The correct linking of frames (each frame contains a pointer + to the frame of its caller at the lowest offset) is captured + by the following predicate. *) + +Inductive callstack_linked: callstack -> Prop := + | callstack_linked_nil: + callstack_linked nil + | callstack_linked_one: + forall fr1 sp1 base1, + callstack_linked ((fr1, sp1, base1) :: nil) + | callstack_linked_cons: + forall fr1 sp1 base1 fr2 base2 sp2 cs, + get_slot fr1 Tint 0 (Vptr sp2 base2) -> + callstack_linked ((fr2, sp2, base2) :: cs) -> + callstack_linked ((fr1, sp1, base1) :: (fr2, sp2, base2) :: cs). + +(** [callstack_dom cs b] (read: call stack [cs] is ``dominated'' + by block reference [b]) means that the stack pointers in [cs] + strictly decrease and are all below [b]. This ensures that + the stack block for a function was allocated after that for its + callers. A consequence is that no two active functions share + the same stack block. *) + +Inductive callstack_dom: callstack -> block -> Prop := + | callstack_dom_nil: + forall b, callstack_dom nil b + | callstack_dom_cons: + forall fr sp base cs b, + sp < b -> + callstack_dom cs sp -> + callstack_dom ((fr, sp, base) :: cs) b. + +Lemma callstack_dom_less: + forall cs b, callstack_dom cs b -> + forall fr sp base, In (fr, sp, base) cs -> sp < b. +Proof. + induction 1. simpl. tauto. + simpl. intros fr0 sp0 base0 [A|B]. + injection A; intros; subst fr0; subst sp0; subst base0. auto. + apply Zlt_trans with sp. eapply IHcallstack_dom; eauto. auto. +Qed. + +Lemma callstack_dom_diff: + forall cs b, callstack_dom cs b -> + forall fr sp base, In (fr, sp, base) cs -> sp <> b. +Proof. + intros. apply Zlt_not_eq. eapply callstack_dom_less; eauto. +Qed. + +Lemma callstack_dom_incr: + forall cs b, callstack_dom cs b -> + forall b', b < b' -> callstack_dom cs b'. +Proof. + induction 1; intros. + apply callstack_dom_nil. + apply callstack_dom_cons. omega. auto. +Qed. + +(** Every block reference is either ``in'' a call stack (that is, + refers to the stack block of one of the calls) or ``not in'' + a call stack (that is, differs from all the stack blocks). *) + +Inductive notin_callstack: block -> callstack -> Prop := + | notin_callstack_nil: + forall b, notin_callstack b nil + | notin_callstack_cons: + forall b fr sp base cs, + b <> sp -> + notin_callstack b cs -> + notin_callstack b ((fr, sp, base) :: cs). + +Lemma in_or_notin_callstack: + forall b cs, + (exists fr, exists base, In (fr, b, base) cs) \/ notin_callstack b cs. +Proof. + induction cs. + right; constructor. + elim IHcs. + intros [fr [base IN]]. left. exists fr; exists base; auto with coqlib. + intro NOTIN. destruct a. destruct p. case (eq_block b b0); intro. + left. exists f; exists i. subst b0. auto with coqlib. + right. apply notin_callstack_cons; auto. +Qed. + +(** [callstack_invariant cs mm ms] relates the memory state [mm] + from the abstract semantics with the memory state [ms] from the + concrete semantics, relative to the current call stack [cs]. + Five conditions are enforced: +- All frames in [cs] agree with the corresponding activation records + (in the sense of [frame_match]). +- The frames in the call stack are properly linked. +- Memory blocks that are not activation records for active function + calls are exactly identical in [mm] and [ms]. +- The allocation pointers are the same in [mm] and [ms]. +- The call stack [cs] is ``dominated'' by this allocation pointer, + implying that all activation records are valid, allocated blocks, + pairwise disjoint, and they were allocated in the order implied + by [cs]. *) + +Record callstack_invariant (cs: callstack) (mm ms: mem) : Prop := + mk_callstack_invariant { + cs_frame: + forall fr sp base, + In (fr, sp, base) cs -> frame_match fr sp base mm ms; + cs_linked: + callstack_linked cs; + cs_others: + forall b, notin_callstack b cs -> + mm.(blocks) b = ms.(blocks) b; + cs_next: + mm.(nextblock) = ms.(nextblock); + cs_dom: + callstack_dom cs ms.(nextblock) + }. + +(** Again, while [callstack_invariant] is presented as a relation, + it actually fully determines the concrete memory state [ms] + from the abstract memory state [mm] and the call stack [cs]. *) + +Lemma callstack_exten: + forall cs mm ms1 ms2, + callstack_invariant cs mm ms1 -> + callstack_invariant cs mm ms2 -> + ms1 = ms2. +Proof. + intros. inversion H; inversion H0. + apply mem_exten. + congruence. + intros. elim (in_or_notin_callstack b cs). + intros [fr [base FM]]. apply frame_match_exten with fr base mm; auto. + intro. transitivity (blocks mm b). + symmetry. auto. auto. +Qed. + +(** The following properties of [callstack_invariant] are the + building blocks for the proof of simulation. First, a [get_slot] + operation in the abstract semantics corresponds to a [sp]-relative + memory load in the concrete semantics. *) + +Lemma callstack_get_slot: + forall fr sp base cs mm ms ty ofs v, + callstack_invariant ((fr, sp, base) :: cs) mm ms -> + get_slot fr ty (Int.signed ofs) v -> + Val.has_type v ty -> + load_stack ms (Vptr sp base) ty ofs = Some v. +Proof. + intros. inversion H. + apply frame_match_get_slot with fr mm. + apply cs_frame0. apply in_eq. + auto. auto. +Qed. + +(** Similarly, a [get_parent] operation corresponds to loading + the back-link from the current activation record, then loading + from this back-link. *) + +Lemma callstack_get_parent: + forall fr1 sp1 base1 fr2 sp2 base2 cs mm ms ty ofs v, + callstack_invariant ((fr1, sp1, base1) :: (fr2, sp2, base2) :: cs) mm ms -> + get_slot fr2 ty (Int.signed ofs) v -> + Val.has_type v ty -> + load_stack ms (Vptr sp1 base1) Tint (Int.repr 0) = Some (Vptr sp2 base2) /\ + load_stack ms (Vptr sp2 base2) ty ofs = Some v. +Proof. + intros. inversion H. split. + inversion cs_linked0. + apply frame_match_get_slot with fr1 mm. + apply cs_frame0. auto with coqlib. + rewrite Int.signed_repr. auto. compute. intuition congruence. + exact I. + apply frame_match_get_slot with fr2 mm. + apply cs_frame0. auto with coqlib. + auto. auto. +Qed. + +(** A memory load valid in the abstract semantics can also be performed + in the concrete semantics. *) + +Lemma callstack_load: + forall cs chunk mm ms a v, + callstack_invariant cs mm ms -> + loadv chunk mm a = Some v -> + loadv chunk ms a = Some v. +Proof. + unfold loadv. intros. destruct a; try discriminate. + inversion H. + elim (in_or_notin_callstack b cs). + intros [fr [base IN]]. apply frame_match_load with fr base mm; auto. + intro. rewrite <- H0. unfold load. + rewrite (cs_others0 b H1). rewrite cs_next0. reflexivity. +Qed. + +(** A [set_slot] operation in the abstract semantics corresponds + to a [sp]-relative memory store in the concrete semantics. + Moreover, the property [callstack_invariant] still holds for + the final call stacks and memory states. *) + +Lemma callstack_set_slot: + forall fr sp base cs mm ms ty ofs v fr', + callstack_invariant ((fr, sp, base) :: cs) mm ms -> + set_slot fr ty (Int.signed ofs) v fr' -> + 4 <= Int.signed ofs -> + exists ms', + store_stack ms (Vptr sp base) ty ofs v = Some ms' /\ + callstack_invariant ((fr', sp, base) :: cs) mm ms'. +Proof. + intros. inversion H. + assert (frame_match fr sp base mm ms). apply cs_frame0. apply in_eq. + generalize (frame_match_set_slot _ _ _ _ _ _ _ _ _ H2 H0). + intros [ms' [SS FM]]. + generalize (store_inv _ _ _ _ _ _ SS). intros [A [B [C [D [x E]]]]]. + exists ms'. + split. auto. + constructor. + (* cs_frame *) + intros. elim H3; intros. + injection H4; intros; clear H4. + subst fr0; subst sp0; subst base0. auto. + apply frame_match_store_stack_other with ms sp base ty ofs v. + apply cs_frame0. auto with coqlib. auto. + apply callstack_dom_diff with cs fr0 base0. inversion cs_dom0; auto. auto. + (* cs_linked *) + inversion cs_linked0. apply callstack_linked_one. + apply callstack_linked_cons. + eapply slot_gso; eauto. + auto. + (* cs_others *) + intros. inversion H3. + rewrite E; simpl. rewrite update_o; auto. apply cs_others0. + constructor; auto. + (* cs_next *) + congruence. + (* cs_dom *) + inversion cs_dom0. constructor. rewrite D; auto. auto. +Qed. + +(** A memory store in the abstract semantics can also be performed + in the concrete semantics. Moreover, the property + [callstack_invariant] is preserved. *) + +Lemma callstack_store_aux: + forall cs mm ms chunk b ofs v mm' ms', + callstack_invariant cs mm ms -> + store chunk mm b ofs v = Some mm' -> + store chunk ms b ofs v = Some ms' -> + callstack_invariant cs mm' ms'. +Proof. + intros. inversion H. + generalize (store_inv _ _ _ _ _ _ H0). intros [A [B [C [D [x E]]]]]. + generalize (store_inv _ _ _ _ _ _ H1). intros [P [Q [R [S [y T]]]]]. + constructor; auto. + (* cs_frame *) + intros. eapply frame_match_store; eauto. + (* cs_others *) + intros. generalize (cs_others0 b0 H2); intro. + rewrite E; rewrite T; unfold update. + case (zeq b0 b); intro. + subst b0. + generalize x; generalize y. rewrite H3. + intros. replace y0 with x0. reflexivity. apply proof_irrelevance. + auto. + (* cs_nextblock *) + congruence. + (* cs_dom *) + rewrite S. auto. +Qed. + +Lemma callstack_store_ok: + forall cs mm ms chunk b ofs v mm', + callstack_invariant cs mm ms -> + store chunk mm b ofs v = Some mm' -> + exists ms', store chunk ms b ofs v = Some ms'. +Proof. + intros. inversion H. + elim (in_or_notin_callstack b cs). + intros [fr [base IN]]. + apply frame_match_store_ok with fr base mm mm'; auto. + intro. generalize (cs_others0 b H1). intro. + generalize (store_inv _ _ _ _ _ _ H0). + rewrite cs_next0; rewrite H2. intros [A [B [C [D [x E]]]]]. + apply store_in_bounds; auto. +Qed. + +Lemma callstack_store: + forall cs mm ms chunk a v mm', + callstack_invariant cs mm ms -> + storev chunk mm a v = Some mm' -> + exists ms', + storev chunk ms a v = Some ms' /\ + callstack_invariant cs mm' ms'. +Proof. + unfold storev; intros. destruct a; try discriminate. + generalize (callstack_store_ok _ _ _ _ _ _ _ _ H H0). + intros [ms' STORE]. + exists ms'. split. auto. eapply callstack_store_aux; eauto. +Qed. + +(** At function entry, a new frame is pushed on the call stack, + and memory blocks are allocated in both semantics. Moreover, + the back link to the caller's activation record is set up + in the concrete semantics. All this preserves [callstack_invariant]. *) + +Lemma callstack_function_entry: + forall fr0 sp0 base0 cs mm ms f fr mm' sp ms' sp', + callstack_invariant ((fr0, sp0, base0) :: cs) mm ms -> + alloc mm 0 f.(fn_stacksize) = (mm', sp) -> + alloc ms (- f.(fn_framesize)) (align_16_top (- f.(fn_framesize)) f.(fn_stacksize)) = (ms', sp') -> + f.(fn_framesize) >= 0 -> + f.(fn_framesize) <= -Int.min_signed -> + set_slot (init_frame f) Tint 0 (Vptr sp0 base0) fr -> + let base := Int.repr (-f.(fn_framesize)) in + exists ms'', + store_stack ms' (Vptr sp base) Tint (Int.repr 0) (Vptr sp0 base0) = Some ms'' + /\ callstack_invariant ((fr, sp, base) :: (fr0, sp0, base0) :: cs) mm' ms'' + /\ sp' = sp. +Proof. + assert (ZERO: 0 = Int.signed (Int.repr 0)). + rewrite Int.signed_repr. auto. compute; intuition congruence. + intros. inversion H. + injection H0; intros. injection H1; intros. + assert (sp' = sp). congruence. rewrite H9 in H7. subst sp'. + assert (frame_match (init_frame f) sp base mm' ms'). + unfold base. eapply frame_match_new; eauto. + rewrite ZERO in H4. + generalize (frame_match_set_slot _ _ _ _ _ _ _ _ _ H9 H4). + intros [ms'' [SS FM]]. + generalize (store_inv _ _ _ _ _ _ SS). + intros [A [B [C [D [x E]]]]]. + exists ms''. split; auto. split. + constructor. + (* cs_frame *) + intros. elim H10; intro. + injection H11; intros; clear H11. + subst fr1; subst sp1; subst base1. auto. + eapply frame_match_store_stack_other; eauto. + eapply frame_match_alloc; [idtac|idtac|eexact H0|eexact H1]. + congruence. eapply cs_frame; eauto with coqlib. + rewrite <- H7. eapply callstack_dom_diff; eauto with coqlib. + (* cs_linked *) + constructor. rewrite ZERO. eapply slot_gss; eauto. auto. + (* cs_others *) + intros. inversion H10. + rewrite E; rewrite update_o; auto. + rewrite <- H6; rewrite <- H8; simpl; rewrite H5; rewrite H7; simpl. + repeat (rewrite update_o; auto). + (* cs_next *) + rewrite D. rewrite <- H6; rewrite <- H8; simpl. congruence. + (* cs_dom *) + constructor. rewrite D; auto. rewrite <- H7. auto. + auto. +Qed. + +(** At function return, the memory blocks corresponding to Cminor + stack data and activation record for the function are freed. + This preserves [callstack_invariant]. *) + +Lemma callstack_function_return: + forall fr sp base cs mm ms, + callstack_invariant ((fr, sp, base) :: cs) mm ms -> + callstack_invariant cs (free mm sp) (free ms sp). +Proof. + intros. inversion H. inversion cs_dom0. + constructor. + (* cs_frame *) + intros. apply frame_match_free. apply cs_frame0; auto with coqlib. + apply callstack_dom_diff with cs fr1 base1. auto. auto. + (* cs_linked *) + inversion cs_linked0. apply callstack_linked_nil. auto. + (* cs_others *) + intros. + unfold free; simpl; unfold update. + case (zeq b0 sp); intro. + auto. + apply cs_others0. apply notin_callstack_cons; auto. + (* cs_nextblock *) + simpl. auto. + (* cs_dom *) + simpl. apply callstack_dom_incr with sp; auto. +Qed. + +(** Finally, [callstack_invariant] holds for the initial memory states + in the two semantics. *) + +Lemma callstack_init: + forall (p: program), + callstack_invariant ((empty_frame, nullptr, Int.zero) :: nil) + (Genv.init_mem p) (Genv.init_mem p). +Proof. + intros. + generalize (Genv.initmem_nullptr p). intros [A B]. + constructor. + (* cs_frame *) + intros. elim H; intro. + injection H0; intros; subst fr; subst sp; subst base. + constructor. + assumption. + unfold low_bound. rewrite B. reflexivity. + unfold low_bound, empty_frame. rewrite B. reflexivity. + unfold high_bound. rewrite B. reflexivity. + simpl. omega. + simpl. compute. intuition congruence. + reflexivity. + rewrite B. unfold empty_frame. simpl. hnf. auto. + rewrite B. hnf. auto. + elim H0. + (* cs_linked *) + apply callstack_linked_one. + (* cs_others *) + auto. + (* cs_nextblock *) + reflexivity. + (* cs_dom *) + constructor. exact A. constructor. +Qed. + +(** * The proof of simulation *) + +Section SIMULATION. + +Variable p: program. +Hypothesis wt_p: wt_program p. +Let ge := Genv.globalenv p. + +(** The proof of simulation relies on diagrams of the following form: +<< + sp, parent, c, rs, fr, mm ----------- sp, c, rs, ms + | | + | | + v v + sp, parent, c', rs', fr', mm' -------- sp, c', rs', ms' +>> + The left vertical arrow is a transition in the abstract semantics. + The right vertical arrow is a transition in the concrete semantics. + The precondition (top horizontal line) is the appropriate + [callstack_invariant] property between the initial memory states + [mm] and [ms] and any call stack with [fr] as top frame and + [parent] as second frame. In addition, well-typedness conditions + over the code [c], the register [rs] and the frame [fr] are demanded. + The postcondition (bottom horizontal line) is [callstack_invariant] + between the final memory states [mm'] and [ms'] and the final + call stack. +*) + +Definition exec_instr_prop + (f: function) (sp: val) (parent: frame) + (c: code) (rs: regset) (fr: frame) (mm: mem) + (c': code) (rs': regset) (fr': frame) (mm': mem) : Prop := + forall ms stk base pstk pbase cs + (WTF: wt_function f) + (INCL: incl c f.(fn_code)) + (WTRS: wt_regset rs) + (WTFR: wt_frame fr) + (WTPA: wt_frame parent) + (CSI: callstack_invariant ((fr, stk, base) :: (parent, pstk, pbase) :: cs) mm ms) + (SP: sp = Vptr stk base), + exists ms', + exec_instr ge f sp c rs ms c' rs' ms' /\ + callstack_invariant ((fr', stk, base) :: (parent, pstk, pbase) :: cs) mm' ms'. + +Definition exec_instrs_prop + (f: function) (sp: val) (parent: frame) + (c: code) (rs: regset) (fr: frame) (mm: mem) + (c': code) (rs': regset) (fr': frame) (mm': mem) : Prop := + forall ms stk base pstk pbase cs + (WTF: wt_function f) + (INCL: incl c f.(fn_code)) + (WTRS: wt_regset rs) + (WTFR: wt_frame fr) + (WTPA: wt_frame parent) + (CSI: callstack_invariant ((fr, stk, base) :: (parent, pstk, pbase) :: cs) mm ms) + (SP: sp = Vptr stk base), + exists ms', + exec_instrs ge f sp c rs ms c' rs' ms' /\ + callstack_invariant ((fr', stk, base) :: (parent, pstk, pbase) :: cs) mm' ms'. + +Definition exec_function_body_prop + (f: function) (parent: frame) (link ra: val) + (rs: regset) (mm: mem) + (rs': regset) (mm': mem) : Prop := + forall ms pstk pbase cs + (WTF: wt_function f) + (WTRS: wt_regset rs) + (WTPA: wt_frame parent) + (WTRA: Val.has_type ra Tint) + (LINK: link = Vptr pstk pbase) + (CSI: callstack_invariant ((parent, pstk, pbase) :: cs) mm ms), + exists ms', + exec_function_body ge f (Vptr pstk pbase) ra rs ms rs' ms' /\ + callstack_invariant ((parent, pstk, pbase) :: cs) mm' ms'. + +Definition exec_function_prop + (f: function) (parent: frame) + (rs: regset) (mm: mem) + (rs': regset) (mm': mem) : Prop := + forall ms pstk pbase cs + (WTF: wt_function f) + (WTRS: wt_regset rs) + (WTPA: wt_frame parent) + (CSI: callstack_invariant ((parent, pstk, pbase) :: cs) mm ms), + exists ms', + exec_function ge f (Vptr pstk pbase) rs ms rs' ms' /\ + callstack_invariant ((parent, pstk, pbase) :: cs) mm' ms'. + +Lemma exec_function_equiv: + forall f parent rs mm rs' mm', + Machabstr.exec_function ge f parent rs mm rs' mm' -> + exec_function_prop f parent rs mm rs' mm'. +Proof. + apply (Machabstr.exec_function_ind4 ge + exec_instr_prop + exec_instrs_prop + exec_function_body_prop + exec_function_prop); + intros; red; intros. + + (* Mlabel *) + exists ms. split. constructor. auto. + (* Mgetstack *) + exists ms. split. + constructor. rewrite SP. eapply callstack_get_slot; eauto. + apply wt_get_slot with fr (Int.signed ofs); auto. + auto. + (* Msetstack *) + generalize (wt_function_instrs f WTF _ (INCL _ (in_eq _ _))). + intro WTI. inversion WTI. + assert (4 <= Int.signed ofs). omega. + generalize (callstack_set_slot _ _ _ _ _ _ _ _ _ _ CSI H H5). + intros [ms' [STO CSI']]. + exists ms'. split. constructor. rewrite SP. auto. auto. + (* Mgetparam *) + exists ms. split. + assert (WTV: Val.has_type v ty). eapply wt_get_slot; eauto. + generalize (callstack_get_parent _ _ _ _ _ _ _ _ _ _ _ _ + CSI H WTV). + intros [L1 L2]. + eapply exec_Mgetparam. rewrite SP; eexact L1. eexact L2. + auto. + (* Mop *) + exists ms. split. constructor. auto. auto. + (* Mload *) + exists ms. split. econstructor. eauto. eapply callstack_load; eauto. + auto. + (* Mstore *) + generalize (callstack_store _ _ _ _ _ _ _ CSI H0). + intros [ms' [STO CSI']]. + exists ms'. split. econstructor. eauto. auto. + auto. + (* Mcall *) + red in H1. + assert (WTF': wt_function f'). + destruct ros; simpl in H. + apply (Genv.find_funct_prop wt_function wt_p H). + destruct (Genv.find_symbol ge i); try discriminate. + apply (Genv.find_funct_ptr_prop wt_function wt_p H). + generalize (H1 _ _ _ _ WTF' WTRS WTFR CSI). + intros [ms' [EXF CSI']]. + exists ms'. split. apply exec_Mcall with f'; auto. + rewrite SP. auto. + auto. + (* Mgoto *) + exists ms. split. constructor; auto. auto. + (* Mcond *) + exists ms. split. apply exec_Mcond_true; auto. auto. + exists ms. split. apply exec_Mcond_false; auto. auto. + + (* refl *) + exists ms. split. apply exec_refl. auto. + (* one *) + generalize (H0 _ _ _ _ _ _ WTF INCL WTRS WTFR WTPA CSI SP). + intros [ms' [EX CSI']]. + exists ms'. split. apply exec_one; auto. auto. + (* trans *) + generalize (subject_reduction_instrs p wt_p + _ _ _ _ _ _ _ _ _ _ _ H WTF INCL WTRS WTFR WTPA). + intros [INCL2 [WTRS2 WTFR2]]. + generalize (H0 _ _ _ _ _ _ WTF INCL WTRS WTFR WTPA CSI SP). + intros [ms1 [EX1 CSI1]]. + generalize (H2 _ _ _ _ _ _ WTF INCL2 WTRS2 WTFR2 WTPA CSI1 SP). + intros [ms2 [EX2 CSI2]]. + exists ms2. split. eapply exec_trans; eauto. auto. + + (* function body *) + caseEq (alloc ms (- f.(fn_framesize)) + (align_16_top (- f.(fn_framesize)) f.(fn_stacksize))). + intros ms1 stk1 ALL. + subst link. + assert (FS: f.(fn_framesize) >= 0). + generalize (wt_function_framesize f WTF). omega. + generalize (callstack_function_entry _ _ _ _ _ _ _ _ _ _ _ _ + CSI H ALL FS + (wt_function_no_overflow f WTF) H0). + intros [ms2 [STORELINK [CSI2 EQ]]]. + subst stk1. + assert (ZERO: Int.signed (Int.repr 0) = 0). reflexivity. + assert (FOUR: Int.signed (Int.repr 4) = 4). reflexivity. + assert (BND: 4 <= Int.signed (Int.repr 4)). + rewrite FOUR; omega. + rewrite <- FOUR in H1. + generalize (callstack_set_slot _ _ _ _ _ _ _ _ _ _ + CSI2 H1 BND). + intros [ms3 [STORERA CSI3]]. + assert (WTFR2: wt_frame fr2). + eapply wt_set_slot; eauto. eapply wt_set_slot; eauto. + red. intros. simpl. auto. + exact I. + red in H3. + generalize (H3 _ _ _ _ _ _ WTF (incl_refl _) WTRS WTFR2 WTPA + CSI3 (refl_equal _)). + intros [ms4 [EXEC CSI4]]. + generalize (exec_instrs_link_invariant _ _ _ _ _ _ _ _ _ _ _ _ + H2 WTF (incl_refl _)). + intros [INCL LINKINV]. + exists (free ms4 stk). split. + eapply exec_funct_body; eauto. + eapply callstack_get_slot. eexact CSI4. + apply LINKINV. rewrite ZERO. omega. + eapply slot_gso; eauto. rewrite ZERO. eapply slot_gss; eauto. + exact I. + eapply callstack_get_slot. eexact CSI4. + apply LINKINV. rewrite FOUR. omega. eapply slot_gss; eauto. auto. + eapply callstack_function_return; eauto. + + (* function *) + generalize (H0 (Vptr pstk pbase) Vzero I I + ms pstk pbase cs WTF WTRS WTPA I (refl_equal _) CSI). + intros [ms' [EXEC CSI']]. + exists ms'. split. constructor. intros. + generalize (H0 (Vptr pstk pbase) ra I H1 + ms pstk pbase cs WTF WTRS WTPA H1 (refl_equal _) CSI). + intros [ms1 [EXEC1 CSI1]]. + rewrite (callstack_exten _ _ _ _ CSI' CSI1). auto. + auto. +Qed. + +End SIMULATION. + +Theorem exec_program_equiv: + forall p r, + wt_program p -> + Machabstr.exec_program p r -> + Mach.exec_program p r. +Proof. + intros p r WTP [fptr [f [rs [mm [FINDPTR [FINDF [SIG [EXEC RES]]]]]]]]. + assert (WTF: wt_function f). + apply (Genv.find_funct_ptr_prop wt_function WTP FINDF). + assert (WTRS: wt_regset (Regmap.init Vundef)). + red; intros. rewrite Regmap.gi; simpl; auto. + assert (WTFR: wt_frame empty_frame). + red; intros. simpl. auto. + generalize (exec_function_equiv p WTP + f empty_frame + (Regmap.init Vundef) (Genv.init_mem p) rs mm + EXEC (Genv.init_mem p) nullptr Int.zero nil + WTF WTRS WTFR (callstack_init p)). + intros [ms' [EXEC' CSI]]. + red. exists fptr; exists f; exists rs; exists ms'. + intuition. rewrite SIG in RES. exact RES. +Qed. -- cgit