Library multi_one_s_simu

From mathcomp Require Import ssreflect ssrfun ssrbool eqtype ssrnat seq.
Require Import ssrZ ZArith_ext seq_ext uniq_tac.
Require Import machine_int multi_int encode_decode integral_type.
Import MachineInt.
Require Import mips_bipl mips_cmd mips_tactics mips_syntax mips_mint.
Import mips_bipl.expr_m.
Require Import simu.
Import simu.simu_m.
Require Import multi_one_s_prg multi_one_s_triple.

Local Open Scope heap_scope.
Local Open Scope assoc_scope.
Local Open Scope uniq_scope.
Local Open Scope machine_int_scope.
Local Open Scope zarith_ext_scope.
Local Open Scope simu_scope.

x <- 1, x signed

Lemma pfwd_sim_one_s (x : bipl.var.v) d k (rx rk a0 a1 a2 a3 : reg) :
  uniq(rx, rk, a0, a1, a2, a3, r0) ->
  disj (mints_regs (assoc.cdom d)) (a0 :: a1 :: a2 :: a3 :: nil) ->
  x \notin assoc.dom d ->
  signed k rx \notin assoc.cdom d ->
  (x <- nat_e 1)%pseudo_expr%pseudo_cmd
    <=p( state_mint (x |=> signed k rx \U+ d), (fun s st h =>
    0 < u2Z ([rk]_st) < 2 ^^ 31 /\
    k = '|u2Z ([rk ]_ st)|)%asm_expr)
  multi_one_s rx rk a0 a1 a2 a3.
Proof.
move=> Hnodup Hdisj x_d k_d.
rewrite /pfwd_sim.
move=> s st h [s_st_h [k_bound k_rk]] s' exec_pseudo st' h' exec_asm.
have rx_st_st' : ([ rx ]_ st = [ rx ]_ st')%asm_expr.
  mips_syntax.Reg_unchanged; simpl mips_frame.modified_regs; by Uniq_not_In.
have rk_st_st' : ([ rk ]_ st = [ rk ]_ st')%asm_expr.
  mips_syntax.Reg_unchanged; simpl mips_frame.modified_regs; by Uniq_not_In.

have : uniq(rx,rk,a0,a1,a2,a3,r0) by Uniq_uniq r0.
move/(multi_one_s_triple rx rk a0 a1 a2 a3).
move/(_ ('|u2Z ([rk ]_ st)%asm_expr|)).
rewrite Z_of_nat_Zabs_nat; last exact/min_u2Z.

move/(state_mint_var_mint _ _ _ _ x (signed k rx)) : (s_st_h).
rewrite assoc.get_union_sing_eq.
move/(_ refl_equal).
case => slen ptr A rx_fit [A_k Hslen slen_x x_A] ptr_fit Hmem.

move/(_ A).


move/(_ ([rx ]_ st)%asm_expr ptr k_bound).
rewrite A_k.
move/(_ k_rk).
rewrite k_rk in ptr_fit.
rewrite Z_of_nat_Zabs_nat in ptr_fit; last exact/min_u2Z.
move/(_ rx_fit ptr_fit).
rewrite k_rk Z_of_nat_Zabs_nat in Hslen; last exact/min_u2Z.
move/(_ slen Hslen) => Hhoare_triple.


have [st'' [h'' Hexec_triple_proj]] : exists st'' h'',
  (Some (st, heap.proj h (heap.dom (heap_mint (signed k rx) st h))) --
     multi_one_s rx rk a0 a1 a2 a3 --->
     Some (st'', h''))%mips_cmd.
  exists st', (heap.proj h' (heap.dom (heap_mint (signed k rx) st h))).
  apply: (mips_syntax.triple_exec_proj _ _ _ Hhoare_triple (heap.dom (heap_mint (signed k rx) st h)) st h st' h').
  split; first by reflexivity.
  split; first by reflexivity.
  suff : h |P| heap.dom (heap_mint (signed k rx) st h) = heap_mint (signed k rx) st h by move=> ->.
  by apply heap.incluE, heap_inclu_heap_mint_signed.
  by [].


set postcond := (fun s h => _ /\ _ /\ (_ ** (_ |--> one32 :: _)) s h)%asm_assert in Hhoare_triple.

have {Hhoare_triple} Hhoare_triple_post_condition : (postcond **
assert_m.TT)%asm_expr%asm_assert st' h'.
  move: (mips_frame.frame_rule_R _ _ _ Hhoare_triple assert_m.TT (assert_m.inde_TT _) (mips_frame.inde_cmd_mult_TT _)).
  move/mips_seplog.hoare_prop_m.soundness.
  rewrite /while.hoare_semantics.
  move/(_ st h) => Hhoare_triple_sem.
  lapply Hhoare_triple_sem; last first.
    exists (heap_mint (signed k rx) st h), (h \D\ heap.dom (heap_mint (signed k rx) st h)).
    split; first by apply heap.disj_difs', seq_ext.inc_refl.
    split => //.
    apply heap.union_difsK => //; exact/heap_inclu_heap_mint_signed.
  move=> {Hhoare_triple_sem}[Hhoare_triple_sem Hhoare_triple_sem'].
  by apply Hhoare_triple_sem' in exec_asm.

rewrite /state_mint; split.
- move=> x0 mx0 x0_mx0.
  case/assoc.get_union_Some_inv : x0_mx0 => x0_mx0.
  + case/assoc.get_sing_inv : x0_mx0 => ? ?; subst x0 mx0.
    case: Hhoare_triple_post_condition => [h1 [h2 [h1_d_h2 [h1_U_h2 [[Hrx [Hrk Hh1]] Hh2]]]]].
    have Zsgn_nk : sgZ (Z_of_nat k) = 1.
      destruct k => //=.
      symmetry in k_rk.
      apply Zabs_nat_0_inv in k_rk.
      lia.
    destruct k as [|k].
      symmetry in k_rk.
      apply Zabs_nat_0_inv in k_rk.
      rewrite k_rk in k_bound.
      by case: k_bound.
    apply mkVarSigned with (Z2s 32 (Z_of_nat (S k))) ptr (one32 :: nseq ('|u2Z ([rk ]_ st)%asm_expr| - 1) zero32) => //.
    - by rewrite Hrx.
    - apply mkSignMagn => //.
      * by rewrite /= size_nseq -k_rk /= subn1.
      * rewrite Z2sK; last first.
          rewrite k_rk Z_of_nat_Zabs_nat; last exact: min_u2Z.
          lia.
        rewrite Zsgn_nk; ring.
      * rewrite Z2sK; last first.
          rewrite k_rk Z_of_nat_Zabs_nat; last exact: min_u2Z.
          lia.
        move/syntax_m.seplog_m.semop_prop_m.exec_cmd0_inv : exec_pseudo.
        case/syntax_m.seplog_m.exec0_assign_inv => _ -> /=.
        by syntax_m.seplog_m.assert_m.expr_m.Store_upd.
      * rewrite Z2sK; last first.
          rewrite k_rk Z_of_nat_Zabs_nat; last exact: min_u2Z.
          lia.
        move/syntax_m.seplog_m.semop_prop_m.exec_cmd0_inv : exec_pseudo.
        case/syntax_m.seplog_m.exec0_assign_inv => _ ->.
        syntax_m.seplog_m.assert_m.expr_m.Store_upd.
        by rewrite [sgZ _]/= Zmult_1_l lSum_S Z2uK // lSum_nseq_0 mulZ0.
    - rewrite k_rk Z_of_nat_Zabs_nat //; exact: min_u2Z.
    - apply con_heap_mint_signed_cons with h1 => //.
      + rewrite h1_U_h2.
        apply heap.inclu_union_L => //.
        exact: heap.inclu_refl.
      + by rewrite -rx_st_st'.
      + rewrite [size _]/= size_nseq.
        set xx := Z_of_nat _.
        suff : xx = u2Z ([rk ]_ st)%asm_expr by move=> ->.
        rewrite {}/xx minus_Sn_m; last by lia.
        rewrite /= -minus_n_O Z_of_nat_Zabs_nat //; exact: min_u2Z.
      + rewrite /= size_nseq subn1 prednK //; apply/ltP; lia.
      + suff : Z2u 32 (u2Z ([rk ]_ st)%asm_expr) = Z2s 32 (Z_of_nat (S k)).
          by move=> <-.
        have k_rk' : u2Z ([rk ]_ st)%asm_expr = Z_of_nat (S k).
          rewrite k_rk Z_of_nat_Zabs_nat //; exact: min_u2Z.
        rewrite Z2u_u2Z.
        apply s2Z_inj.
        rewrite Z2sK; last first.
          rewrite -k_rk'.
          clear -k_bound.
          lia.
        rewrite -k_rk' s2Z_u2Z_pos' //.
        clear -k_bound.
        rewrite [Peano.pred _]/=; lia.
  + have x0_x : x0 <> x.
      move=> ?; subst x0.
      apply assoc.get_Some_in in x0_mx0.
      rewrite -assoc.elts_dom in x_d.
      move/negP : x_d; apply.
      apply/mapP.
      by exists (x, mx0).
    move: (proj2 s_st_h _ _ x0_x mx0 (signed k rx)).
    rewrite assoc.get_union_sing_neq; last by [].
    rewrite assoc.get_union_sing_eq.
    move/(_ x0_mx0 (refl_equal _)) => heap_mint_disj.
    move: (mips_syntax.exec_deter_proj _ _ _ _ _ exec_asm _ _ _ Hexec_triple_proj) => [st'_st'' [h''_h' h_h']].
    have Hd_unchanged : forall v r, assoc.get v d = Some r ->
      disj (mint_regs r) (mips_frame.modified_regs (multi_one_s rx rk a0 a1 a2 a3)).
      move=> v r Hvr; rewrite [mips_frame.modified_regs _]/=; Disj_remove_dup.
      apply (disj_incl_LR Hdisj); last by apply incl_refl_Permutation; PermutProve.
      exact/incP/inc_mint_regs/(assoc.get_Some_in_cdom _ v).
    have <- : heap_mint mx0 st h = heap_mint mx0 st' h'.
      apply (heap_mint_state_invariant (heap_mint (signed k rx) st h) x0 s) => //.
      move=> rx0 Hrx0; Reg_unchanged.
      apply (@disj_not_In _ (mint_regs mx0)); last by [].
      exact/disj_sym/(Hd_unchanged x0).
      move: (proj1 s_st_h x0 mx0).
      rewrite assoc.get_union_sing_neq; last by [].
      by apply.
    apply var_mint_invariant with s st => //.
    move=> rx0 Hrx0; Reg_unchanged.
    apply (@disj_not_In _(mint_regs mx0)); last by [].
    apply/disj_sym/(Hd_unchanged x0) => //.
    Var_unchanged. rewrite /= mem_seq1; exact/negP/eqP.
    move: (proj1 s_st_h x0 mx0).
    rewrite assoc.get_union_sing_neq; last by [].
    by apply.
- have Hdom : heap.dom (heap_mint (signed k rx) st' h') = heap.dom (heap_mint (signed k rx) st h).
    symmetry.
    case: Hhoare_triple_post_condition => h1 [h2 [h1_d_h2 [h1_U_h2 [[_ [_ Hh1]] Hh2]]]].
    apply (dom_heap_mint_sign_state_invariant _ _ _ _ _ _ x s slen (Z2u 32 (u2Z ([rk ]_ st)%asm_expr))) => //.
      by move/assert_m.mapstos_get1/heap_get_heap_mint_inv : Hmem.
    apply assert_m.mapstos_get1 in Hh1.
    rewrite h1_U_h2; exact: heap.get_union_L.
    apply assert_m.mapstos_get2 in Hmem.
    apply assert_m.mapstos_get2 in Hh1.
    rewrite h1_U_h2.
    apply heap_get_heap_mint_inv in Hmem.
    rewrite Hmem.
    symmetry.
    exact: heap.get_union_L.
    apply mkVarSigned with slen ptr A => //.
    apply mkSignMagn => //; rewrite k_rk Z_of_nat_Zabs_nat //; exact: min_u2Z.
    rewrite k_rk Z_of_nat_Zabs_nat //; exact: min_u2Z.
    by eapply dom_heap_invariant; eauto.
  apply (state_mint_part2_one_variable _ _ _ _ _ _ _ _ s_st_h Hdom).
  + move=> t x0 Ht Hx0.
    Reg_unchanged. simpl mips_frame.modified_regs.
    case/assoc.in_cdom_union_inv : Ht => Ht.
    * rewrite assoc.cdom_sing /= seq.mem_seq1 in Ht.
      move/eqP : Ht => Ht; subst t.
      apply (@disj_not_In _(mint_regs (unsign rk rx))); last by rewrite /=; auto.
      Disj_remove_dup.
      rewrite /=.
      apply uniq_disj. rewrite [cat _ _]/=. by Uniq_uniq r0.
    * apply (@disj_not_In _ (mint_regs t)); last by [].
      Disj_remove_dup.
      apply/disj_sym/(disj_incl_LR Hdisj); last by apply incl_refl_Permutation; PermutProve.
      exact/incP/inc_mint_regs.
  + move: (mips_syntax.exec_deter_proj _ _ _ _ _ exec_asm _ _ _ Hexec_triple_proj); tauto.
  + exact: (mips_syntax.dom_heap_invariant _ _ _ _ _ exec_asm).
Qed.