Module Asmblockgenproof1


Proof of correctness for individual instructions


Require Import Coqlib Errors Maps Zbits.
Require Import AST Integers Floats Values Memory Globalenvs Linking Compopts Events.
Require Import Op Locations Machblock Conventions Lia.
Require Import Asmblock Asmblockgen Asmblockgenproof0 Asmblockprops.
Require Import OptionMonad.
Require Import MemcpyTypes.

Module MB := Machblock.
Module AB := Asmblock.

Local Open Scope option_monad_scope.
Local Opaque PregEq.eq.

Arithmetic helper used in the Ocmp Ceq clz lowering.
Lemma int_sub_eq_zero:
  forall i j, Int.eq (Int.sub i j) Int.zero = Int.eq i j.
Proof.
  intros. destruct (Int.eq i j) eqn:Hij.
  - apply Int.same_if_eq in Hij. subst. rewrite Int.sub_idem. apply Int.eq_true.
  - apply Int.eq_false. intro Hsub.
    assert (Heq: i = j).
    { apply (f_equal (fun x => Int.add x j)) in Hsub.
      rewrite Int.sub_add_opp in Hsub.
      rewrite Int.add_assoc in Hsub.
      rewrite (Int.add_commut (Int.neg j) j) in Hsub.
      rewrite Int.add_neg_zero in Hsub.
      rewrite Int.add_zero in Hsub.
      rewrite Int.add_zero_l in Hsub.
      exact Hsub. }
    rewrite Heq, Int.eq_true in Hij. discriminate.
Qed.

Useful properties of special registers.
Lemma preg_of_not_R13:
  forall r, preg_of r <> IR13.
Proof.
  destruct r; simpl; discriminate.
Qed.

Lemma ireg_of_not_R13:
  forall m r, ireg_of m = OK r -> r <> IR13.
Proof.
  unfold ireg_of. intros. destruct (preg_of m) eqn:E; inv H.
  red. intros. elim (preg_of_not_R13 m).
  destruct d. destruct i; inv H1; auto.
  all: try discriminate.
Qed.

Lemma ireg_of_not_R13':
  forall m r, ireg_of m = OK r -> DR (IR r) <> IR13.
Proof.
  intros. generalize (ireg_of_not_R13 _ _ H). congruence.
Qed.
Global Hint Resolve preg_of_not_R13 ireg_of_not_R13 ireg_of_not_R13': asmgen.

Lemma preg_of_not_R14:
  forall r, preg_of r <> IR14.
Proof.
  destruct r; simpl; discriminate.
Qed.

Lemma ireg_of_not_R14:
  forall m r, ireg_of m = OK r -> IR r <> IR IR14.
Proof.
  unfold ireg_of. intros. destruct (preg_of m) eqn:E; inv H.
  red. intros. elim (preg_of_not_R14 m).
  destruct d. destruct i; inv H1; auto.
  all: try discriminate.
Qed.

Lemma ireg_of_not_R14':
  forall m r, ireg_of m = OK r -> DR (IR r) <> IR14.
Proof.
  intros. generalize (ireg_of_not_R14 _ _ H). congruence.
Qed.
Global Hint Resolve preg_of_not_R14 ireg_of_not_R14 ireg_of_not_R14': asmgen.

Definition if_preg (r: preg) : bool :=
  match r with
  | IR _ => true
  | FR _ => true
  | CR _ => false
  | PC => false
  end.

Lemma data_if_preg: forall r, data_preg r = true -> if_preg r = true.
Proof.
  intros. destruct r. destruct d.
  all: reflexivity || discriminate.
Qed.

Lemma data_preg_not_R14: forall r, data_preg r = true -> r <> IR14.
Proof.
  intros. destruct r. destruct d. destruct i.
  all: simpl in *; congruence.
Qed.

Lemma if_preg_not_PC: forall r, if_preg r = true -> r <> PC.
Proof.
  intros; red; intros; subst; discriminate.
Qed.
Global Hint Resolve data_if_preg if_preg_not_PC: asmgen.

Lemma undef_flags_rs_eq: forall rs r,
  if_preg r = true -> undef_flags rs r = rs r.
Proof.
  intros. destruct r; reflexivity || discriminate.
Qed.

Lemma undef_flags_rs_eq': forall rs (r: ireg),
  undef_flags rs r = rs r.
Proof.
  intros. destruct r; reflexivity || discriminate.
Qed.

Useful simplification tactic

Ltac Simplif :=
  ((rewrite undef_flags_rs_eq by eauto with asmgen)
  || (rewrite undef_flags_rs_eq' by eauto with asmgen)
  || (rewrite Pregmap.gss)
  || (rewrite Pregmap.gso by eauto with asmgen)); auto with asmgen.

Ltac Simpl := repeat Simplif.

Correctness of ARM constructor functions


Section CONSTRUCTORS.

Variable ge: genv.

Local Transparent Archi.ptr64.

Lemma iterate_op_correct:
  forall op1 op2 (f: val -> int -> val) (rs: regset) (r: ireg) m v0 is_addsub n k,
  (forall (rs:regset) n,
    exec_basic ge (op2 (SOimm n)) rs m =
    Next (undef_flags rs#r <- (f (rs#r) n)) m) ->
  (forall n,
    exec_basic ge (op1 (SOimm n)) rs m =
    Next (undef_flags rs#r <- (f v0 n)) m) ->
  exists rs',
     exec_straight ge (iterate_op op1 op2 (decompose_int is_addsub n) k) rs m k rs' m
  /\ rs'#r = List.fold_left f (decompose_int is_addsub n) v0
  /\ forall r': preg, r' <> r -> if_preg r' = true -> rs'#r' = rs#r'.
Proof.
  intros until k; intros SEM2 SEM1.
  unfold iterate_op.
  destruct (decompose_int is_addsub n) as [ | i tl] eqn:DI.
  unfold decompose_int in DI. destruct (decompose_int_base is_addsub n); congruence.
  revert k. pattern tl. apply List.rev_ind.
  (* base case *)
  intros; simpl. econstructor.
  split. apply exec_straight_one. rewrite SEM1. reflexivity. intuition Simpl.
  (* inductive case *)
  intros.
  rewrite List.map_app. simpl. rewrite app_ass. simpl.
  destruct (H (op2 (SOimm x) :: k)) as [rs' [A [B C]]].
  econstructor.
  split. eapply exec_straight_trans. eexact A. apply exec_straight_one.
  rewrite SEM2. reflexivity.
  split. rewrite fold_left_app; simpl. Simpl. rewrite B. auto.
  intros; Simpl.
Qed.

Loading a constant.

Lemma loadimm_correct:
  forall r n k rs m,
  exists rs',
     exec_straight ge (loadimm r n k) rs m k rs' m
  /\ rs'#r = Vint n
  /\ forall r': preg, r' <> r -> if_preg r' = true -> rs'#r' = rs#r'.
Proof.
  intros. unfold loadimm.
  set (l1 := length (decompose_int CONST n)).
  set (l2 := length (decompose_int OTHER (Int.not n))).
  destruct (Nat.leb l1 1%nat).
{ (* single mov *)
  econstructor; split. apply exec_straight_one. simpl; reflexivity. auto.
  split; intros; Simpl. }
  destruct (Nat.leb l2 1%nat).
{ (* single movn *)
  econstructor; split. apply exec_straight_one.
  simpl. rewrite Int.not_involutive. reflexivity. auto.
  split; intros; Simpl. }
  destruct Archi.thumb2_support.
{ (* movw / movt *)
  unfold loadimm_word.
  destruct (Int.eq (Int.shru n (Int.repr 16)) Int.zero).
  { econstructor; split.
    apply exec_straight_one. simpl; eauto. auto. split; intros; Simpl. }
  { econstructor; split.
    eapply exec_straight_two. simpl; reflexivity. simpl; reflexivity. auto. auto.
    split; intros; Simpl. simpl. f_equal. rewrite Int.zero_ext_and by lia.
    rewrite Int.and_assoc. change 65535 with (two_p 16 - 1). rewrite Int.and_idem.
    apply Int.same_bits_eq; intros.
    rewrite Int.bits_or, Int.bits_and, Int.bits_shl, Int.testbit_repr by auto.
    rewrite Ztestbit_two_p_m1 by lia. change (Int.unsigned (Int.repr 16)) with 16.
    destruct (zlt i 16).
    rewrite andb_true_r, orb_false_r; auto.
    rewrite andb_false_r; simpl. rewrite Int.bits_shru by lia.
    change (Int.unsigned (Int.repr 16)) with 16. rewrite zlt_true by lia. f_equal; lia.
  }
}
  destruct (Nat.leb l1 l2).
{ (* mov - orr* *)
  replace (Vint n) with (List.fold_left (fun v i => Val.or v (Vint i)) (decompose_int CONST n) Vzero).
  apply iterate_op_correct. simpl.
  reflexivity.
  intros; simpl. rewrite Int.or_commut; rewrite Int.or_zero; auto.
  rewrite decompose_int_or. simpl. rewrite Int.or_commut; rewrite Int.or_zero; auto.
}
{ (* mvn - bic* *)
  replace (Vint n) with (List.fold_left (fun v i => Val.and v (Vint (Int.not i))) (decompose_int OTHER (Int.not n)) (Vint Int.mone)).
  apply iterate_op_correct.
  auto.
  intros. simpl. rewrite Int.and_commut; rewrite Int.and_mone; auto.
  rewrite decompose_int_bic. simpl. rewrite Int.not_involutive. rewrite Int.and_commut. rewrite Int.and_mone; auto.
}
Qed.

Add integer immediate.

Lemma addimm_correct:
  forall r1 r2 n k rs m,
  exists rs',
     exec_straight ge (addimm r1 r2 n k) rs m k rs' m
  /\ rs'#r1 = Val.add rs#r2 (Vint n)
  /\ forall r': preg, r' <> r1 -> if_preg r' = true -> rs'#r' = rs#r'.
Proof.
  intros. unfold addimm.
  destruct (Int.ltu (Int.repr (-256)) n).
  (* sub *)
  econstructor; split. apply exec_straight_one; simpl; auto.
  split; intros; Simpl. apply Val.sub_opp_add.
  destruct (Nat.leb (length (decompose_int ADDSUB n)) (length (decompose_int ADDSUB (Int.neg n)))).
  (* add - add* *)
  replace (Val.add (rs r2) (Vint n))
     with (List.fold_left (fun v i => Val.add v (Vint i)) (decompose_int ADDSUB n) (rs r2)).
  apply iterate_op_correct.
  auto.
  auto.
  apply decompose_int_add.
  (* sub - sub* *)
  replace (Val.add (rs r2) (Vint n))
     with (List.fold_left (fun v i => Val.sub v (Vint i)) (decompose_int ADDSUB (Int.neg n)) (rs r2)).
  apply iterate_op_correct.
  auto.
  auto.
  rewrite decompose_int_sub. apply Val.sub_opp_add.
Qed.


Lemma andimm_correct:
  forall r1 r2 n k rs m,
  exists rs',
     exec_straight ge (andimm r1 r2 n k) rs m k rs' m
  /\ rs'#r1 = Val.and rs#r2 (Vint n)
  /\ forall r': preg, r' <> r1 -> if_preg r' = true -> rs'#r' = rs#r'.
Proof.
  intros. unfold andimm. destruct (is_immed_arith OTHER n).
  (* andi *)
  exists (undef_flags rs#r1 <- (Val.and rs#r2 (Vint n))).
  split. apply exec_straight_one; auto. split; intros; Simpl.
  (* bic - bic* *)
  replace (Val.and (rs r2) (Vint n))
     with (List.fold_left (fun v i => Val.and v (Vint (Int.not i))) (decompose_int OTHER (Int.not n)) (rs r2)).
  apply iterate_op_correct.
  auto. auto.
  rewrite decompose_int_bic. rewrite Int.not_involutive. auto.
Qed.

Reverse sub immediate

Lemma rsubimm_correct:
  forall r1 r2 n k rs m,
  exists rs',
     exec_straight ge (rsubimm r1 r2 n k) rs m k rs' m
  /\ rs'#r1 = Val.sub (Vint n) rs#r2
  /\ forall r': preg, r' <> r1 -> if_preg r' = true -> rs'#r' = rs#r'.
Proof.
  intros. unfold rsubimm.
  (* rsb - add* *)
  replace (Val.sub (Vint n) (rs r2))
     with (List.fold_left (fun v i => Val.add v (Vint i)) (decompose_int OTHER n) (Val.neg (rs r2))).
  apply iterate_op_correct.
  auto.
  intros. simpl. destruct (rs r2); auto. simpl. rewrite Int.sub_add_opp.
  rewrite Int.add_commut; auto.
  rewrite decompose_int_add.
  destruct (rs r2); simpl; auto. rewrite Int.sub_add_opp. rewrite Int.add_commut; auto.
Qed.

Or immediate

Lemma orimm_correct:
  forall r1 r2 n k rs m,
  exists rs',
     exec_straight ge (orimm r1 r2 n k) rs m k rs' m
  /\ rs'#r1 = Val.or rs#r2 (Vint n)
  /\ forall r': preg, r' <> r1 -> if_preg r' = true -> rs'#r' = rs#r'.
Proof.
  intros. unfold orimm.
  (* ori - ori* *)
  replace (Val.or (rs r2) (Vint n))
     with (List.fold_left (fun v i => Val.or v (Vint i)) (decompose_int OTHER n) (rs r2)).
  apply iterate_op_correct.
  auto.
  auto.
  apply decompose_int_or.
Qed.

Xor immediate

Lemma xorimm_correct:
  forall r1 r2 n k rs m,
  exists rs',
     exec_straight ge (xorimm r1 r2 n k) rs m k rs' m
  /\ rs'#r1 = Val.xor rs#r2 (Vint n)
  /\ forall r': preg, r' <> r1 -> if_preg r' = true -> rs'#r' = rs#r'.
Proof.
  intros. unfold xorimm.
  (* xori - xori* *)
  replace (Val.xor (rs r2) (Vint n))
     with (List.fold_left (fun v i => Val.xor v (Vint i)) (decompose_int OTHER n) (rs r2)).
  apply iterate_op_correct.
  auto.
  auto.
  apply decompose_int_xor.
Qed.

Indexed memory loads.

Lemma indexed_memory_access_correct:
  forall (P: regset -> Prop) (mk_instr: ireg -> int -> basic)
         (mk_immed: int -> int) (base: ireg) n k (rs: regset) m m',
  (forall (r1: ireg) (rs1: regset) n1 k,
    Val.add rs1#r1 (Vint n1) = Val.add rs#base (Vint n) ->
    (forall (r: preg), if_preg r = true -> r <> IR14 -> rs1 r = rs r) ->
    exists rs',
    exec_straight ge (mk_instr r1 n1 :: k) rs1 m k rs' m' /\ P rs') ->
  exists rs',
     exec_straight ge
        (indexed_memory_access mk_instr mk_immed base n k) rs m
        k rs' m'
  /\ P rs'.
Proof.
  intros until m'; intros SEM.
  unfold indexed_memory_access.
  destruct (Int.eq n (mk_immed n)).
- apply SEM; auto.
- destruct (addimm_correct IR14 base (Int.sub n (mk_immed n)) (mk_instr IR14 (mk_immed n) :: k) rs m)
  as (rs1 & A & B & C).
  destruct (SEM IR14 rs1 (mk_immed n) k) as (rs2 & D & E).
  rewrite B. rewrite Val.add_assoc. f_equal. simpl.
  rewrite Int.sub_add_opp. rewrite Int.add_assoc.
  rewrite (Int.add_commut (Int.neg (mk_immed n))).
  rewrite Int.add_neg_zero. rewrite Int.add_zero. auto.
  auto with asmgen.
  exists rs2; split; auto. eapply exec_straight_trans; eauto.
Qed.

Lemma loadind_int_correct:
  forall (base: ireg) ofs dst (rs: regset) m v k,
  Mem.loadv Mint32 m (Val.offset_ptr rs#base ofs) = Some v ->
  exists rs',
     exec_straight ge (loadind_int base ofs dst k) rs m k rs' m
  /\ rs'#dst = v
  /\ forall r, if_preg r = true -> r <> IR14 -> r <> dst -> rs'#r = rs#r.
Proof.
  intros; unfold loadind_int.
  assert (Val.offset_ptr (rs base) ofs = Val.add (rs base) (Vint (Ptrofs.to_int ofs))).
  { destruct (rs base); try discriminate. simpl. f_equal; f_equal. symmetry; auto with ptrofs. }
  apply indexed_memory_access_correct; intros.
  econstructor; split.
  apply exec_straight_one. simpl. unfold exec_load. rewrite H1, <- H0.
  unfold exec_load_aux. rewrite H. reflexivity.
  split; intros; Simpl.
Qed.

Lemma loadind_correct:
  forall (base: ireg) ofs ty dst k c (rs: regset) m v,
  loadind base ofs ty dst k = OK c ->
  Mem.loadv (chunk_of_type ty) m (Val.offset_ptr rs#base ofs) = Some v ->
  exists rs',
     exec_straight ge c rs m k rs' m
  /\ rs'#(preg_of dst) = v
  /\ forall r, if_preg r = true -> r <> IR14 -> r <> preg_of dst -> rs'#r = rs#r.
Proof.
  unfold loadind; intros.
  assert (Val.offset_ptr (rs base) ofs = Val.add (rs base) (Vint (Ptrofs.to_int ofs))).
  { destruct (rs base); try discriminate. simpl. f_equal; f_equal. symmetry; auto with ptrofs. }
  destruct ty; destruct (preg_of dst); try destruct d; inv H; simpl in H0.
- (* int *)
  apply loadind_int_correct; auto.
- (* float *)
  apply indexed_memory_access_correct; intros.
  econstructor; split.
  apply exec_straight_one. simpl. unfold exec_load. rewrite H, <- H1.
  unfold exec_load_aux. rewrite H0. eauto. auto.
  split; intros; Simpl.
- (* single *)
  apply indexed_memory_access_correct; intros.
  econstructor; split.
  apply exec_straight_one. simpl. unfold exec_load. rewrite H, <- H1.
  unfold exec_load_aux. rewrite H0. eauto. auto.
  split; intros; Simpl.
- (* any32 *)
  apply indexed_memory_access_correct; intros.
  econstructor; split.
  apply exec_straight_one. simpl. unfold exec_load. rewrite H, <- H1.
  unfold exec_load_aux. rewrite H0. eauto. auto.
  split; intros; Simpl.
- (* any64 *)
  apply indexed_memory_access_correct; intros.
  econstructor; split.
  apply exec_straight_one. simpl. unfold exec_load. rewrite H, <- H1.
  unfold exec_load_aux. rewrite H0. eauto. auto.
  split; intros; Simpl.
Qed.

Indexed memory stores.

Lemma storeind_correct:
  forall (base: ireg) ofs ty src k c (rs: regset) m m',
  storeind src base ofs ty k = OK c ->
  Mem.storev (chunk_of_type ty) m (Val.offset_ptr rs#base ofs) (rs#(preg_of src)) = Some m' ->
  exists rs',
     exec_straight ge c rs m k rs' m'
  /\ forall r, if_preg r = true -> r <> IR14 -> rs'#r = rs#r.
Proof.
  unfold storeind; intros.
  assert (DATA: data_preg (preg_of src) = true) by eauto with asmgen.
  assert (Val.offset_ptr (rs base) ofs = Val.add (rs base) (Vint (Ptrofs.to_int ofs))).
  { destruct (rs base); try discriminate. simpl. f_equal; f_equal. symmetry; auto with ptrofs. }
  destruct ty; destruct (preg_of src); try destruct d; inv H; simpl in H0.
- (* int *)
  apply indexed_memory_access_correct; intros.
  econstructor; split.
  apply exec_straight_one. simpl. unfold exec_store. rewrite H, <- H1.
  unfold exec_store_aux. rewrite H2, H0 by auto with asmgen; eauto. auto.
- (* float *)
  apply indexed_memory_access_correct; intros.
  econstructor; split.
  apply exec_straight_one. simpl. unfold exec_store. rewrite H, <- H1.
  unfold exec_store_aux. rewrite H2, H0 by auto with asmgen; eauto. auto.
- (* single *)
  apply indexed_memory_access_correct; intros.
  econstructor; split.
  apply exec_straight_one. simpl. unfold exec_store. rewrite H, <- H1.
  unfold exec_store_aux. rewrite H2, H0 by auto with asmgen; eauto. auto.
- (* any32 *)
  apply indexed_memory_access_correct; intros.
  econstructor; split.
  apply exec_straight_one. simpl. unfold exec_store. rewrite H, <- H1.
  unfold exec_store_aux. rewrite H2, H0 by auto with asmgen; eauto. auto.
- (* any64 *)
  apply indexed_memory_access_correct; intros.
  econstructor; split.
  apply exec_straight_one. simpl. unfold exec_store. rewrite H, <- H1.
  unfold exec_store_aux. rewrite H2, H0 by auto with asmgen; eauto. auto.
Qed.

Saving the link register

Lemma save_lr_correct:
  forall ofs k (rs: regset) m m',
  Mem.storev Mint32 m (Val.offset_ptr rs#IR13 ofs) (rs#IR14) = Some m' ->
  exists rs',
     exec_straight ge (save_lr ofs k) rs m k rs' m'
  /\ (forall r, if_preg r = true -> r <> IR12 -> rs'#r = rs#r)
  /\ (save_lr_preserves_R12 ofs = true -> rs'#IR12 = rs#IR12).
Proof.
  intros; unfold save_lr, save_lr_preserves_R12.
  set (n := Ptrofs.to_int ofs). set (n1 := mk_immed_mem_word n).
  assert (EQ: Val.offset_ptr rs#IR13 ofs = Val.add rs#IR13 (Vint n)).
  { destruct rs#IR13; try discriminate. simpl. f_equal; f_equal. unfold n; symmetry; auto with ptrofs. }
  destruct (Int.eq n n1).
- econstructor; split. apply exec_straight_one. simpl; unfold exec_store. rewrite <- EQ.
  unfold exec_store_aux. rewrite H; reflexivity. auto.
- destruct (addimm_correct IR12 IR13 (Int.sub n n1) (Pstr IR14 IR12 (SOimm n1) ::bi k) rs m)
  as (rs1 & A & B & C).
  econstructor; split.
  eapply exec_straight_trans. eexact A.
  apply exec_straight_one. simpl; unfold exec_store.
  rewrite B. rewrite Val.add_assoc. simpl.
  rewrite Int.sub_add_opp. rewrite Int.add_assoc.
  rewrite (Int.add_commut (Int.neg n1)).
  rewrite Int.add_neg_zero. rewrite Int.add_zero.
  rewrite <- EQ. unfold exec_store_aux. rewrite C by eauto with asmgen. rewrite H. reflexivity.
  auto.
  split. intros; Simpl. apply C. exact H1. exact H0. congruence.
Qed.

Translation of shift immediates

Lemma transl_shift_correct:
  forall s (r: ireg) (rs: regset),
  eval_shift_op (transl_shift s r) rs = eval_shift s (rs#r).
Proof.
  intros. destruct s; simpl; auto.
Qed.

Translation of conditions

Lemma compare_int_spec:
  forall rs v1 v2 m,
  let rs1 := compare_int rs v1 v2 m in
     rs1#CN = Val.negative (Val.sub v1 v2)
  /\ rs1#CZ = Val.cmpu (Mem.valid_pointer m) Ceq v1 v2
  /\ rs1#CC = Val.cmpu (Mem.valid_pointer m) Cge v1 v2
  /\ rs1#CV = Val.sub_overflow v1 v2.
Proof.
  intros. unfold rs1. intuition.
Qed.

Lemma compare_int_inv:
  forall rs v1 v2 m,
  let rs1 := compare_int rs v1 v2 m in
  forall r', data_preg r' = true -> rs1#r' = rs#r'.
Proof.
  intros. unfold rs1, compare_int.
  repeat Simplif.
Qed.

Lemma compare_int_test_spec:
  forall rs v1 v2,
  let rs1 := compare_int_test rs v1 v2 in
     rs1#CN = Val.negative (Val.and v1 v2)
  /\ rs1#CZ = Val.cmp Ceq (Val.and v1 v2) (Vint Int.zero)
  /\ rs1#CC = Vundef
  /\ rs1#CV = Vundef.
Proof.
  intros. unfold rs1, compare_int_test. intuition.
Qed.

Lemma compare_int_test_inv:
  forall rs v1 v2,
  let rs1 := compare_int_test rs v1 v2 in
  forall r', data_preg r' = true -> rs1#r' = rs#r'.
Proof.
  intros. unfold rs1, compare_int_test.
  repeat Simplif.
Qed.

Lemma cond_for_maskzero_correct:
  forall n v rs b,
  Val.cmp_bool Ceq (Val.and v (Vint n)) (Vint Int.zero) = Some b ->
  eval_testcond TCeq (compare_int_test rs v (Vint n)) = Some b.
Proof.
  intros. generalize (compare_int_test_spec rs v (Vint n)).
  set (rs' := compare_int_test rs v (Vint n)).
  intros [A [B [C D]]].
  unfold eval_testcond. rewrite B.
  destruct v; simpl in H; inv H.
  simpl. unfold Val.cmp; simpl.
  destruct (Int.eq (Int.and i n) Int.zero); reflexivity.
Qed.

Lemma cond_for_masknotzero_correct:
  forall n v rs b,
  Val.cmp_bool Cne (Val.and v (Vint n)) (Vint Int.zero) = Some b ->
  eval_testcond TCne (compare_int_test rs v (Vint n)) = Some b.
Proof.
  intros. generalize (compare_int_test_spec rs v (Vint n)).
  set (rs' := compare_int_test rs v (Vint n)).
  intros [A [B [C D]]].
  unfold eval_testcond. rewrite B.
  destruct v; simpl in H; inv H.
  simpl. unfold Val.cmp; simpl.
  destruct (Int.eq (Int.and i n) Int.zero); reflexivity.
Qed.

Lemma int_signed_eq:
  forall x y, Int.eq x y = zeq (Int.signed x) (Int.signed y).
Proof.
  intros. unfold Int.eq. unfold proj_sumbool.
  destruct (zeq (Int.unsigned x) (Int.unsigned y));
  destruct (zeq (Int.signed x) (Int.signed y)); auto.
  elim n. unfold Int.signed. rewrite e; auto.
  elim n. apply Int.eqm_small_eq; auto with ints.
  eapply Int.eqm_trans. apply Int.eqm_sym. apply Int.eqm_signed_unsigned.
  rewrite e. apply Int.eqm_signed_unsigned.
Qed.

Lemma int_not_lt:
  forall x y, negb (Int.lt y x) = (Int.lt x y || Int.eq x y).
Proof.
  intros. unfold Int.lt. rewrite int_signed_eq. unfold proj_sumbool.
  destruct (zlt (Int.signed y) (Int.signed x)).
  rewrite zlt_false. rewrite zeq_false. auto. lia. lia.
  destruct (zeq (Int.signed x) (Int.signed y)).
  rewrite zlt_false. auto. lia.
  rewrite zlt_true. auto. lia.
Qed.

Lemma int_lt_not:
  forall x y, Int.lt y x = negb (Int.lt x y) && negb (Int.eq x y).
Proof.
  intros. rewrite <- negb_orb. rewrite <- int_not_lt. rewrite negb_involutive. auto.
Qed.

Lemma int_not_ltu:
  forall x y, negb (Int.ltu y x) = (Int.ltu x y || Int.eq x y).
Proof.
  intros. unfold Int.ltu, Int.eq.
  destruct (zlt (Int.unsigned y) (Int.unsigned x)).
  rewrite zlt_false. rewrite zeq_false. auto. lia. lia.
  destruct (zeq (Int.unsigned x) (Int.unsigned y)).
  rewrite zlt_false. auto. lia.
  rewrite zlt_true. auto. lia.
Qed.

Lemma int_ltu_not:
  forall x y, Int.ltu y x = negb (Int.ltu x y) && negb (Int.eq x y).
Proof.
  intros. rewrite <- negb_orb. rewrite <- int_not_ltu. rewrite negb_involutive. auto.
Qed.

Lemma cond_for_signed_cmp_correct:
  forall c v1 v2 rs m b,
  Val.cmp_bool c v1 v2 = Some b ->
  eval_testcond (cond_for_signed_cmp c)
                (compare_int rs v1 v2 m) = Some b.
Proof.
  intros. generalize (compare_int_spec rs v1 v2 m).
  set (rs' := (compare_int rs v1 v2 m)).
  intros [A [B [C D]]].
  destruct v1; destruct v2; simpl in H; inv H.
  unfold eval_testcond. rewrite A; rewrite B; rewrite C; rewrite D.
  simpl. unfold Val.cmp, Val.cmpu.
  rewrite Int.lt_sub_overflow.
  destruct c; simpl.
  destruct (Int.eq i i0); auto.
  destruct (Int.eq i i0); auto.
  destruct (Int.lt i i0); auto.
  rewrite int_not_lt. destruct (Int.lt i i0); simpl; destruct (Int.eq i i0); auto.
  rewrite (int_lt_not i i0). destruct (Int.lt i i0); destruct (Int.eq i i0); reflexivity.
  destruct (Int.lt i i0); reflexivity.
Qed.

Lemma cond_for_unsigned_cmp_correct:
  forall c v1 v2 rs m b,
  Val.cmpu_bool (Mem.valid_pointer m) c v1 v2 = Some b ->
  eval_testcond (cond_for_unsigned_cmp c)
                (compare_int rs v1 v2 m) = Some b.
Proof.
  intros. generalize (compare_int_spec rs v1 v2 m).
  set (rs' := compare_int rs v1 v2 m).
  intros [A [B [C D]]].
  unfold eval_testcond. rewrite B; rewrite C. unfold Val.cmpu, Val.cmp.
  destruct v1; destruct v2; simpl in H; inv H.
(* int int *)
  destruct c; simpl; auto.
  destruct (Int.eq i i0); reflexivity.
  destruct (Int.eq i i0); auto.
  destruct (Int.ltu i i0); auto.
  rewrite (int_not_ltu i i0). destruct (Int.ltu i i0); destruct (Int.eq i i0); auto.
  rewrite (int_ltu_not i i0). destruct (Int.ltu i i0); destruct (Int.eq i i0); reflexivity.
  destruct (Int.ltu i i0); reflexivity.
(* int ptr *)
  destruct (Int.eq i Int.zero &&
    (Mem.valid_pointer m b0 (Ptrofs.unsigned i0) || Mem.valid_pointer m b0 (Ptrofs.unsigned i0 - 1))) eqn:?; try discriminate.
  destruct c; simpl in *; inv H1.
  rewrite Heqb1; reflexivity.
  rewrite Heqb1; reflexivity.
(* ptr int *)
  destruct (Int.eq i0 Int.zero &&
    (Mem.valid_pointer m b0 (Ptrofs.unsigned i) || Mem.valid_pointer m b0 (Ptrofs.unsigned i - 1))) eqn:?; try discriminate.
  destruct c; simpl in *; inv H1.
  rewrite Heqb1; reflexivity.
  rewrite Heqb1; reflexivity.
(* ptr ptr *)
  simpl.
  fold (Mem.weak_valid_pointer m b0 (Ptrofs.unsigned i)) in *.
  fold (Mem.weak_valid_pointer m b1 (Ptrofs.unsigned i0)) in *.
  destruct (eq_block b0 b1).
  destruct (Mem.weak_valid_pointer m b0 (Ptrofs.unsigned i) &&
            Mem.weak_valid_pointer m b1 (Ptrofs.unsigned i0)); inversion H1.
  destruct c; simpl; auto.
  destruct (Ptrofs.eq i i0); reflexivity.
  destruct (Ptrofs.eq i i0); auto.
  destruct (Ptrofs.ltu i i0); auto.
  rewrite (Ptrofs.not_ltu i i0). destruct (Ptrofs.ltu i i0); simpl; destruct (Ptrofs.eq i i0); auto.
  rewrite (Ptrofs.ltu_not i i0). destruct (Ptrofs.ltu i i0); destruct (Ptrofs.eq i i0); reflexivity.
  destruct (Ptrofs.ltu i i0); reflexivity.
  destruct (Mem.valid_pointer m b0 (Ptrofs.unsigned i) &&
            Mem.valid_pointer m b1 (Ptrofs.unsigned i0)); try discriminate.
  destruct c; simpl in *; inv H1; reflexivity.
Qed.

Lemma compare_float_spec:
  forall rs f1 f2,
  let rs1 := compare_float rs (Vfloat f1) (Vfloat f2) in
     rs1#CN = Val.of_bool (Float.cmp Clt f1 f2)
  /\ rs1#CZ = Val.of_bool (Float.cmp Ceq f1 f2)
  /\ rs1#CC = Val.of_bool (negb (Float.cmp Clt f1 f2))
  /\ rs1#CV = Val.of_bool (negb (Float.cmp Ceq f1 f2 || Float.cmp Clt f1 f2 || Float.cmp Cgt f1 f2)).
Proof.
  intros. intuition.
Qed.

Lemma compare_float_inv:
  forall rs v1 v2,
  let rs1 := compare_float rs v1 v2 in
  forall r', data_preg r' = true -> rs1#r' = rs#r'.
Proof.
  intros. unfold rs1, compare_float.
  assert ((rs#CN <- Vundef #CZ <- Vundef #CC <- Vundef #CV <- Vundef) r' = rs r').
  { repeat Simplif. }
  destruct v1; destruct v2; auto.
  repeat Simplif.
Qed.

Lemma cond_for_float_cmp_correct: forall c v1 v2 b rs,
  Val.cmpf_bool c v1 v2 = Some b ->
  eval_testcond (cond_for_float_cmp c) (compare_float rs v1 v2) = Some b.
Proof.
  intros. destruct v1; try discriminate; destruct v2; simpl in H; inv H.
  generalize (compare_float_spec rs f f0).
  set (rs' := compare_float rs (Vfloat f) (Vfloat f0)).
  intros (B & C & D & E).
  unfold eval_testcond; rewrite B, C, D, E.
Local Transparent Float.cmp Float.ordered.
  unfold Float.cmp, Float.ordered;
  destruct c; destruct (Float.compare f f0) as [[]|]; reflexivity.
Qed.

Lemma cond_for_float_not_cmp_correct: forall c v1 v2 b rs,
  option_map negb (Val.cmpf_bool c v1 v2) = Some b ->
  eval_testcond (cond_for_float_not_cmp c) (compare_float rs v1 v2) = Some b.
Proof.
  intros. destruct v1; try discriminate; destruct v2; simpl in H; inv H.
  generalize (compare_float_spec rs f f0).
  set (rs' := compare_float rs (Vfloat f) (Vfloat f0)).
  intros (B & C & D & E).
  unfold eval_testcond; rewrite B, C, D, E.
Local Transparent Float.cmp Float.ordered.
  unfold Float.cmp, Float.ordered;
  destruct c; destruct (Float.compare f f0) as [[]|]; reflexivity.
Qed.

Lemma compare_float32_spec:
  forall rs f1 f2,
  let rs1 := compare_float32 rs (Vsingle f1) (Vsingle f2) in
     rs1#CN = Val.of_bool (Float32.cmp Clt f1 f2)
  /\ rs1#CZ = Val.of_bool (Float32.cmp Ceq f1 f2)
  /\ rs1#CC = Val.of_bool (negb (Float32.cmp Clt f1 f2))
  /\ rs1#CV = Val.of_bool (negb (Float32.cmp Ceq f1 f2 || Float32.cmp Clt f1 f2 || Float32.cmp Cgt f1 f2)).
Proof.
  intros. intuition.
Qed.

Lemma compare_float32_inv:
  forall rs v1 v2,
  let rs1 := compare_float32 rs v1 v2 in
  forall r', data_preg r' = true -> rs1#r' = rs#r'.
Proof.
  intros. unfold rs1, compare_float32.
  assert ((rs#CN <- Vundef #CZ <- Vundef #CC <- Vundef #CV <- Vundef) r' = rs r').
  { repeat Simplif. }
  destruct v1; destruct v2; auto.
  repeat Simplif.
Qed.

Lemma cond_for_float32_cmp_correct: forall c v1 v2 b rs,
  Val.cmpfs_bool c v1 v2 = Some b ->
  eval_testcond (cond_for_float_cmp c) (compare_float32 rs v1 v2) = Some b.
Proof.
  intros. destruct v1; try discriminate; destruct v2; simpl in H; inv H.
  generalize (compare_float32_spec rs f f0).
  set (rs' := compare_float rs (Vsingle f) (Vsingle f0)).
  intros (B & C & D & E).
  unfold eval_testcond; rewrite B, C, D, E.
Local Transparent Float32.cmp Float32.ordered.
  unfold Float32.cmp, Float32.ordered;
  destruct c; destruct (Float32.compare f f0) as [[]|]; reflexivity.
Qed.

Lemma cond_for_float32_not_cmp_correct: forall c v1 v2 b rs,
  option_map negb (Val.cmpfs_bool c v1 v2) = Some b ->
  eval_testcond (cond_for_float_not_cmp c) (compare_float32 rs v1 v2) = Some b.
Proof.
  intros. destruct v1; try discriminate; destruct v2; simpl in H; inv H.
  generalize (compare_float32_spec rs f f0).
  set (rs' := compare_float rs (Vsingle f) (Vsingle f0)).
  intros (B & C & D & E).
  unfold eval_testcond; rewrite B, C, D, E.
Local Transparent Float32.cmp Float32.ordered.
  unfold Float32.cmp, Float32.ordered;
  destruct c; destruct (Float32.compare f f0) as [[]|]; reflexivity.
Qed.

Ltac ArgsInv :=
  repeat (match goal with
  | [ H: Error _ = OK _ |- _ ] => discriminate
  | [ H: match ?args with nil => _ | _ :: _ => _ end = OK _ |- _ ] => destruct args
  | [ H: bind _ _ = OK _ |- _ ] => monadInv H
  | [ H: match _ with left _ => _ | right _ => assertion_failed end = OK _ |- _ ] => monadInv H
  | [ H: match _ with true => _ | false => assertion_failed end = OK _ |- _ ] => monadInv H
  end);
  subst;
  repeat (match goal with
  | [ H: ireg_of ?x = OK ?y |- _ ] => simpl in *; rewrite (ireg_of_eq _ _ H) in *
  | [ H: freg_of ?x = OK ?y |- _ ] => simpl in *; rewrite (freg_of_eq _ _ H) in *
  end).

Key bit-level fact: after cmp lo1 lo2 sets C := lo1 >=u lo2 and sbcs r hi1 hi2 computes Int.add_carry hi1 (Int.not hi2) C, the resulting carry equals Int.one iff (hi1:lo1) >=u (hi2:lo2) (unsigned 64-bit comparison). The proof avoids a 12-way destruct by separating lo1 <? lo2 first, then hi1 vs hi2, leaving 6 simple zlt _ Int.modulus goals each closed by lia.

Signed analog of cmp_sbcs_carry_correct: after cmp+sbcs, N XOR V equals signed 64-bit less-than. The c1=1 (lo1>=lo2, bin=0) branch is direct from Int.lt_sub_overflow; the c1=0 (bin=1) branch is the borrow-in generalisation, proven inline with the same modular-arithmetic argument as Int.lt_sub_overflow.

Lemma cmp_sbcs_signed_correct:
  forall hi1 lo1 hi2 lo2,
  let c1 := if Int.ltu lo1 lo2 then Int.zero else Int.one in
  let bin := Int.sub Int.one c1 in
  Int.xor (Int.sub_overflow hi1 hi2 bin)
          (Int.negative (Int.sub (Int.sub hi1 hi2) bin)) =
    (if Int64.lt (Int64.ofwords hi1 lo1) (Int64.ofwords hi2 lo2)
     then Int.one else Int.zero).
Proof.
  intros hi1 lo1 hi2 lo2.
  cbv zeta.
  rewrite Int64.decompose_lt.
  destruct (Int.ltu lo1 lo2) eqn:Hlo; cbn.
  2: { (* c1=1, bin=0: direct from Int.lt_sub_overflow. *)
       replace (Int.sub Int.one Int.one) with Int.zero by reflexivity.
       rewrite Int.sub_zero_l. rewrite Int.lt_sub_overflow.
       destruct (Int.eq hi1 hi2) eqn:Hh;
         [apply Int.same_if_eq in Hh; subst hi2 | reflexivity].
       unfold Int.lt; rewrite Coqlib.zlt_false by lia; reflexivity. }
  (* c1=0, bin=1: borrow-in generalisation. *)
  replace (Int.sub Int.one Int.zero) with Int.one by reflexivity.
  unfold Int.negative, Int.sub_overflow, Int.lt.
  rewrite (Int.sub_signed (Int.sub hi1 hi2) Int.one).
  rewrite (Int.sub_signed hi1 hi2).
  rewrite Int.signed_one.
  unfold Int.eq.
  replace (if zeq (Int.unsigned hi1) (Int.unsigned hi2) then true else false)
     with (if zeq (Int.signed hi1) (Int.signed hi2) then true else false)
     by (rewrite <- Int.eq_signed; reflexivity).
  generalize (Int.signed_range hi1) (Int.signed_range hi2).
  generalize (Int.signed hi1) (Int.signed hi2). intros X Y RX RY.
  change (Int.signed Int.zero) with 0.
  unfold Int.min_signed, Int.max_signed in *.
  generalize Int.half_modulus_pos Int.half_modulus_modulus; intros HM MM.
  (* Reduce the doubly-nested [Int.signed (Int.repr ...)] to a single one. *)
  assert (Hsigned: Int.signed (Int.repr (Int.signed (Int.repr (X - Y)) - 1))
                 = Int.signed (Int.repr (X - Y - 1))).
  { f_equal. apply Int.eqm_samerepr.
    apply Int.eqm_sub; [|apply Int.eqm_refl].
    eapply Int.eqm_trans. apply Int.eqm_signed_unsigned.
    apply Int.eqm_sym. apply Int.eqm_unsigned_repr. }
  rewrite Hsigned. clear Hsigned.
  destruct (zle 0 (X - Y - 1)) as [Hge|Hlt0];
    [destruct (zle (- Int.half_modulus) (X - Y - 1)); [|lia]; cbn|].
  1: change 2147483647 with (Int.half_modulus - 1).
  1: destruct (zle (X - Y - 1) (Int.half_modulus - 1)) as [Hle|Hgt]; cbn.
  1: rewrite (Int.signed_repr (X-Y-1))
       by (unfold Int.min_signed, Int.max_signed; lia).
  1: destruct (zlt (X-Y-1) 0); [lia|]; cbn.
  1: destruct (zeq X Y); destruct (zlt X Y);
       try (exfalso; lia); try reflexivity;
       change (Int.xor Int.zero Int.zero) with Int.zero;
       reflexivity.
  1: replace (Int.signed (Int.repr (X-Y-1))) with (X-Y-1-Int.modulus).
  2: { rewrite Int.signed_repr_eq.
       replace ((X-Y-1) mod Int.modulus) with (X-Y-1)
          by (symmetry; apply Z.mod_small; lia).
       rewrite zlt_false by lia. reflexivity. }
  1: destruct (zlt (X-Y-1-Int.modulus) 0); [|lia]; cbn.
  1: destruct (zeq X Y); destruct (zlt X Y);
       try (exfalso; lia); try reflexivity;
       change (Int.xor Int.one Int.one) with Int.zero;
       reflexivity.
  change 2147483647 with (Int.half_modulus - 1).
  destruct (zle (X - Y - 1) (Int.half_modulus - 1)); [|lia].
  rewrite andb_true_r.
  destruct (zle (- Int.half_modulus) (X - Y - 1)) as [Hgey|Hlty]; cbn.
  1: rewrite (Int.signed_repr (X-Y-1))
       by (unfold Int.min_signed, Int.max_signed; lia).
  1: destruct (zlt (X-Y-1) 0); [|lia]; cbn.
  1: destruct (zeq X Y); destruct (zlt X Y);
       try (exfalso; lia); try reflexivity;
       change (Int.xor Int.zero Int.one) with Int.one;
       reflexivity.
  replace (Int.signed (Int.repr (X-Y-1))) with (X-Y-1+Int.modulus).
  2: { rewrite Int.signed_repr_eq.
       replace ((X-Y-1) mod Int.modulus) with (X-Y-1+Int.modulus).
       - rewrite zlt_true by lia. reflexivity.
       - apply Z.mod_unique with (-1); lia. }
  destruct (zlt (X-Y-1+Int.modulus) 0); [lia|]; cbn.
  destruct (zeq X Y); destruct (zlt X Y);
    try (exfalso; lia); try reflexivity;
    change (Int.xor Int.one Int.zero) with Int.one;
    reflexivity.
  Unshelve. change Int.zwordsize with 32; lia.
Qed.

Lemma cmp_sbcs_carry_correct:
  forall hi1 lo1 hi2 lo2,
  let c1 := if Int.ltu lo1 lo2 then Int.zero else Int.one in
  Int.add_carry hi1 (Int.not hi2) c1 =
    (if Int64.ltu (Int64.ofwords hi1 lo1) (Int64.ofwords hi2 lo2)
     then Int.zero else Int.one).
Proof.
  intros hi1 lo1 hi2 lo2.
  cbv zeta.
  rewrite Int64.decompose_ltu.
  set (uhi1 := Int.unsigned hi1).
  set (uhi2 := Int.unsigned hi2).
  set (ulo1 := Int.unsigned lo1).
  set (ulo2 := Int.unsigned lo2).
  unfold Int.add_carry, Int.ltu, Int.eq.
  fold uhi1 uhi2 ulo1 ulo2.
  rewrite Int.unsigned_not. fold uhi2.
  change Int.max_unsigned with (Int.modulus - 1).
  pose proof (Int.unsigned_range hi1) as Rh1; fold uhi1 in Rh1.
  pose proof (Int.unsigned_range hi2) as Rh2; fold uhi2 in Rh2.
  set (M := Int.modulus) in *.
  destruct (zlt ulo1 ulo2) as [Hlo|Hlo].
  2: replace (Int.unsigned Int.one) with 1 by reflexivity.
  1: replace (Int.unsigned Int.zero) with 0 by reflexivity.
  all: cbn -[zlt zeq M].
  all: destruct (zeq uhi1 uhi2) as [Hh|Hh];
       [subst uhi1 | destruct (zlt uhi1 uhi2)];
       destruct (zlt _ M); try reflexivity; exfalso; lia.
Qed.

Lemma transl_cond_correct:
  forall cond args k c rs m,
  transl_cond cond args k = OK c ->
  exists rs',
     exec_straight ge c rs m k rs' m
  /\ (forall b,
      eval_condition cond (map rs (map preg_of args)) m = Some b ->
      eval_testcond (cond_for_cond cond) rs' = Some b)
  /\ forall r, data_preg r = true -> rs'#r = rs#r.
Proof.
  intros until m; intros TR. unfold transl_cond in TR; destruct cond; ArgsInv.
  (* Ccomp *)
  - econstructor; split. apply exec_straight_one. simpl. auto.
    split. apply cond_for_signed_cmp_correct.
    apply compare_int_inv.
  (* Ccompu *)
  - econstructor; split. apply exec_straight_one. simpl. auto.
    split. intros. apply cond_for_unsigned_cmp_correct; auto.
    apply compare_int_inv.
  (* Ccompshift *)
  - econstructor; split. apply exec_straight_one. simpl. auto.
    split; rewrite transl_shift_correct. intros.
    apply cond_for_signed_cmp_correct; auto.
    apply compare_int_inv.
  (* Ccompushift *)
  - econstructor; split. apply exec_straight_one. simpl. auto.
    split; rewrite transl_shift_correct. intros.
    apply cond_for_unsigned_cmp_correct; auto.
    apply compare_int_inv.
  (* Ccompimm *)
  - destruct (is_immed_arith OTHER i); [| destruct (is_immed_arith OTHER (Int.neg i))].
    + econstructor; split. apply exec_straight_one. simpl. auto.
      split. intros. apply cond_for_signed_cmp_correct. auto.
      apply compare_int_inv.
    + econstructor; split. apply exec_straight_one. simpl. auto.
      split. intros. apply cond_for_signed_cmp_correct.
      rewrite Int.neg_involutive. auto.
      apply compare_int_inv.
    + exploit (loadimm_correct IR14). intros [rs' [P [Q R]]].
      econstructor; split.
      eapply exec_straight_trans. eexact P.
      apply exec_straight_one. simpl. rewrite Q, R by eauto with asmgen. auto.
      split. intros. apply cond_for_signed_cmp_correct. auto.
      intros. rewrite compare_int_inv by auto. auto with asmgen.
  (* Ccompuimm *)
  - destruct (is_immed_arith OTHER i); [| destruct (is_immed_arith OTHER (Int.neg i))].
    + econstructor; split. apply exec_straight_one. simpl. auto.
      split. intros. apply cond_for_unsigned_cmp_correct. auto.
      apply compare_int_inv.
    + econstructor; split. apply exec_straight_one. simpl. auto.
      split. intros. apply cond_for_unsigned_cmp_correct.
      rewrite Int.neg_involutive. auto.
      apply compare_int_inv.
    + exploit (loadimm_correct IR14). intros [rs' [P [Q R]]].
      econstructor; split.
      eapply exec_straight_trans. eexact P.
      apply exec_straight_one. simpl. rewrite Q, R by eauto with asmgen. auto.
      split. intros. apply cond_for_unsigned_cmp_correct. auto.
      intros. rewrite compare_int_inv by auto. auto with asmgen.
  (* Ccompf *)
  - econstructor; split. apply exec_straight_one. simpl. auto.
    split. intros.
    apply cond_for_float_cmp_correct. auto.
    apply compare_float_inv.
  (* Cnotcompf *)
  - econstructor; split. apply exec_straight_one. simpl. auto.
    split. intros.
    apply cond_for_float_not_cmp_correct. auto.
    apply compare_float_inv.
  (* Ccompfzero *)
  - econstructor; split. apply exec_straight_one. simpl. auto.
    split. intros.
    apply cond_for_float_cmp_correct. auto.
    apply compare_float_inv.
  (* Cnotcompfzero *)
  - econstructor; split. apply exec_straight_one. simpl. auto.
    split. intros.
    apply cond_for_float_not_cmp_correct. auto.
    apply compare_float_inv.
  (* Ccompfs *)
  - econstructor; split. apply exec_straight_one. simpl. auto.
    split. intros.
    apply cond_for_float32_cmp_correct. auto.
    apply compare_float32_inv.
  (* Cnotcompfs *)
  - econstructor; split. apply exec_straight_one. simpl. auto.
    split. intros.
    apply cond_for_float32_not_cmp_correct. auto.
    apply compare_float32_inv.
  (* Ccompfszero *)
  - econstructor; split. apply exec_straight_one. simpl. auto.
    split. intros.
    apply cond_for_float32_cmp_correct. auto.
    apply compare_float32_inv.
  (* Cnotcompfzero *)
  - econstructor; split. apply exec_straight_one. simpl. auto.
    split. intros.
    apply cond_for_float32_not_cmp_correct. auto.
    apply compare_float32_inv.
  (* Cmaskzero *)
  - destruct (is_immed_arith OTHER i).
    + econstructor; split. apply exec_straight_one. simpl. auto.
      split. intros. apply cond_for_maskzero_correct. auto.
      intros. apply compare_int_test_inv. auto.
    + exploit (loadimm_correct IR14). intros [rs' [P [Q R]]].
      econstructor; split.
      eapply exec_straight_trans. eexact P.
      apply exec_straight_one. simpl. rewrite Q, R by eauto with asmgen. auto.
      split. intros. apply cond_for_maskzero_correct. auto.
      intros. rewrite compare_int_test_inv by auto. auto with asmgen.
  (* Cmasknotzero *)
  - destruct (is_immed_arith OTHER i).
    + econstructor; split. apply exec_straight_one. simpl. auto.
      split. intros. apply cond_for_masknotzero_correct. auto.
      intros. apply compare_int_test_inv. auto.
    + exploit (loadimm_correct IR14). intros [rs' [P [Q R]]].
      econstructor; split.
      eapply exec_straight_trans. eexact P.
      apply exec_straight_one. simpl. rewrite Q, R by eauto with asmgen. auto.
      split. intros. apply cond_for_masknotzero_correct. auto.
      intros. rewrite compare_int_test_inv by auto. auto with asmgen.
  (* Ccompcarryu — 64-bit unsigned ordered comparison via cmp+sbcs.
     After ArgsInv, args = m0::m1::m2::m3::nil with iregs x, x0, x1, x2,
     ireg_of equations EQ:m0->x, EQ1:m1->x0, EQ0:m2->x1, EQ2:m3->x2. *)

  - match goal with
    | H : match c0 with Ceq | _ => _ end = OK _ |- _ =>
        destruct c0; cbn in H; inv H
    end.
    + (* Clt: cmp lo1, lo2; sbcs IR14, hi1, hi2; predicate TClo *)
      eexists. split.
      { eapply exec_straight_two; cbn; reflexivity. }
      cbn. split.
      * intros b EVAL. cbn in EVAL.
        destruct (rs (IR x)) eqn:Hr0; cbn in *; try discriminate;
        destruct (rs (IR x0)) eqn:Hr1; cbn in *; try discriminate;
        destruct (rs (IR x1)) eqn:Hr2; cbn in *; try discriminate;
        destruct (rs (IR x2)) eqn:Hr3; cbn in *; try discriminate.
        inv EVAL.
        rewrite Pregmap.gso by discriminate.
        unfold compare_int_sbc; cbn.
        rewrite Pregmap.gso by discriminate.
        rewrite Pregmap.gss.
        unfold compare_int.
        rewrite ! Pregmap.gso by discriminate.
        rewrite ! Pregmap.gss.
        rewrite Hr0, Hr2.
        cbn.
        replace (if negb (Int.ltu i0 i2) then Vtrue else Vfalse)
          with (Vint (if Int.ltu i0 i2 then Int.zero else Int.one))
          by (destruct (Int.ltu i0 i2); reflexivity).
        cbn.
        rewrite (cmp_sbcs_carry_correct i i0 i1 i2).
        destruct (Int64.ltu (Int64.ofwords i i0) (Int64.ofwords i1 i2));
          reflexivity.
      * intros r DR. cbn.
        rewrite Pregmap.gso
          by (intro Heq; subst r; cbn in DR; discriminate).
        unfold compare_int_sbc, compare_int; cbn.
        rewrite ! Pregmap.gso
          by (intro Heq; subst r; cbn in DR; discriminate).
        reflexivity.
    + (* Cle: swapped — cmp lo2, lo1; sbcs IR14, hi2, hi1; predicate TChs *)
      eexists. split.
      { eapply exec_straight_two; cbn; reflexivity. }
      cbn. split.
      * intros b EVAL. cbn in EVAL.
        destruct (rs (IR x)) eqn:Hr0; cbn in *; try discriminate;
        destruct (rs (IR x0)) eqn:Hr1; cbn in *; try discriminate;
        destruct (rs (IR x1)) eqn:Hr2; cbn in *; try discriminate;
        destruct (rs (IR x2)) eqn:Hr3; cbn in *; try discriminate.
        inv EVAL.
        rewrite Pregmap.gso by discriminate.
        unfold compare_int_sbc; cbn.
        rewrite Pregmap.gso by discriminate.
        rewrite Pregmap.gss.
        unfold compare_int.
        rewrite ! Pregmap.gso by discriminate.
        rewrite ! Pregmap.gss.
        rewrite Hr0, Hr2.
        cbn.
        replace (if negb (Int.ltu i2 i0) then Vtrue else Vfalse)
          with (Vint (if Int.ltu i2 i0 then Int.zero else Int.one))
          by (destruct (Int.ltu i2 i0); reflexivity).
        cbn.
        rewrite (cmp_sbcs_carry_correct i1 i2 i i0).
        destruct (Int64.ltu (Int64.ofwords i1 i2) (Int64.ofwords i i0));
          reflexivity.
      * intros r DR. cbn.
        rewrite Pregmap.gso
          by (intro Heq; subst r; cbn in DR; discriminate).
        unfold compare_int_sbc, compare_int; cbn.
        rewrite ! Pregmap.gso
          by (intro Heq; subst r; cbn in DR; discriminate).
        reflexivity.
    + (* Cgt: swapped — cmp lo2, lo1; sbcs IR14, hi2, hi1; predicate TClo *)
      eexists. split.
      { eapply exec_straight_two; cbn; reflexivity. }
      cbn. split.
      * intros b EVAL. cbn in EVAL.
        destruct (rs (IR x)) eqn:Hr0; cbn in *; try discriminate;
        destruct (rs (IR x0)) eqn:Hr1; cbn in *; try discriminate;
        destruct (rs (IR x1)) eqn:Hr2; cbn in *; try discriminate;
        destruct (rs (IR x2)) eqn:Hr3; cbn in *; try discriminate.
        inv EVAL.
        rewrite Pregmap.gso by discriminate.
        unfold compare_int_sbc; cbn.
        rewrite Pregmap.gso by discriminate.
        rewrite Pregmap.gss.
        unfold compare_int.
        rewrite ! Pregmap.gso by discriminate.
        rewrite ! Pregmap.gss.
        rewrite Hr0, Hr2.
        cbn.
        replace (if negb (Int.ltu i2 i0) then Vtrue else Vfalse)
          with (Vint (if Int.ltu i2 i0 then Int.zero else Int.one))
          by (destruct (Int.ltu i2 i0); reflexivity).
        cbn.
        rewrite (cmp_sbcs_carry_correct i1 i2 i i0).
        destruct (Int64.ltu (Int64.ofwords i1 i2) (Int64.ofwords i i0));
          reflexivity.
      * intros r DR. cbn.
        rewrite Pregmap.gso
          by (intro Heq; subst r; cbn in DR; discriminate).
        unfold compare_int_sbc, compare_int; cbn.
        rewrite ! Pregmap.gso
          by (intro Heq; subst r; cbn in DR; discriminate).
        reflexivity.
    + (* Cge: cmp lo1, lo2; sbcs IR14, hi1, hi2; predicate TChs *)
      eexists. split.
      { eapply exec_straight_two; cbn; reflexivity. }
      cbn. split.
      * intros b EVAL. cbn in EVAL.
        destruct (rs (IR x)) eqn:Hr0; cbn in *; try discriminate;
        destruct (rs (IR x0)) eqn:Hr1; cbn in *; try discriminate;
        destruct (rs (IR x1)) eqn:Hr2; cbn in *; try discriminate;
        destruct (rs (IR x2)) eqn:Hr3; cbn in *; try discriminate.
        inv EVAL.
        rewrite Pregmap.gso by discriminate.
        unfold compare_int_sbc; cbn.
        rewrite Pregmap.gso by discriminate.
        rewrite Pregmap.gss.
        unfold compare_int.
        rewrite ! Pregmap.gso by discriminate.
        rewrite ! Pregmap.gss.
        rewrite Hr0, Hr2.
        cbn.
        replace (if negb (Int.ltu i0 i2) then Vtrue else Vfalse)
          with (Vint (if Int.ltu i0 i2 then Int.zero else Int.one))
          by (destruct (Int.ltu i0 i2); reflexivity).
        cbn.
        rewrite (cmp_sbcs_carry_correct i i0 i1 i2).
        destruct (Int64.ltu (Int64.ofwords i i0) (Int64.ofwords i1 i2));
          reflexivity.
      * intros r DR. cbn.
        rewrite Pregmap.gso
          by (intro Heq; subst r; cbn in DR; discriminate).
        unfold compare_int_sbc, compare_int; cbn.
        rewrite ! Pregmap.gso
          by (intro Heq; subst r; cbn in DR; discriminate).
        reflexivity.
  (* Ccompcarry — signed analog using cmp_sbcs_signed_correct. *)
  - match goal with
    | H : match c0 with Ceq | _ => _ end = OK _ |- _ =>
        destruct c0; cbn in H; inv H
    end.
    + (* Clt signed *)
      eexists. split.
      { eapply exec_straight_two; cbn; reflexivity. }
      cbn. split.
      * intros b EVAL. cbn in EVAL.
        destruct (rs (IR x)) eqn:Hr0; cbn in *; try discriminate;
        destruct (rs (IR x0)) eqn:Hr1; cbn in *; try discriminate;
        destruct (rs (IR x1)) eqn:Hr2; cbn in *; try discriminate;
        destruct (rs (IR x2)) eqn:Hr3; cbn in *; try discriminate.
        inv EVAL.
        rewrite ! Pregmap.gso by discriminate.
        unfold compare_int_sbc; cbn.
        rewrite Pregmap.gss.
        rewrite ! Pregmap.gso by discriminate.
        rewrite Pregmap.gss.
        unfold compare_int.
        rewrite ! Pregmap.gso by discriminate.
        rewrite ! Pregmap.gss.
        rewrite Hr0, Hr2.
        cbn.
        replace (if negb (Int.ltu i0 i2) then Vtrue else Vfalse)
          with (Vint (if Int.ltu i0 i2 then Int.zero else Int.one))
          by (destruct (Int.ltu i0 i2); reflexivity).
        cbn.
        pose proof (cmp_sbcs_signed_correct i i0 i1 i2) as Hsign;
          cbv zeta in Hsign; rewrite Hsign.
        destruct (Int64.lt (Int64.ofwords i i0) (Int64.ofwords i1 i2));
          reflexivity.
      * intros r DR. cbn.
        rewrite Pregmap.gso
          by (intro Heq; subst r; cbn in DR; discriminate).
        unfold compare_int_sbc, compare_int; cbn.
        rewrite ! Pregmap.gso
          by (intro Heq; subst r; cbn in DR; discriminate).
        reflexivity.
    + (* Cle signed: swapped *)
      eexists. split.
      { eapply exec_straight_two; cbn; reflexivity. }
      cbn. split.
      * intros b EVAL. cbn in EVAL.
        destruct (rs (IR x)) eqn:Hr0; cbn in *; try discriminate;
        destruct (rs (IR x0)) eqn:Hr1; cbn in *; try discriminate;
        destruct (rs (IR x1)) eqn:Hr2; cbn in *; try discriminate;
        destruct (rs (IR x2)) eqn:Hr3; cbn in *; try discriminate.
        inv EVAL.
        rewrite ! Pregmap.gso by discriminate.
        unfold compare_int_sbc; cbn.
        rewrite Pregmap.gss.
        rewrite ! Pregmap.gso by discriminate.
        rewrite Pregmap.gss.
        unfold compare_int.
        rewrite ! Pregmap.gso by discriminate.
        rewrite ! Pregmap.gss.
        rewrite Hr0, Hr2.
        cbn.
        replace (if negb (Int.ltu i2 i0) then Vtrue else Vfalse)
          with (Vint (if Int.ltu i2 i0 then Int.zero else Int.one))
          by (destruct (Int.ltu i2 i0); reflexivity).
        cbn.
        pose proof (cmp_sbcs_signed_correct i1 i2 i i0) as Hsign;
          cbv zeta in Hsign; rewrite Hsign.
        destruct (Int64.lt (Int64.ofwords i1 i2) (Int64.ofwords i i0));
          reflexivity.
      * intros r DR. cbn.
        rewrite Pregmap.gso
          by (intro Heq; subst r; cbn in DR; discriminate).
        unfold compare_int_sbc, compare_int; cbn.
        rewrite ! Pregmap.gso
          by (intro Heq; subst r; cbn in DR; discriminate).
        reflexivity.
    + (* Cgt signed: swapped *)
      eexists. split.
      { eapply exec_straight_two; cbn; reflexivity. }
      cbn. split.
      * intros b EVAL. cbn in EVAL.
        destruct (rs (IR x)) eqn:Hr0; cbn in *; try discriminate;
        destruct (rs (IR x0)) eqn:Hr1; cbn in *; try discriminate;
        destruct (rs (IR x1)) eqn:Hr2; cbn in *; try discriminate;
        destruct (rs (IR x2)) eqn:Hr3; cbn in *; try discriminate.
        inv EVAL.
        rewrite ! Pregmap.gso by discriminate.
        unfold compare_int_sbc; cbn.
        rewrite Pregmap.gss.
        rewrite ! Pregmap.gso by discriminate.
        rewrite Pregmap.gss.
        unfold compare_int.
        rewrite ! Pregmap.gso by discriminate.
        rewrite ! Pregmap.gss.
        rewrite Hr0, Hr2.
        cbn.
        replace (if negb (Int.ltu i2 i0) then Vtrue else Vfalse)
          with (Vint (if Int.ltu i2 i0 then Int.zero else Int.one))
          by (destruct (Int.ltu i2 i0); reflexivity).
        cbn.
        pose proof (cmp_sbcs_signed_correct i1 i2 i i0) as Hsign;
          cbv zeta in Hsign; rewrite Hsign.
        destruct (Int64.lt (Int64.ofwords i1 i2) (Int64.ofwords i i0));
          reflexivity.
      * intros r DR. cbn.
        rewrite Pregmap.gso
          by (intro Heq; subst r; cbn in DR; discriminate).
        unfold compare_int_sbc, compare_int; cbn.
        rewrite ! Pregmap.gso
          by (intro Heq; subst r; cbn in DR; discriminate).
        reflexivity.
    + (* Cge signed *)
      eexists. split.
      { eapply exec_straight_two; cbn; reflexivity. }
      cbn. split.
      * intros b EVAL. cbn in EVAL.
        destruct (rs (IR x)) eqn:Hr0; cbn in *; try discriminate;
        destruct (rs (IR x0)) eqn:Hr1; cbn in *; try discriminate;
        destruct (rs (IR x1)) eqn:Hr2; cbn in *; try discriminate;
        destruct (rs (IR x2)) eqn:Hr3; cbn in *; try discriminate.
        inv EVAL.
        rewrite ! Pregmap.gso by discriminate.
        unfold compare_int_sbc; cbn.
        rewrite Pregmap.gss.
        rewrite ! Pregmap.gso by discriminate.
        rewrite Pregmap.gss.
        unfold compare_int.
        rewrite ! Pregmap.gso by discriminate.
        rewrite ! Pregmap.gss.
        rewrite Hr0, Hr2.
        cbn.
        replace (if negb (Int.ltu i0 i2) then Vtrue else Vfalse)
          with (Vint (if Int.ltu i0 i2 then Int.zero else Int.one))
          by (destruct (Int.ltu i0 i2); reflexivity).
        cbn.
        pose proof (cmp_sbcs_signed_correct i i0 i1 i2) as Hsign;
          cbv zeta in Hsign; rewrite Hsign.
        destruct (Int64.lt (Int64.ofwords i i0) (Int64.ofwords i1 i2));
          reflexivity.
      * intros r DR. cbn.
        rewrite Pregmap.gso
          by (intro Heq; subst r; cbn in DR; discriminate).
        unfold compare_int_sbc, compare_int; cbn.
        rewrite ! Pregmap.gso
          by (intro Heq; subst r; cbn in DR; discriminate).
        reflexivity.
Qed.

Lemma transl_cond_correct':
  forall cond args k c tbb rs m,
  transl_cond cond args k = OK c ->
  exists rs',
     exec_straight ge c rs m k rs' m
  /\ (forall b,
      eval_condition cond (map rs (map preg_of args)) m = Some b ->
      eval_testcond (cond_for_cond cond) (incrPC (Ptrofs.repr (size tbb)) rs') = Some b)
  /\ forall r, data_preg r = true -> rs'#r = rs#r.
Proof.
  intros until m; intros TR.
  eapply transl_cond_correct; eauto.
Qed.

Lemma transl_cbranch_pbc:
  forall cond args lbl near_labels bdy,
  transl_cbranch cond args lbl near_labels = OK (bdy, PCtlFlow (Pbc (cond_for_cond cond) lbl)) ->
  transl_cond cond args nil = OK bdy.
Proof.
  intros. unfold transl_cbranch in H.
  destruct cond; try (monadInv H; auto; fail).
  all: destruct c; try (monadInv H; auto; fail).
  all: destruct args as [|a1 [|]]; try discriminate.
  all: destruct (Compopts.thumb tt && Compopts.short_branches tt && Int.eq _ Int.zero && is_near_label lbl near_labels).
  all: try (monadInv H; auto; fail).
  all: monadInv H; destruct (is_low_ireg _); monadInv EQ0; auto.
Qed.

Definition outcome_undef_flags (o: outcome) : outcome :=
  match o with Next rs m => Next (undef_flags rs) m | Stuck => Stuck end.

Lemma transl_cbranch_correct_gen:
  forall f cond args lbl near_labels b m rs tbb bdy ctl,
  transl_cbranch cond args lbl near_labels = OK (bdy, ctl) ->
  eval_condition cond (map rs (map preg_of args)) m = Some b ->
  exists rs' cfi (wrap: outcome -> outcome),
     ctl = PCtlFlow cfi
  /\ exec_straight_opt ge bdy rs m nil rs' m
  /\ exec_cfi ge f cfi (incrPC (Ptrofs.repr (size tbb)) rs') m =
        wrap (if b then goto_label f lbl (incrPC (Ptrofs.repr (size tbb)) rs') m else
        Next (incrPC (Ptrofs.repr (size tbb)) rs') m)
  /\ (wrap Stuck = Stuck)
  /\ (forall rs0 m0, exists rs1, wrap (Next rs0 m0) = Next rs1 m0
        /\ (forall r, data_preg r = true -> rs1 r = rs0 r)
        /\ rs1 PC = rs0 PC
        /\ rs1 IR14 = rs0 IR14)
  /\ forall r, data_preg r = true -> rs'#r = rs#r.
Proof.
  assert (wrap_id_props: forall (f0: outcome -> outcome),
    f0 = (fun x => x) ->
    f0 Stuck = Stuck /\
    (forall rs0 m0, exists rs1, f0 (Next rs0 m0) = Next rs1 m0
      /\ (forall r, data_preg r = true -> rs1 r = rs0 r) /\ rs1 PC = rs0 PC /\ rs1 IR14 = rs0 IR14)).
  { intros. subst. split; auto. intros. eexists; split; [reflexivity | repeat split; auto]. }
  assert (wrap_undef_props:
    outcome_undef_flags Stuck = Stuck /\
    (forall rs0 m0, exists rs1, outcome_undef_flags (Next rs0 m0) = Next rs1 m0
      /\ (forall r, data_preg r = true -> rs1 r = rs0 r) /\ rs1 PC = rs0 PC /\ rs1 IR14 = rs0 IR14)).
  { split; auto. intros. eexists; split; [reflexivity |]. simpl.
    repeat split; auto; intros; unfold undef_flags; destruct r; simpl in *; auto; discriminate. }
  intros until ctl. intros TR EV.
  unfold transl_cbranch in TR.
  destruct cond;
  try (monadInv TR;
    exploit transl_cond_correct'; eauto; intros (rs' & A & B & C);
    do 2 eexists; exists (fun x => x); split; [reflexivity |]; split; [apply exec_straight_opt_intro; eexact A |];
    split; [simpl; rewrite (B b) by auto; auto | split; [auto | split; [intros; eexists; split; [reflexivity | split; auto] | auto]]]).
  (* Ccompimm *)
  - destruct c;
    try (monadInv TR;
      exploit transl_cond_correct'; eauto; intros (rs' & A & B & C);
      do 2 eexists; exists (fun x => x); split; [reflexivity |]; split; [apply exec_straight_opt_intro; eexact A |];
      split; [simpl; rewrite (B b) by auto; auto | split; [auto | split; [intros; eexists; split; [reflexivity | split; auto] | auto]]]).
    + destruct args as [| a1 [|]]; try discriminate.
      destruct (Compopts.thumb tt && Compopts.short_branches tt && Int.eq i Int.zero && is_near_label lbl near_labels) eqn:Hn;
        [monadInv TR; destruct (is_low_ireg x) eqn:Hlow; monadInv EQ0 | monadInv TR].
      * do 2 eexists; exists (fun x => x); split; [reflexivity |]; split; [apply exec_straight_opt_intro; apply exec_straight_one; simpl; auto |].
        split; [| split; [auto | split; [intros; eexists; split; [reflexivity | split; auto] | auto]]]. simpl.
        unfold eval_condition in *.
        assert (Hgso: (incrPC (Ptrofs.repr (size tbb)) rs) x = rs x)
          by (unfold incrPC; rewrite Pregmap.gso; auto; congruence).
        simpl in EV. rewrite (ireg_of_eq _ _ EQ) in EV.
        unfold eval_branch. rewrite Hgso.
        unfold eval_testzero, Val.mxcmpu_bool, Val.cmpu_bool, Val.cmp_bool, Int.cmp, Int.cmpu in *.
        destruct (rs x); cbv beta iota in *; try congruence.
        (* Vint case: EV gives us the equation *)
        apply andb_true_iff in Hn. destruct Hn as [Hn _].
        apply andb_true_iff in Hn. destruct Hn as [_ Hn].
        apply Int.same_if_eq in Hn. subst i.
        injection EV; intro Hb; subst b. reflexivity.
      * exploit transl_cond_correct'; [eassumption|].
        intros (rs' & A & B & C).
        do 2 eexists; exists (fun x => x); split; [reflexivity |]; split; [apply exec_straight_opt_intro; eexact A |].
        split; [simpl; rewrite (B b) by auto; auto | split; [auto | split; [intros; eexists; split; [reflexivity | split; auto] | auto]]].
      * exploit transl_cond_correct'; [eassumption|].
        intros (rs' & A & B & C).
        do 2 eexists; exists (fun x => x); split; [reflexivity |]; split; [apply exec_straight_opt_intro; eexact A |].
        split; [simpl; rewrite (B b) by auto; auto | split; [auto | split; [intros; eexists; split; [reflexivity | split; auto] | auto]]].
    + destruct args as [| a1 [|]]; try discriminate.
      destruct (Compopts.thumb tt && Compopts.short_branches tt && Int.eq i Int.zero && is_near_label lbl near_labels) eqn:Hn;
        [monadInv TR; destruct (is_low_ireg x) eqn:Hlow; monadInv EQ0 | monadInv TR].
      * do 2 eexists; exists (fun x => x); split; [reflexivity |]; split; [apply exec_straight_opt_intro; apply exec_straight_one; simpl; auto |].
        split; [| split; [auto | split; [intros; eexists; split; [reflexivity | split; auto] | auto]]]. simpl.
        unfold eval_condition in *.
        assert (Hgso: (incrPC (Ptrofs.repr (size tbb)) rs) x = rs x)
          by (unfold incrPC; rewrite Pregmap.gso; auto; congruence).
        simpl in EV. rewrite (ireg_of_eq _ _ EQ) in EV.
        unfold eval_neg_branch. rewrite Hgso.
        unfold eval_testzero, Val.mxcmpu_bool, Val.cmpu_bool, Val.cmp_bool, Int.cmp, Int.cmpu in *.
        destruct (rs x); cbv beta iota in *; try congruence.
        (* Vint case: EV gives us the equation *)
        apply andb_true_iff in Hn. destruct Hn as [Hn _].
        apply andb_true_iff in Hn. destruct Hn as [_ Hn].
        apply Int.same_if_eq in Hn. subst i.
        injection EV; intro Hb; subst b.
        destruct (Int.eq _ _); reflexivity.
      * exploit transl_cond_correct'; [eassumption|].
        intros (rs' & A & B & C).
        do 2 eexists; exists (fun x => x); split; [reflexivity |]; split; [apply exec_straight_opt_intro; eexact A |].
        split; [simpl; rewrite (B b) by auto; auto | split; [auto | split; [intros; eexists; split; [reflexivity | split; auto] | auto]]].
      * exploit transl_cond_correct'; [eassumption|].
        intros (rs' & A & B & C).
        do 2 eexists; exists (fun x => x); split; [reflexivity |]; split; [apply exec_straight_opt_intro; eexact A |].
        split; [simpl; rewrite (B b) by auto; auto | split; [auto | split; [intros; eexists; split; [reflexivity | split; auto] | auto]]].
  (* Ccompuimm *)
  - destruct c;
    try (monadInv TR;
      exploit transl_cond_correct'; eauto; intros (rs' & A & B & C);
      do 2 eexists; exists (fun x => x); split; [reflexivity |]; split; [apply exec_straight_opt_intro; eexact A |];
      split; [simpl; rewrite (B b) by auto; auto | split; [auto | split; [intros; eexists; split; [reflexivity | split; auto] | auto]]]).
    + destruct args as [| a1 [|]]; try discriminate.
      destruct (Compopts.thumb tt && Compopts.short_branches tt && Int.eq i Int.zero && is_near_label lbl near_labels) eqn:Hn;
        [monadInv TR; destruct (is_low_ireg x) eqn:Hlow; monadInv EQ0 | monadInv TR].
      * do 2 eexists; exists (fun x => x); split; [reflexivity |]; split; [apply exec_straight_opt_intro; apply exec_straight_one; simpl; auto |].
        split; [| split; [auto | split; [intros; eexists; split; [reflexivity | split; auto] | auto]]]. simpl.
        assert (Hgso: (incrPC (Ptrofs.repr (size tbb)) rs) x = rs x)
          by (unfold incrPC; rewrite Pregmap.gso; auto; congruence).
        unfold eval_branch. rewrite Hgso.
        apply andb_true_iff in Hn; destruct Hn as [Hn _].
        apply andb_true_iff in Hn; destruct Hn as [_ Hn].
        apply Int.same_if_eq in Hn; try subst.
        unfold eval_condition in EV. simpl in EV. try rewrite (ireg_of_eq _ _ EQ) in EV.
        assert (HEV: eval_testzero (rs x) = Some b).
        { unfold eval_testzero. apply (Val.mxcmpu_bool_correct (Mem.valid_pointer m)).
          exact EV. }
        rewrite HEV. destruct b; reflexivity.
      * exploit transl_cond_correct'; [eassumption|].
        intros (rs' & A & B & C).
        do 2 eexists; exists (fun x => x); split; [reflexivity |]; split; [apply exec_straight_opt_intro; eexact A |].
        split; [simpl; rewrite (B b) by auto; auto | split; [auto | split; [intros; eexists; split; [reflexivity | split; auto] | auto]]].
      * exploit transl_cond_correct'; [eassumption|].
        intros (rs' & A & B & C).
        do 2 eexists; exists (fun x => x); split; [reflexivity |]; split; [apply exec_straight_opt_intro; eexact A |].
        split; [simpl; rewrite (B b) by auto; auto | split; [auto | split; [intros; eexists; split; [reflexivity | split; auto] | auto]]].
    + destruct args as [| a1 [|]]; try discriminate.
      destruct (Compopts.thumb tt && Compopts.short_branches tt && Int.eq i Int.zero && is_near_label lbl near_labels) eqn:Hn;
        [monadInv TR; destruct (is_low_ireg x) eqn:Hlow; monadInv EQ0 | monadInv TR].
      * do 2 eexists; exists (fun x => x); split; [reflexivity |]; split; [apply exec_straight_opt_intro; apply exec_straight_one; simpl; auto |].
        split; [| split; [auto | split; [intros; eexists; split; [reflexivity | split; auto] | auto]]]. simpl.
        assert (Hgso: (incrPC (Ptrofs.repr (size tbb)) rs) x = rs x)
          by (unfold incrPC; rewrite Pregmap.gso; auto; congruence).
        unfold eval_neg_branch. rewrite Hgso.
        apply andb_true_iff in Hn; destruct Hn as [Hn _].
        apply andb_true_iff in Hn; destruct Hn as [_ Hn].
        apply Int.same_if_eq in Hn; try subst.
        unfold eval_condition in EV. simpl in EV. try rewrite (ireg_of_eq _ _ EQ) in EV.
        unfold eval_testzero, Val.mxcmpu_bool, Val.cmpu_bool.
        destruct (rs x); simpl in *; try congruence.
        injection EV; intro; subst. destruct (Int.eq _ _); reflexivity.
        all: try (destruct Archi.ptr64; simpl in *; try congruence;
                  destruct (_ && _)%bool eqn:?; simpl in *; try congruence;
                  injection EV; intro; subst; reflexivity).
      * exploit transl_cond_correct'; [eassumption|].
        intros (rs' & A & B & C).
        do 2 eexists; exists (fun x => x); split; [reflexivity |]; split; [apply exec_straight_opt_intro; eexact A |].
        split; [simpl; rewrite (B b) by auto; auto | split; [auto | split; [intros; eexists; split; [reflexivity | split; auto] | auto]]].
      * exploit transl_cond_correct'; [eassumption|].
        intros (rs' & A & B & C).
        do 2 eexists; exists (fun x => x); split; [reflexivity |]; split; [apply exec_straight_opt_intro; eexact A |].
        split; [simpl; rewrite (B b) by auto; auto | split; [auto | split; [intros; eexists; split; [reflexivity | split; auto] | auto]]].
Unshelve. all: try exact tbb.
Qed.

Lemma transl_cbranch_correct:
  forall fm f cond args lbl near_labels b m rs tbb bdy,
  transl_control fm (MBcond cond args lbl) near_labels = OK (bdy, PCtlFlow (Pbc (cond_for_cond cond) lbl)) ->
  eval_condition cond (map rs (map preg_of args)) m = Some b ->
  exists rs',
     exec_straight_opt ge bdy rs m nil rs' m
  /\ exec_cfi ge f (Pbc (cond_for_cond cond) lbl) (incrPC (Ptrofs.repr (size tbb)) rs') m =
        (if b then goto_label f lbl (incrPC (Ptrofs.repr (size tbb)) rs') m else
        Next (incrPC (Ptrofs.repr (size tbb)) rs') m)
  /\ forall r, data_preg r = true -> rs'#r = rs#r.
Proof.
  intros until bdy. intros TR EV.
  unfold transl_control in TR. monadInv TR.
  exploit transl_cbranch_pbc; eauto. intros TCOND.
  exploit transl_cond_correct'; eauto. intros (rs' & A & B & C).
  eexists; split.
  apply exec_straight_opt_intro. eexact A.
  split; auto. simpl. rewrite (B b) by auto. auto.
Qed.

Translation of arithmetic operations.

Ltac TranslOpSimpl :=
  econstructor; split;
  [ apply exec_straight_one; simpl; unfold arith_uf_fn; simpl; eauto
  | split; [try rewrite transl_shift_correct; repeat Simpl | intros; repeat Simpl] ].

Correctness of the default fallback: transl_cond + Pmovite[1, 0].
Lemma transl_op_cmp_default_correct:
  forall cmp args r k bc (rs: regset) m,
  transl_op_cmp_default cmp args r k = OK bc ->
  exists rs',
    exec_straight ge bc rs m k rs' m
    /\ Val.lessdef
        (Val.of_optbool (eval_condition cmp (map rs (map preg_of args)) m))
        (rs' r)
    /\ forall r0, data_preg r0 = true -> r0 <> r -> rs' r0 = rs r0.
Proof.
  intros cmp args r k bc rs m TR.
  unfold transl_op_cmp_default in TR.
  exploit transl_cond_correct; eauto.
  instantiate (1 := rs). instantiate (1 := m). intros [rs1 [A [B C]]].
  econstructor; split.
  eapply exec_straight_trans. eexact A. apply exec_straight_one; simpl; eauto.
  split.
  Simpl. destruct (eval_condition cmp (map rs (map preg_of args)) m) as [b|] eqn:EC; simpl; auto.
  rewrite (B b) by auto. destruct b; apply Val.lessdef_refl.
  intros r0 Dr Hne. Simpl.
Qed.

Correctness of the Cne special-case: Psubs/Padds + Pmovite TCeq r (SOreg r) (SOimm 1).
Lemma transl_op_cmp_cne_correct:
  forall cmp args r k bc (rs: regset) m,
  transl_op_cmp_cne cmp args r k = Some (OK bc) ->
  exists rs',
    exec_straight ge bc rs m k rs' m
    /\ Val.lessdef
        (Val.of_optbool (eval_condition cmp (map rs (map preg_of args)) m))
        (rs' r)
    /\ forall r0, data_preg r0 = true -> r0 <> r -> rs' r0 = rs r0.
Proof.
  intros cmp args r k bc rs m TR.
  unfold transl_op_cmp_cne in TR.
  destruct cmp; try discriminate.
  - (* Ccomp c *)
    destruct c; try discriminate.
    (* Ccomp Cne *)
    destruct args as [|a1 [|a2 [|? ?]]]; try discriminate.
    inv TR. monadInv H0.
    simpl. rewrite (ireg_of_eq _ _ EQ). rewrite (ireg_of_eq _ _ EQ1).
    eexists; split.
    { eapply exec_straight_two; reflexivity. }
    split.
    { simpl.
      destruct (rs x) as [|nx| | | |] eqn:Hx;
        destruct (rs x0) as [|ny| | | |] eqn:Hy; simpl; auto.
      assert (HCZ: ((compare_int rs (Vint nx) (Vint ny) m) # r <- (Vint (Int.sub nx ny))) CZ = compare_int rs (Vint nx) (Vint ny) m CZ).
      { unfold Pregmap.set; destruct (PregEq.eq CZ r); [discriminate|reflexivity]. }
      rewrite HCZ.
      generalize (compare_int_spec rs (Vint nx) (Vint ny) m); intros [A [B [C D]]]; rewrite B.
      change (Val.cmpu (Mem.valid_pointer m) Ceq (Vint nx) (Vint ny)) with (Val.of_bool (Int.cmpu Ceq nx ny)).
      unfold Int.cmpu; destruct (Int.eq nx ny) eqn:HE; simpl; rewrite Pregmap.gss.
      - change (Int.eq Int.one Int.one) with true; cbv iota; rewrite Pregmap.gss;
        apply Int.same_if_eq in HE; subst ny; rewrite Int.sub_idem; apply Val.lessdef_refl.
      - change (Int.eq Int.zero Int.one) with false; cbv iota; apply Val.lessdef_refl. }
    { intros r0 Dr Hne. simpl. Simpl.
      rewrite compare_int_inv by auto. reflexivity. }
  - (* Ccompimm c i *)
    destruct c; try discriminate.
    (* Ccompimm Cne i *)
    destruct args as [|a1 [|? ?]]; try discriminate.
    destruct (is_immed_arith OTHER i) eqn:HIM.
    { inv TR. monadInv H0.
      simpl. rewrite (ireg_of_eq _ _ EQ).
      eexists; split.
      { eapply exec_straight_two; reflexivity. }
      split.
      { simpl.
        destruct (rs x) as [|nx| | | |] eqn:Hx; simpl; auto.
        assert (HCZ: ((compare_int rs (Vint nx) (Vint i) m) # r <- (Vint (Int.sub nx i))) CZ = compare_int rs (Vint nx) (Vint i) m CZ).
        { unfold Pregmap.set; destruct (PregEq.eq CZ r); [discriminate|reflexivity]. }
        rewrite HCZ.
        generalize (compare_int_spec rs (Vint nx) (Vint i) m); intros [A [B [C D]]]; rewrite B.
        change (Val.cmpu (Mem.valid_pointer m) Ceq (Vint nx) (Vint i)) with (Val.of_bool (Int.cmpu Ceq nx i)).
        unfold Int.cmpu; destruct (Int.eq nx i) eqn:HE; simpl; rewrite Pregmap.gss.
        - change (Int.eq Int.one Int.one) with true; cbv iota; rewrite Pregmap.gss;
          apply Int.same_if_eq in HE; subst nx; rewrite Int.sub_idem; apply Val.lessdef_refl.
        - change (Int.eq Int.zero Int.one) with false; cbv iota; apply Val.lessdef_refl. }
      { intros r0 Dr Hne. simpl. Simpl.
        rewrite compare_int_inv by auto. reflexivity. } }
    destruct (is_immed_arith OTHER (Int.neg i)) eqn:HIMN; try discriminate.
    inv TR. monadInv H0.
    simpl. rewrite (ireg_of_eq _ _ EQ).
    eexists; split.
    { eapply exec_straight_two; reflexivity. }
    split.
    { simpl. unfold compare_int_add.
      destruct (rs x) as [|nx| | | |] eqn:Hx; simpl; auto.
      change (Val.neg (Vint (Int.neg i))) with (Vint (Int.neg (Int.neg i))).
      rewrite Int.neg_involutive.
      replace (Int.add nx (Int.neg i)) with (Int.sub nx i)
        by (rewrite Int.sub_add_opp; reflexivity).
      assert (HCZ: ((compare_int rs (Vint nx) (Vint i) m) # r <- (Vint (Int.sub nx i))) CZ = compare_int rs (Vint nx) (Vint i) m CZ).
      { unfold Pregmap.set; destruct (PregEq.eq CZ r); [discriminate|reflexivity]. }
      rewrite HCZ.
      generalize (compare_int_spec rs (Vint nx) (Vint i) m); intros [A [B [C D]]]; rewrite B.
      change (Val.cmpu (Mem.valid_pointer m) Ceq (Vint nx) (Vint i)) with (Val.of_bool (Int.cmpu Ceq nx i)).
      unfold Int.cmpu; destruct (Int.eq nx i) eqn:HE; simpl; rewrite Pregmap.gss.
      - change (Int.eq Int.one Int.one) with true; cbv iota; rewrite Pregmap.gss;
        apply Int.same_if_eq in HE; subst nx; rewrite Int.sub_idem; apply Val.lessdef_refl.
      - change (Int.eq Int.zero Int.one) with false; cbv iota; apply Val.lessdef_refl. }
    { intros r0 Dr Hne. simpl. Simpl.
      unfold compare_int_add.
      change (Val.neg (Vint (Int.neg i))) with (Vint (Int.neg (Int.neg i))).
      rewrite Int.neg_involutive.
      rewrite compare_int_inv by auto. reflexivity. }
Qed.

Correctness of the Ceq CLZ special-case: Psub/Padd + Pclz + Pmov(lsr #5).
Lemma transl_op_cmp_ceq_clz_correct:
  forall cmp args r k bc (rs: regset) m,
  transl_op_cmp_ceq_clz cmp args r k = Some (OK bc) ->
  exists rs',
    exec_straight ge bc rs m k rs' m
    /\ Val.lessdef
        (Val.of_optbool (eval_condition cmp (map rs (map preg_of args)) m))
        (rs' r)
    /\ forall r0, data_preg r0 = true -> r0 <> r -> rs' r0 = rs r0.
Proof.
  intros cmp args r k bc rs m TR.
  unfold transl_op_cmp_ceq_clz in TR.
  destruct cmp; try discriminate.
  - (* Ccomp c *)
    destruct c; try discriminate.
    (* Ccomp Ceq *)
    destruct args as [|a1 [|a2 [|? ?]]]; try discriminate.
    inv TR. monadInv H0.
    simpl. rewrite (ireg_of_eq _ _ EQ). rewrite (ireg_of_eq _ _ EQ1).
    eexists; split.
    { eapply exec_straight_three; reflexivity. }
    split.
    { simpl. unfold undef_flags. Simpl.
      unfold Val.cmp_bool, Val.of_optbool.
      destruct (rs x) as [|nx| | | |] eqn:Hx;
        destruct (rs x0) as [|ny| | | |] eqn:Hy; simpl; auto.
      change (Int.ltu (Int.repr 5) Int.iwordsize) with true. simpl.
      rewrite int_clz_shru5. rewrite int_sub_eq_zero.
      destruct (Int.eq nx ny); apply Val.lessdef_refl. }
    { intros r0 Dr Hne. simpl. unfold undef_flags. Simpl. }
  - (* Ccompshift c sh *)
    destruct c; try discriminate.
    (* Ccompshift Ceq sh *)
    destruct args as [|a1 [|a2 [|? ?]]]; try discriminate.
    inv TR. monadInv H0.
    simpl. rewrite (ireg_of_eq _ _ EQ). rewrite (ireg_of_eq _ _ EQ1).
    eexists; split.
    { eapply exec_straight_three; reflexivity. }
    split.
    { simpl. unfold undef_flags. Simpl.
      rewrite transl_shift_correct.
      unfold Val.cmp_bool, Val.of_optbool.
      destruct (rs x) as [|nx| | | |] eqn:Hx; simpl; auto;
      destruct (eval_shift s (rs x0)) as [|ny| | | |] eqn:Hsh; simpl; auto.
      change (Int.ltu (Int.repr 5) Int.iwordsize) with true. simpl.
      rewrite int_clz_shru5. rewrite int_sub_eq_zero.
      destruct (Int.eq nx ny); apply Val.lessdef_refl. }
    { intros r0 Dr Hne. simpl. unfold undef_flags. Simpl. }
  - (* Ccompimm c i *)
    destruct c; try discriminate.
    (* Ccompimm Ceq i *)
    destruct args as [|a1 [|? ?]]; try discriminate.
    destruct (Int.eq i Int.zero) eqn:HNZ.
    { inv TR. monadInv H0.
      simpl. rewrite (ireg_of_eq _ _ EQ).
      eexists; split.
      { eapply exec_straight_two; reflexivity. }
      split.
      { simpl. Simpl. unfold Val.cmp_bool, Val.of_optbool.
        destruct (rs x) as [|nx| | | |] eqn:Hx; simpl; auto.
        change (Int.ltu (Int.repr 5) Int.iwordsize) with true. simpl.
        rewrite int_clz_shru5.
        apply Int.same_if_eq in HNZ. subst i.
        unfold Int.cmp. destruct (Int.eq nx Int.zero); simpl; apply Val.lessdef_refl. }
      { intros r0 Dr Hne. simpl. unfold undef_flags. Simpl. } }
    destruct (is_immed_arith OTHER i) eqn:HIM.
    { inv TR. monadInv H0.
      simpl. rewrite (ireg_of_eq _ _ EQ).
      eexists; split.
      { eapply exec_straight_three; reflexivity. }
      split.
      { simpl. unfold undef_flags. Simpl.
        unfold Val.cmp_bool, Val.of_optbool.
        destruct (rs x) as [|nx| | | |] eqn:Hx; simpl; auto.
        change (Int.ltu (Int.repr 5) Int.iwordsize) with true. simpl.
        rewrite int_clz_shru5. rewrite int_sub_eq_zero.
        unfold Int.cmp. destruct (Int.eq nx i); simpl; apply Val.lessdef_refl. }
      { intros r0 Dr Hne. simpl. unfold undef_flags. Simpl. } }
    destruct (is_immed_arith OTHER (Int.neg i)) eqn:HIMN; try discriminate.
    inv TR. monadInv H0.
    simpl. rewrite (ireg_of_eq _ _ EQ).
    eexists; split.
    { eapply exec_straight_three; reflexivity. }
    split.
    { simpl. unfold undef_flags. Simpl.
      unfold Val.cmp_bool, Val.of_optbool.
      destruct (rs x) as [|nx| | | |] eqn:Hx; simpl; auto.
      change (Int.ltu (Int.repr 5) Int.iwordsize) with true. simpl.
      rewrite int_clz_shru5.
      replace (Int.add nx (Int.neg i)) with (Int.sub nx i)
        by (rewrite Int.sub_add_opp; reflexivity).
      rewrite int_sub_eq_zero.
      unfold Int.cmp. destruct (Int.eq nx i); simpl; apply Val.lessdef_refl. }
    { intros r0 Dr Hne. simpl. unfold undef_flags. Simpl. }
Qed.

Correctness of transl_op_cmp: dispatches to the right helper.
Lemma transl_op_cmp_correct:
  forall cmp args r k bc (rs: regset) m,
  transl_op_cmp cmp args r k = OK bc ->
  exists rs',
    exec_straight ge bc rs m k rs' m
    /\ Val.lessdef
        (Val.of_optbool (eval_condition cmp (map rs (map preg_of args)) m))
        (rs' r)
    /\ forall r0, data_preg r0 = true -> r0 <> r -> rs' r0 = rs r0.
Proof.
  intros cmp args r k bc rs m TR.
  unfold transl_op_cmp in TR.
  destruct (transl_op_cmp_cne cmp args r k) as [bc_cne|] eqn:CNE.
  - (* Cne special-case fired; TR : bc_cne = OK bc after iota *)
    rewrite TR in CNE.
    eapply transl_op_cmp_cne_correct; exact CNE.
  - destruct (Compopts.cmp_via_clz tt).
    + (* clz mode on *)
      destruct (transl_op_cmp_ceq_clz cmp args r k) as [bc_clz|] eqn:CEQ.
      * rewrite TR in CEQ.
        eapply transl_op_cmp_ceq_clz_correct; exact CEQ.
      * eapply transl_op_cmp_default_correct; exact TR.
    + (* clz mode off *)
      eapply transl_op_cmp_default_correct; exact TR.
Qed.


Lemma transl_op_correct_same:
  forall op args res k c (rs: regset) m v,
  transl_op op args res k = OK c ->
  eval_operation ge rs#IR13 op (map rs (map preg_of args)) m = Some v ->
  match op with
  | Ocopy | Ocopyimm _ | Ocmp _ | Osel _ | Oselimm _ _ | Oselimm2 _ _ _
  | Ocmpcarryu _ | Ocmpcarry _ | Oaddrstack _ => False
  | _ => True end ->
  exists rs',
     exec_straight ge c rs m k rs' m
  /\ rs'#(preg_of res) = v
  /\ forall r, data_preg r = true -> r <> preg_of res -> preg_notin r (destroyed_by_op op) -> rs'#r = rs#r.
Proof.
  intros until v; intros TR EV NOCMP.
  unfold transl_op in TR; destruct op; ArgsInv; simpl in EV; inv EV; try (TranslOpSimpl; fail).
  (* Omove *)
  - destruct (preg_of res) eqn:RES; try destruct d; try discriminate;
    destruct (preg_of m0) eqn:ARG; try destruct d; inv TR.
    econstructor; split. apply exec_straight_one; simpl; eauto. intuition Simpl.
    econstructor; split. apply exec_straight_one; simpl; eauto. intuition Simpl.
  (* Ocopy *)
  - contradiction.
  (* Ocopyimm *)
  - contradiction.
  (* Ointconst *)
  - generalize (loadimm_correct x i k rs m). intros [rs' [A [B C]]].
    exists rs'; auto with asmgen.
  (* Ofloatconst *)
  - destruct (Compopts.optim_double_zero_via_int tt) eqn:OPT.
    + destruct (Float.eq_dec f Float.zero) as [Hzero|Hnz].
      * (* zero case: loadimm IR14 0 ; Pdouble_of_iibits x IR14 IR14 *)
        subst f. simpl in EQ0. inv EQ0.
        generalize (loadimm_correct IR14 Int.zero
                      (Pdouble_of_iibits x IR14 IR14 ::bi k) rs m).
        intros (rs1 & A1 & B1 & C1).
        assert (HZ: Float.from_words Int.zero Int.zero = Float.zero).
        { Local Transparent Float.from_words Float.of_bits Float.zero.
          unfold Float.from_words.
          change (Int64.ofwords Int.zero Int.zero) with Int64.zero.
          apply Binary.B2FF_inj. reflexivity. }
        Local Opaque Float.from_words Float.of_bits Float.zero.
        set (rs2 := rs1 # x <- (Vfloat Float.zero)).
        exists rs2.
        split; [|split].
        ** eapply exec_straight_trans. exact A1.
           apply exec_straight_one. simpl. rewrite B1. simpl. rewrite HZ. reflexivity.
        ** unfold rs2. apply Pregmap.gss.
        ** intros r DATA NE PN. unfold rs2.
           rewrite Pregmap.gso by auto.
           apply C1. apply data_preg_not_R14; auto. apply data_if_preg; auto.
      * (* nonzero, optim on *) simpl in EQ0. inv EQ0. TranslOpSimpl.
    + (* optim off *) simpl in EQ0. inv EQ0. TranslOpSimpl.
  (* Osingleconst *)
  - destruct (Compopts.optim_single_zero_via_int tt) eqn:OPT.
    + destruct (Float32.eq_dec f Float32.zero) as [Hzero|Hnz].
      * (* zero case *)
        subst f. simpl in EQ0. inv EQ0.
        generalize (loadimm_correct IR14 Int.zero
                      (Psingle_of_bits x IR14 ::bi k) rs m).
        intros (rs1 & A1 & B1 & C1).
        assert (HZ: Float32.of_bits Int.zero = Float32.zero).
        { Local Transparent Float32.of_bits Float32.zero.
          apply Binary.B2FF_inj. reflexivity. }
        eexists; split.
        ** eapply exec_straight_trans. exact A1.
           apply exec_straight_one. simpl. rewrite B1. simpl. rewrite HZ. reflexivity.
        ** split. Simpl.
           intros r DATA NE PN. rewrite Pregmap.gso by auto.
           apply C1.
           apply data_preg_not_R14; auto.
           apply data_if_preg; auto.
      * (* nonzero, optim on *) simpl in EQ0. inv EQ0. TranslOpSimpl.
    + (* optim off *) simpl in EQ0. inv EQ0. TranslOpSimpl.
  (* Oaddrstack *)
  - contradiction.
  (* Ocast8signed *)
  - destruct Archi.thumb2_support.
    econstructor; split. apply exec_straight_one; simpl; eauto. intuition Simpl.
    set (rs1 := (undef_flags rs#x <- (Val.shl rs#x0 (Vint (Int.repr 24))))).
    set (rs2 := (undef_flags rs1#x <- (Val.shr rs1#x (Vint (Int.repr 24))))).
    exists rs2.
    split. apply exec_straight_two with rs1 m; auto.
    split. unfold rs2; Simpl. unfold rs1; Simpl.
    unfold Val.shr, Val.shl; destruct (rs x0); auto.
    change (Int.ltu (Int.repr 24) Int.iwordsize) with true; simpl.
    f_equal. symmetry. apply (Int.sign_ext_shr_shl 8). compute; intuition congruence.
    intros. unfold rs2, rs1; Simpl.
  (* Ocast16signed *)
  - destruct Archi.thumb2_support.
    econstructor; split. apply exec_straight_one; simpl; eauto. intuition Simpl.
    set (rs1 := (undef_flags rs#x <- (Val.shl rs#x0 (Vint (Int.repr 16))))).
    set (rs2 := (undef_flags rs1#x <- (Val.shr rs1#x (Vint (Int.repr 16))))).
    exists rs2.
    split. apply exec_straight_two with rs1 m; auto.
    split. unfold rs2; Simpl. unfold rs1; Simpl.
    unfold Val.shr, Val.shl; destruct (rs x0); auto.
    change (Int.ltu (Int.repr 16) Int.iwordsize) with true; simpl.
    f_equal. symmetry. apply (Int.sign_ext_shr_shl 16). compute; intuition congruence.
    intros. unfold rs2, rs1; Simpl.
  (* Oaddimm *)
  - generalize (addimm_correct x x0 i k rs m).
    intros [rs' [A [B C]]].
    exists rs'; auto with asmgen.
  (* Orsbimm *)
  - generalize (rsubimm_correct x x0 i k rs m).
    intros [rs' [A [B C]]].
    exists rs'; auto with asmgen.
  (* Odiv *)
  - Local Transparent destroyed_by_op.
    unfold destroyed_by_op.
    destruct (Archi.hardware_idiv tt) eqn:?; monadInv EQ3;
    econstructor; split; try apply exec_straight_one; simpl; try rewrite H0;
    try rewrite Heqb; auto;split; Simpl;
    intros; intuition Simpl.
  (* Odivu *)
  - unfold destroyed_by_op.
    destruct (Archi.hardware_idiv tt) eqn:?; monadInv EQ3;
    econstructor; split; try apply exec_straight_one; simpl; try rewrite H0;
    try rewrite Heqb; auto;split; Simpl;
    intros; intuition Simpl.
  (* Oandimm *)
  - destruct (Archi.thumb2_support && Int.eq i (Int.repr 255)) eqn:HUXTB.
    + apply andb_true_iff in HUXTB. destruct HUXTB as [_ HEQ].
      apply Int.same_if_eq in HEQ. subst i.
      econstructor; split. apply exec_straight_one; simpl; eauto.
      split. Simpl. rewrite Val.cast8unsigned_and. auto.
      intros; Simpl.
    + destruct (Archi.thumb2_support && Int.eq i (Int.repr 65535)) eqn:HUXTH.
      * apply andb_true_iff in HUXTH. destruct HUXTH as [_ HEQ].
        apply Int.same_if_eq in HEQ. subst i.
        econstructor; split. apply exec_straight_one; simpl; eauto.
        split. Simpl. rewrite Val.cast16unsigned_and. auto.
        intros; Simpl.
      * generalize (andimm_correct x x0 i k rs m).
        intros [rs' [A [B C]]].
        exists rs'; auto with asmgen.
  (* Oorimm *)
  - generalize (orimm_correct x x0 i k rs m).
    intros [rs' [A [B C]]].
    exists rs'; auto with asmgen.
  (* Oxorimm *)
  - generalize (xorimm_correct x x0 i k rs m).
    intros [rs' [A [B C]]].
    exists rs'; auto with asmgen.
  (* Oshrximm *)
  - destruct (rs x0) eqn: X0; simpl in H0; try discriminate.
    destruct (Int.ltu i (Int.repr 31)) eqn: LTU; inv H0.
    revert EQ2. predSpec Int.eq Int.eq_spec i Int.zero; intros EQ2.
    (* i = 0 *)
    + inv EQ2. econstructor.
      split. apply exec_straight_one. simpl. reflexivity. auto.
      split. Simpl. unfold Int.shrx. rewrite Int.shl_zero. unfold Int.divs.
      change (Int.signed Int.one) with 1. rewrite Z.quot_1_r. rewrite Int.repr_signed. auto.
      intros. Simpl.
    (* i <> 0 *)
    + revert EQ2. predSpec Int.eq Int.eq_spec i Int.one; intros EQ2.
      * inv EQ2.
        econstructor; split.
        eapply exec_straight_two; simpl; reflexivity.
        split.
        -- rewrite X0.
           rewrite Int.shrx1_shr by reflexivity.
           Simpl.
        -- intros.
           Simpl.
      * clear H0. inv EQ2.
        assert (LTU': Int.ltu (Int.sub Int.iwordsize i) Int.iwordsize = true).
        -- generalize (Int.ltu_inv _ _ LTU). intros.
           unfold Int.sub, Int.ltu. rewrite Int.unsigned_repr_wordsize.
           rewrite Int.unsigned_repr. apply zlt_true.
           assert (Int.unsigned i <> 0).
           { red; intros; elim H. rewrite <- (Int.repr_unsigned i). rewrite H1; reflexivity. }
           lia.
           change (Int.unsigned (Int.repr 31)) with (Int.zwordsize - 1) in H0.
           generalize Int.wordsize_max_unsigned; lia.
        -- assert (LTU'': Int.ltu i Int.iwordsize = true).
           { generalize (Int.ltu_inv _ _ LTU). intros.
             unfold Int.ltu. rewrite Int.unsigned_repr_wordsize. apply zlt_true.
             change (Int.unsigned (Int.repr 31)) with (Int.zwordsize - 1) in H0.
             lia. }
           set (j := Int.sub Int.iwordsize i) in *.
           set (rs1 := (undef_flags rs#IR14 <- (Val.shr (Vint i0) (Vint (Int.repr 31))))).
           set (rs2 := (undef_flags rs1#IR14 <- (Val.add (Vint i0) (Val.shru rs1#IR14 (Vint j))))).
           set (rs3 := (undef_flags rs2#x <- (Val.shr rs2#IR14 (Vint i)))).
           exists rs3; split.
           apply exec_straight_three with rs1 m rs2 m.
           simpl. rewrite X0; reflexivity.
           simpl. f_equal. Simpl. replace (rs1 x0) with (rs x0). rewrite X0; reflexivity.
           unfold rs1; Simpl.
           reflexivity.
           auto. auto.
           split. unfold rs3; Simpl. unfold rs2; Simpl. unfold rs1; Simpl.
           simpl. change (Int.ltu (Int.repr 31) Int.iwordsize) with true. simpl.
           rewrite LTU'; simpl. rewrite LTU''; simpl.
           f_equal. symmetry. apply Int.shrx_shr_2. assumption.
           intros. unfold rs3; Simpl. unfold rs2; Simpl. unfold rs1; Simpl.
  (* Ointoffloat *)
  - econstructor; split. apply exec_straight_one. simpl. rewrite H0; simpl. eauto.
    Transparent destroyed_by_op.
    simpl. intuition Simpl.
  (* Ointuoffloat *)
  - econstructor; split. apply exec_straight_one. simpl. rewrite H0; simpl. eauto.
    simpl. intuition Simpl.
  (* Ofloatofint *)
  - econstructor; split. apply exec_straight_one. simpl. rewrite H0; simpl. eauto.
    simpl. intuition Simpl.
  (* Ofloatofintu *)
  - econstructor; split. apply exec_straight_one. simpl. rewrite H0; simpl. eauto.
    simpl. intuition Simpl.
  (* Ointofsingle *)
  - econstructor; split. apply exec_straight_one. simpl. rewrite H0; simpl. eauto.
    simpl. intuition Simpl.
  (* Ointuofsingle *)
  - econstructor; split. apply exec_straight_one. simpl. rewrite H0; simpl. eauto.
    simpl. intuition Simpl.
  (* Osingleofint *)
  - econstructor; split. apply exec_straight_one. simpl. rewrite H0; simpl. eauto.
    simpl. intuition Simpl.
  (* Osingleofintu *)
  - econstructor; split. apply exec_straight_one. simpl. rewrite H0; simpl. eauto.
    simpl. intuition Simpl.
  (* Ocmp *)
  - contradiction.
  (* Osel *)
  - contradiction.
  (* Oselimm *)
  - contradiction.
  (* Oselimm2 *)
  - contradiction.
  (* Oubfx *)
  - destruct (Int.eq lsb Int.zero && Int.eq sz (Int.repr 8)) eqn:HUXTB.
    + apply andb_true_iff in HUXTB. destruct HUXTB as [HEQ1 HEQ2].
      apply Int.same_if_eq in HEQ1. apply Int.same_if_eq in HEQ2.
      subst lsb sz.
      econstructor; split. apply exec_straight_one; simpl; eauto. split; [Simpl|intros; Simpl].
      destruct (rs x0); simpl; auto. rewrite Int.shru_zero. auto.
    + destruct (Int.eq lsb Int.zero && Int.eq sz (Int.repr 16)) eqn:HUXTH.
      * apply andb_true_iff in HUXTH. destruct HUXTH as [HEQ1 HEQ2].
        apply Int.same_if_eq in HEQ1. apply Int.same_if_eq in HEQ2.
        subst lsb sz.
        econstructor; split. apply exec_straight_one; simpl; eauto. split; [Simpl|intros; Simpl].
        destruct (rs x0); simpl; auto. rewrite Int.shru_zero. auto.
      * TranslOpSimpl.
  (* Osbfx *)
  - destruct (Int.eq lsb Int.zero && Int.eq sze (Int.repr 8)) eqn:HSXTB.
    + apply andb_true_iff in HSXTB. destruct HSXTB as [HEQ1 HEQ2].
      apply Int.same_if_eq in HEQ1. apply Int.same_if_eq in HEQ2.
      subst lsb sze.
      econstructor; split. apply exec_straight_one; simpl; eauto. split; [Simpl|intros; Simpl].
      destruct (rs x0); simpl; auto. rewrite Int.shru_zero. auto.
    + destruct (Int.eq lsb Int.zero && Int.eq sze (Int.repr 16)) eqn:HSXTH.
      * apply andb_true_iff in HSXTH. destruct HSXTH as [HEQ1 HEQ2].
        apply Int.same_if_eq in HEQ1. apply Int.same_if_eq in HEQ2.
        subst lsb sze.
        econstructor; split. apply exec_straight_one; simpl; eauto. split; [Simpl|intros; Simpl].
        destruct (rs x0); simpl; auto. rewrite Int.shru_zero. auto.
      * TranslOpSimpl.
  (* Obfc *)
  - destruct (Archi.thumb2_support
              && Int.eq lsb (Int.repr 8) && Int.eq sz (Int.repr 24)) eqn:HUXTB.
    + apply andb_true_iff in HUXTB. destruct HUXTB as [HT HEQ24].
      apply andb_true_iff in HT. destruct HT as [_ HEQ8].
      apply Int.same_if_eq in HEQ8. apply Int.same_if_eq in HEQ24.
      subst lsb sz.
      econstructor; split. apply exec_straight_one; simpl; eauto.
      split. Simpl.
      unfold ExtValues.clearf, ExtValues.bitfield_mask, ExtValues.is_bitfield. simpl.
      rewrite Val.cast8unsigned_and. auto.
      intros; Simpl.
    + destruct (Archi.thumb2_support
                && Int.eq lsb (Int.repr 16) && Int.eq sz (Int.repr 16)) eqn:HUXTH.
      * apply andb_true_iff in HUXTH. destruct HUXTH as [HT HEQ16b].
        apply andb_true_iff in HT. destruct HT as [_ HEQ16].
        apply Int.same_if_eq in HEQ16. apply Int.same_if_eq in HEQ16b.
        subst lsb sz.
        econstructor; split. apply exec_straight_one; simpl; eauto.
        split. Simpl.
        unfold ExtValues.clearf, ExtValues.bitfield_mask, ExtValues.is_bitfield. simpl.
        rewrite Val.cast16unsigned_and. auto.
        intros; Simpl.
      * TranslOpSimpl.
  (* Oaddimm_reg *)
  - generalize (addimm_correct x x0 i k rs m).
    intros [rs' [A [B C]]].
    exists rs'; auto with asmgen.
  (* OEandimm *)
  - destruct (Archi.thumb2_support && Int.eq i (Int.repr 255)) eqn:HUXTB.
    + apply andb_true_iff in HUXTB. destruct HUXTB as [_ HEQ].
      apply Int.same_if_eq in HEQ. subst i.
      econstructor; split. apply exec_straight_one; simpl; eauto.
      split. Simpl. rewrite Val.cast8unsigned_and. auto.
      intros; Simpl.
    + destruct (Archi.thumb2_support && Int.eq i (Int.repr 65535)) eqn:HUXTH.
      * apply andb_true_iff in HUXTH. destruct HUXTH as [_ HEQ].
        apply Int.same_if_eq in HEQ. subst i.
        econstructor; split. apply exec_straight_one; simpl; eauto.
        split. Simpl. rewrite Val.cast16unsigned_and. auto.
        intros; Simpl.
      * econstructor; split.
        apply exec_straight_one; simpl; eauto.
        split; [Simpl | intros; Simpl].
  (* Ocmpcarryu *)
  - contradiction.
  (* Ocmpcarry *)
  - contradiction.
Qed.

Lemma transl_op_correct:
  forall op args res k c (rs: regset) m v,
  transl_op op args res k = OK c ->
  eval_operation ge rs#SP op (map rs (map preg_of args)) m = Some v ->
  exists rs',
     exec_straight ge c rs m k rs' m
  /\ Val.lessdef v rs'#(preg_of res)
  /\ forall r, data_preg r = true -> r <> preg_of res -> preg_notin r (destroyed_by_op op) -> rs'#r = rs#r.
Proof.
  intros.
  assert (SAME:
    (exists rs', exec_straight ge c rs m k rs' m
         /\ rs'#(preg_of res) = v
         /\ forall r, data_preg r = true -> r <> preg_of res -> preg_notin r (destroyed_by_op op) -> rs'#r = rs#r) ->
     exists rs', exec_straight ge c rs m k rs' m
         /\ Val.lessdef v rs'#(preg_of res)
         /\ forall r, data_preg r = true -> r <> preg_of res -> preg_notin r (destroyed_by_op op) -> rs'#r = rs#r).
  { intros (rs' & A & B & C). subst v; exists rs'; auto. }
  destruct op; try (apply SAME; eapply transl_op_correct_same; eauto; fail).
  (* Ocopy *)
  - clear SAME; simpl in *; ArgsInv.
    cbn in H0. inv H0.
    eexists. split.
    { apply exec_straight_one; reflexivity. }
    split.
    { apply Val.lessdef_refl. }
    reflexivity.
  (* Ocopy *)
  - clear SAME; simpl in *; ArgsInv.
    cbn in H0. inv H0.
    eexists. split.
    { apply exec_straight_one; reflexivity. }
    split.
    { apply Val.lessdef_refl. }
    reflexivity.
  (* Oaddrstack *)
  - clear SAME; simpl in *; ArgsInv.
    destruct (addimm_correct x IR13 (Ptrofs.to_int i) k rs m) as [rs' [EX [RES OTH]]].
    exists rs'; split. auto. split.
    rewrite RES; inv H0. destruct (rs IR13); simpl; auto. rewrite Ptrofs.of_int_to_int; auto.
    intros; apply OTH; eauto with asmgen.
  (* Ocmp *)
  - clear SAME. simpl in H. monadInv H. rename x into r.
    rewrite (ireg_of_eq _ _ EQ).
    exploit transl_op_cmp_correct; eauto. intros [rs' [A [B C]]].
    exists rs'; split; [exact A|split; [|intros; eapply C; eauto]].
    simpl in H0. inv H0. exact B.
  (* Osel *)
  - clear SAME. unfold transl_op in H; ArgsInv. simpl in H0; inv H0.
    assert (D1: data_preg (preg_of m0) = true) by auto with asmgen.
    assert (D2: data_preg (preg_of m1) = true) by auto with asmgen.
    destruct (preg_of res) as [[ir|fr]|cr|] eqn:RES;
    monadInv H.
    + inv EQ2. rewrite (ireg_of_eq _ _ EQ), (ireg_of_eq _ _ EQ1) in *.
      destruct (ireg_eq x x0); inv H0.
      * econstructor; split.
        apply exec_straight_one; simpl; eauto.
        split; intros; Simpl.
        destruct (eval_condition c0 (map rs (map preg_of args)) m); simpl; eauto.
        destruct b; eauto using Val.lessdef_normalize.
      * exploit transl_cond_correct; eauto. instantiate (1 := rs). instantiate (1 := m). intros (rs1 & A & B & C).
        econstructor; split.
        eapply exec_straight_trans. eexact A. apply exec_straight_one. simpl; eauto. auto.
        split; intros; Simpl.
        destruct (eval_condition c0 (map rs (map preg_of args)) m) as [b|]; simpl; auto.
        rewrite (B b) by auto.
        destruct b; rewrite !C by auto; apply Val.lessdef_refl.
    + inv EQ2. rewrite (freg_of_eq _ _ EQ), (freg_of_eq _ _ EQ1) in *.
      destruct (freg_eq x x0); inv H0.
      * econstructor; split.
        apply exec_straight_one; simpl; eauto.
        split; intros; Simpl.
        destruct (eval_condition c0 (map rs (map preg_of args)) m); simpl; eauto.
        destruct b; eauto using Val.lessdef_normalize.
      * exploit transl_cond_correct; eauto. instantiate (1 := rs). instantiate (1 := m). intros [rs1 [A [B C]]].
        econstructor; split.
        eapply exec_straight_trans. eexact A. apply exec_straight_one. simpl; eauto. auto.
        split; intros; Simpl.
        rewrite ! C by auto.
        destruct (eval_condition c0 (map rs (map preg_of args)) m) as [b|]; simpl; auto.
        rewrite (B b) by auto. destruct b; apply Val.lessdef_refl.
  (* Oselimm *)
  - clear SAME. unfold transl_op in H; ArgsInv.
    simpl in H0; inv H0.
    assert (D1: data_preg (preg_of m0) = true) by auto with asmgen.
    rewrite (ireg_of_eq _ _ EQ1) in *.
    exploit transl_cond_correct; eauto. instantiate (1 := rs). instantiate (1 := m). intros (rs1 & A & B & C).
    econstructor; split.
    eapply exec_straight_trans. eexact A. apply exec_straight_one. simpl; eauto. auto.
    split; intros; Simpl.
    destruct (eval_condition c0 (map rs (map preg_of args)) m) as [b|]; simpl; auto.
    rewrite (B b) by auto.
    destruct b; simpl.
    + rewrite C by auto. apply Val.lessdef_refl.
    + apply Val.lessdef_refl.
  (* Oselimm2 *)
  - clear SAME. simpl in H. monadInv H. simpl in H0. inv H0.
    rewrite (ireg_of_eq _ _ EQ).
    exploit transl_cond_correct; eauto. instantiate (1 := rs). instantiate (1 := m). intros (rs1 & A & B & C).
    econstructor; split.
    eapply exec_straight_trans. eexact A. apply exec_straight_one. simpl; eauto. auto.
    split; intros; Simpl.
    destruct (eval_condition c0 (map rs (map preg_of args)) m) as [b|]; simpl; auto.
    rewrite (B b) by auto. destruct b; apply Val.lessdef_refl.
  (* Ocmpcarryu — semantic correctness of cmp+sbcs+movite for unsigned 64-bit
     ordered comparisons.  Uses [cmp_sbcs_carry_correct] above.  For
     non-Vint inputs the abstract value is [Vundef] and lessdef is trivial;
     for all-Vint inputs the carry-flag identity discharges all 4 cases. *)

  - clear SAME. unfold transl_op in H. ArgsInv. cbn in H0. inv H0.
    destruct c0; cbn in EQ5; inv EQ5.
    + (* Clt: cmp lo1,lo2; sbcs r,hi1,hi2; TClo *)
      pose (rs1 := compare_int rs (rs x1) (rs x3) m).
      assert (Hx0: rs1 x0 = rs x0)
        by (subst rs1; apply compare_int_inv;
            rewrite <- (ireg_of_eq _ _ EQ1); auto with asmgen).
      assert (Hx2: rs1 x2 = rs x2)
        by (subst rs1; apply compare_int_inv;
            rewrite <- (ireg_of_eq _ _ EQ2); auto with asmgen).
      assert (HCC: rs1 CC = Val.cmpu (Mem.valid_pointer m) Cge (rs x1) (rs x3))
        by (subst rs1; reflexivity).
      eexists. split. eapply exec_straight_three; cbn; reflexivity.
      fold rs1; rewrite Hx0, Hx2, HCC. split.
      * rewrite Pregmap.gss; rewrite Pregmap.gso by discriminate;
        unfold compare_int_sbc; cbn;
        rewrite Pregmap.gso by discriminate; rewrite Pregmap.gss;
        destruct (rs x0) eqn:Hr0; cbn; try (constructor; fail);
        destruct (rs x1) eqn:Hr1; cbn; try (constructor; fail);
        destruct (rs x2) eqn:Hr2; cbn; try (constructor; fail);
        destruct (rs x3) eqn:Hr3; cbn; try (constructor; fail);
        replace (if negb (Int.ltu i0 i2) then Vtrue else Vfalse)
          with (Vint (if Int.ltu i0 i2 then Int.zero else Int.one))
          by (destruct (Int.ltu i0 i2); reflexivity);
        rewrite (cmp_sbcs_carry_correct i i0 i1 i2);
        destruct (Int64.ltu (Int64.ofwords i i0) (Int64.ofwords i1 i2));
        apply Val.lessdef_refl.
      * intros r DR Hne _; rewrite Pregmap.gso by auto;
        rewrite Pregmap.gso by auto; unfold compare_int_sbc; cbn;
        rewrite ! Pregmap.gso by auto with asmgen;
        subst rs1; apply compare_int_inv; auto.
    + (* Cle: cmp lo2,lo1; sbcs r,hi2,hi1; TChs *)
      pose (rs1 := compare_int rs (rs x3) (rs x1) m).
      assert (Hx0: rs1 x0 = rs x0)
        by (subst rs1; apply compare_int_inv;
            rewrite <- (ireg_of_eq _ _ EQ1); auto with asmgen).
      assert (Hx2: rs1 x2 = rs x2)
        by (subst rs1; apply compare_int_inv;
            rewrite <- (ireg_of_eq _ _ EQ2); auto with asmgen).
      assert (HCC: rs1 CC = Val.cmpu (Mem.valid_pointer m) Cge (rs x3) (rs x1))
        by (subst rs1; reflexivity).
      eexists. split. eapply exec_straight_three; cbn; reflexivity.
      fold rs1; rewrite Hx0, Hx2, HCC. split.
      * rewrite Pregmap.gss; rewrite Pregmap.gso by discriminate;
        unfold compare_int_sbc; cbn;
        rewrite Pregmap.gso by discriminate; rewrite Pregmap.gss;
        destruct (rs x0) eqn:Hr0; cbn; try (constructor; fail);
        destruct (rs x1) eqn:Hr1; cbn; try (constructor; fail);
        destruct (rs x2) eqn:Hr2; cbn; try (constructor; fail);
        destruct (rs x3) eqn:Hr3; cbn; try (constructor; fail);
        replace (if negb (Int.ltu i2 i0) then Vtrue else Vfalse)
          with (Vint (if Int.ltu i2 i0 then Int.zero else Int.one))
          by (destruct (Int.ltu i2 i0); reflexivity);
        rewrite (cmp_sbcs_carry_correct i1 i2 i i0);
        destruct (Int64.ltu (Int64.ofwords i1 i2) (Int64.ofwords i i0));
        apply Val.lessdef_refl.
      * intros r DR Hne _; rewrite Pregmap.gso by auto;
        rewrite Pregmap.gso by auto; unfold compare_int_sbc; cbn;
        rewrite ! Pregmap.gso by auto with asmgen;
        subst rs1; apply compare_int_inv; auto.
    + (* Cgt: cmp lo2,lo1; sbcs r,hi2,hi1; TClo *)
      pose (rs1 := compare_int rs (rs x3) (rs x1) m).
      assert (Hx0: rs1 x0 = rs x0)
        by (subst rs1; apply compare_int_inv;
            rewrite <- (ireg_of_eq _ _ EQ1); auto with asmgen).
      assert (Hx2: rs1 x2 = rs x2)
        by (subst rs1; apply compare_int_inv;
            rewrite <- (ireg_of_eq _ _ EQ2); auto with asmgen).
      assert (HCC: rs1 CC = Val.cmpu (Mem.valid_pointer m) Cge (rs x3) (rs x1))
        by (subst rs1; reflexivity).
      eexists. split. eapply exec_straight_three; cbn; reflexivity.
      fold rs1; rewrite Hx0, Hx2, HCC. split.
      * rewrite Pregmap.gss; rewrite Pregmap.gso by discriminate;
        unfold compare_int_sbc; cbn;
        rewrite Pregmap.gso by discriminate; rewrite Pregmap.gss;
        destruct (rs x0) eqn:Hr0; cbn; try (constructor; fail);
        destruct (rs x1) eqn:Hr1; cbn; try (constructor; fail);
        destruct (rs x2) eqn:Hr2; cbn; try (constructor; fail);
        destruct (rs x3) eqn:Hr3; cbn; try (constructor; fail);
        replace (if negb (Int.ltu i2 i0) then Vtrue else Vfalse)
          with (Vint (if Int.ltu i2 i0 then Int.zero else Int.one))
          by (destruct (Int.ltu i2 i0); reflexivity);
        rewrite (cmp_sbcs_carry_correct i1 i2 i i0);
        destruct (Int64.ltu (Int64.ofwords i1 i2) (Int64.ofwords i i0));
        apply Val.lessdef_refl.
      * intros r DR Hne _; rewrite Pregmap.gso by auto;
        rewrite Pregmap.gso by auto; unfold compare_int_sbc; cbn;
        rewrite ! Pregmap.gso by auto with asmgen;
        subst rs1; apply compare_int_inv; auto.
    + (* Cge: cmp lo1,lo2; sbcs r,hi1,hi2; TChs *)
      pose (rs1 := compare_int rs (rs x1) (rs x3) m).
      assert (Hx0: rs1 x0 = rs x0)
        by (subst rs1; apply compare_int_inv;
            rewrite <- (ireg_of_eq _ _ EQ1); auto with asmgen).
      assert (Hx2: rs1 x2 = rs x2)
        by (subst rs1; apply compare_int_inv;
            rewrite <- (ireg_of_eq _ _ EQ2); auto with asmgen).
      assert (HCC: rs1 CC = Val.cmpu (Mem.valid_pointer m) Cge (rs x1) (rs x3))
        by (subst rs1; reflexivity).
      eexists. split. eapply exec_straight_three; cbn; reflexivity.
      fold rs1; rewrite Hx0, Hx2, HCC. split.
      * rewrite Pregmap.gss; rewrite Pregmap.gso by discriminate;
        unfold compare_int_sbc; cbn;
        rewrite Pregmap.gso by discriminate; rewrite Pregmap.gss;
        destruct (rs x0) eqn:Hr0; cbn; try (constructor; fail);
        destruct (rs x1) eqn:Hr1; cbn; try (constructor; fail);
        destruct (rs x2) eqn:Hr2; cbn; try (constructor; fail);
        destruct (rs x3) eqn:Hr3; cbn; try (constructor; fail);
        replace (if negb (Int.ltu i0 i2) then Vtrue else Vfalse)
          with (Vint (if Int.ltu i0 i2 then Int.zero else Int.one))
          by (destruct (Int.ltu i0 i2); reflexivity);
        rewrite (cmp_sbcs_carry_correct i i0 i1 i2);
        destruct (Int64.ltu (Int64.ofwords i i0) (Int64.ofwords i1 i2));
        apply Val.lessdef_refl.
      * intros r DR Hne _; rewrite Pregmap.gso by auto;
        rewrite Pregmap.gso by auto; unfold compare_int_sbc; cbn;
        rewrite ! Pregmap.gso by auto with asmgen;
        subst rs1; apply compare_int_inv; auto.
  (* Ocmpcarry — signed analog of Ocmpcarryu using [cmp_sbcs_signed_correct]. *)
  - clear SAME. unfold transl_op in H. ArgsInv. cbn in H0. inv H0.
    destruct c0; cbn in EQ5; inv EQ5.
    + (* Clt signed *)
      pose (rs1 := compare_int rs (rs x1) (rs x3) m).
      assert (Hx0: rs1 x0 = rs x0)
        by (subst rs1; apply compare_int_inv; rewrite <- (ireg_of_eq _ _ EQ1); auto with asmgen).
      assert (Hx2: rs1 x2 = rs x2)
        by (subst rs1; apply compare_int_inv; rewrite <- (ireg_of_eq _ _ EQ2); auto with asmgen).
      assert (HCC: rs1 CC = Val.cmpu (Mem.valid_pointer m) Cge (rs x1) (rs x3))
        by (subst rs1; reflexivity).
      eexists. split. eapply exec_straight_three; cbn; reflexivity.
      fold rs1; rewrite Hx0, Hx2, HCC. split.
      * rewrite Pregmap.gss; rewrite Pregmap.gso by discriminate;
        rewrite Pregmap.gso by discriminate;
        unfold compare_int_sbc; cbn;
        rewrite Pregmap.gss;
        rewrite Pregmap.gso by discriminate;
        rewrite Pregmap.gso by discriminate;
        rewrite Pregmap.gso by discriminate;
        rewrite Pregmap.gss;
        destruct (rs x0) eqn:Hr0; cbn; try (constructor; fail);
        destruct (rs x1) eqn:Hr1; cbn; try (constructor; fail);
        destruct (rs x2) eqn:Hr2; cbn; try (constructor; fail);
        destruct (rs x3) eqn:Hr3; cbn; try (constructor; fail);
        replace (if negb (Int.ltu i0 i2) then Vtrue else Vfalse)
          with (Vint (if Int.ltu i0 i2 then Int.zero else Int.one))
          by (destruct (Int.ltu i0 i2); reflexivity);
        cbn;
        pose proof (cmp_sbcs_signed_correct i i0 i1 i2) as Hsign;
        cbv zeta in Hsign; rewrite Hsign;
        destruct (Int64.lt (Int64.ofwords i i0) (Int64.ofwords i1 i2));
        apply Val.lessdef_refl.
      * intros r DR Hne _; rewrite Pregmap.gso by auto;
        rewrite Pregmap.gso by auto; unfold compare_int_sbc; cbn;
        rewrite ! Pregmap.gso by auto with asmgen;
        subst rs1; apply compare_int_inv; auto.
    + (* Cle signed: swapped *)
      pose (rs1 := compare_int rs (rs x3) (rs x1) m).
      assert (Hx0: rs1 x0 = rs x0)
        by (subst rs1; apply compare_int_inv; rewrite <- (ireg_of_eq _ _ EQ1); auto with asmgen).
      assert (Hx2: rs1 x2 = rs x2)
        by (subst rs1; apply compare_int_inv; rewrite <- (ireg_of_eq _ _ EQ2); auto with asmgen).
      assert (HCC: rs1 CC = Val.cmpu (Mem.valid_pointer m) Cge (rs x3) (rs x1))
        by (subst rs1; reflexivity).
      eexists. split. eapply exec_straight_three; cbn; reflexivity.
      fold rs1; rewrite Hx0, Hx2, HCC. split.
      * rewrite Pregmap.gss; rewrite Pregmap.gso by discriminate;
        rewrite Pregmap.gso by discriminate;
        unfold compare_int_sbc; cbn;
        rewrite Pregmap.gss;
        rewrite Pregmap.gso by discriminate;
        rewrite Pregmap.gso by discriminate;
        rewrite Pregmap.gso by discriminate;
        rewrite Pregmap.gss;
        destruct (rs x0) eqn:Hr0; cbn; try (constructor; fail);
        destruct (rs x1) eqn:Hr1; cbn; try (constructor; fail);
        destruct (rs x2) eqn:Hr2; cbn; try (constructor; fail);
        destruct (rs x3) eqn:Hr3; cbn; try (constructor; fail);
        replace (if negb (Int.ltu i2 i0) then Vtrue else Vfalse)
          with (Vint (if Int.ltu i2 i0 then Int.zero else Int.one))
          by (destruct (Int.ltu i2 i0); reflexivity);
        cbn;
        pose proof (cmp_sbcs_signed_correct i1 i2 i i0) as Hsign;
        cbv zeta in Hsign; rewrite Hsign;
        destruct (Int64.lt (Int64.ofwords i1 i2) (Int64.ofwords i i0));
        apply Val.lessdef_refl.
      * intros r DR Hne _; rewrite Pregmap.gso by auto;
        rewrite Pregmap.gso by auto; unfold compare_int_sbc; cbn;
        rewrite ! Pregmap.gso by auto with asmgen;
        subst rs1; apply compare_int_inv; auto.
    + (* Cgt signed: swapped *)
      pose (rs1 := compare_int rs (rs x3) (rs x1) m).
      assert (Hx0: rs1 x0 = rs x0)
        by (subst rs1; apply compare_int_inv; rewrite <- (ireg_of_eq _ _ EQ1); auto with asmgen).
      assert (Hx2: rs1 x2 = rs x2)
        by (subst rs1; apply compare_int_inv; rewrite <- (ireg_of_eq _ _ EQ2); auto with asmgen).
      assert (HCC: rs1 CC = Val.cmpu (Mem.valid_pointer m) Cge (rs x3) (rs x1))
        by (subst rs1; reflexivity).
      eexists. split. eapply exec_straight_three; cbn; reflexivity.
      fold rs1; rewrite Hx0, Hx2, HCC. split.
      * rewrite Pregmap.gss; rewrite Pregmap.gso by discriminate;
        rewrite Pregmap.gso by discriminate;
        unfold compare_int_sbc; cbn;
        rewrite Pregmap.gss;
        rewrite Pregmap.gso by discriminate;
        rewrite Pregmap.gso by discriminate;
        rewrite Pregmap.gso by discriminate;
        rewrite Pregmap.gss;
        destruct (rs x0) eqn:Hr0; cbn; try (constructor; fail);
        destruct (rs x1) eqn:Hr1; cbn; try (constructor; fail);
        destruct (rs x2) eqn:Hr2; cbn; try (constructor; fail);
        destruct (rs x3) eqn:Hr3; cbn; try (constructor; fail);
        replace (if negb (Int.ltu i2 i0) then Vtrue else Vfalse)
          with (Vint (if Int.ltu i2 i0 then Int.zero else Int.one))
          by (destruct (Int.ltu i2 i0); reflexivity);
        cbn;
        pose proof (cmp_sbcs_signed_correct i1 i2 i i0) as Hsign;
        cbv zeta in Hsign; rewrite Hsign;
        destruct (Int64.lt (Int64.ofwords i1 i2) (Int64.ofwords i i0));
        apply Val.lessdef_refl.
      * intros r DR Hne _; rewrite Pregmap.gso by auto;
        rewrite Pregmap.gso by auto; unfold compare_int_sbc; cbn;
        rewrite ! Pregmap.gso by auto with asmgen;
        subst rs1; apply compare_int_inv; auto.
    + (* Cge signed *)
      pose (rs1 := compare_int rs (rs x1) (rs x3) m).
      assert (Hx0: rs1 x0 = rs x0)
        by (subst rs1; apply compare_int_inv; rewrite <- (ireg_of_eq _ _ EQ1); auto with asmgen).
      assert (Hx2: rs1 x2 = rs x2)
        by (subst rs1; apply compare_int_inv; rewrite <- (ireg_of_eq _ _ EQ2); auto with asmgen).
      assert (HCC: rs1 CC = Val.cmpu (Mem.valid_pointer m) Cge (rs x1) (rs x3))
        by (subst rs1; reflexivity).
      eexists. split. eapply exec_straight_three; cbn; reflexivity.
      fold rs1; rewrite Hx0, Hx2, HCC. split.
      * rewrite Pregmap.gss; rewrite Pregmap.gso by discriminate;
        rewrite Pregmap.gso by discriminate;
        unfold compare_int_sbc; cbn;
        rewrite Pregmap.gss;
        rewrite Pregmap.gso by discriminate;
        rewrite Pregmap.gso by discriminate;
        rewrite Pregmap.gso by discriminate;
        rewrite Pregmap.gss;
        destruct (rs x0) eqn:Hr0; cbn; try (constructor; fail);
        destruct (rs x1) eqn:Hr1; cbn; try (constructor; fail);
        destruct (rs x2) eqn:Hr2; cbn; try (constructor; fail);
        destruct (rs x3) eqn:Hr3; cbn; try (constructor; fail);
        replace (if negb (Int.ltu i0 i2) then Vtrue else Vfalse)
          with (Vint (if Int.ltu i0 i2 then Int.zero else Int.one))
          by (destruct (Int.ltu i0 i2); reflexivity);
        cbn;
        pose proof (cmp_sbcs_signed_correct i i0 i1 i2) as Hsign;
        cbv zeta in Hsign; rewrite Hsign;
        destruct (Int64.lt (Int64.ofwords i i0) (Int64.ofwords i1 i2));
        apply Val.lessdef_refl.
      * intros r DR Hne _; rewrite Pregmap.gso by auto;
        rewrite Pregmap.gso by auto; unfold compare_int_sbc; cbn;
        rewrite ! Pregmap.gso by auto with asmgen;
        subst rs1; apply compare_int_inv; auto.
Qed.

Lemma transl_memory_access_correct:
  forall (P: regset -> Prop) (mk_instr_imm: ireg -> int -> basic)
         (mk_instr_gen: option (ireg -> shift_op -> basic))
         (mk_immed: int -> int)
         addr args k c (rs: regset) a m m',
  transl_memory_access mk_instr_imm mk_instr_gen mk_immed addr args k = OK c ->
  eval_addressing ge (rs#SP) addr (map rs (map preg_of args)) = Some a ->
  match a with Vptr _ _ => True | _ => False end ->
  (forall (r1: ireg) (rs1: regset) n k,
    Val.add rs1#r1 (Vint n) = a ->
    (forall (r: preg), if_preg r = true -> r <> IR14 -> rs1 r = rs r) ->
    exists rs',
    exec_straight ge (mk_instr_imm r1 n :: k) rs1 m k rs' m' /\ P rs') ->
  match mk_instr_gen with
  | None => True
  | Some mk =>
      (forall (r1: ireg) (sa: shift_op) k,
      Val.add rs#r1 (eval_shift_op sa rs) = a ->
       exists rs',
      exec_straight ge (mk r1 sa :: k) rs m k rs' m' /\ P rs')
  end ->
  exists rs',
    exec_straight ge c rs m k rs' m' /\ P rs'.
Proof.
  intros until m'; intros TR EA ADDR MK1 MK2.
  unfold transl_memory_access in TR; destruct addr; ArgsInv; simpl in EA; inv EA.
  (* Aindexed *)
  apply indexed_memory_access_correct. exact MK1.
  (* Aindexed2 *)
  destruct mk_instr_gen as [mk | ]; monadInv TR. apply MK2.
  simpl. erewrite ! ireg_of_eq; eauto.
  (* Aindexed2shift *)
  destruct mk_instr_gen as [mk | ]; monadInv TR. apply MK2.
  erewrite ! ireg_of_eq; eauto. rewrite transl_shift_correct. auto.
  (* Ainstack *)
  inv TR. apply indexed_memory_access_correct. intros. eapply MK1; eauto.
  rewrite H. destruct (rs IR13); try contradiction. simpl. f_equal; f_equal. auto with ptrofs.
Qed.

Lemma transl_load_int_correct:
  forall mk_instr is_immed dst addr args k c (rs: regset) a chunk m v,
  transl_memory_access_int mk_instr is_immed dst addr args k = OK c ->
  eval_addressing ge (rs#SP) addr (map rs (map preg_of args)) = Some a ->
  Mem.loadv chunk m a = Some v ->
  (forall (r1 r2: ireg) (sa: shift_op) (rs1: regset),
    exec_basic ge (mk_instr r1 r2 sa) rs1 m =
    exec_load_aux chunk (Val.add rs1#r2 (eval_shift_op sa rs1)) r1 rs1 m) ->
  exists rs',
      exec_straight ge c rs m k rs' m
   /\ rs'#(preg_of dst) = v
   /\ forall r, data_preg r = true -> r <> preg_of dst -> rs'#r = rs#r.
Proof.
  intros. monadInv H. erewrite ireg_of_eq by eauto.
  eapply transl_memory_access_correct; eauto.
  destruct a; discriminate || trivial.
  intros; simpl. econstructor; split. apply exec_straight_one.
  rewrite H2. unfold exec_load_aux. simpl eval_shift_op. rewrite H. rewrite H1. eauto. auto.
  split. Simpl. intros. Simpl.
  econstructor; split. apply exec_straight_one.
  rewrite H2. unfold exec_load_aux. rewrite H. rewrite H1. eauto. auto.
  split. Simpl. intros; Simpl.
Qed.

Lemma transl_load_float_correct:
  forall mk_instr is_immed dst addr args k c (rs: regset) a chunk m v,
  transl_memory_access_float mk_instr is_immed dst addr args k = OK c ->
  eval_addressing ge (rs#SP) addr (map rs (map preg_of args)) = Some a ->
  Mem.loadv chunk m a = Some v ->
  (forall (r1: freg) (r2: ireg) (n: int) (rs1: regset),
    exec_basic ge (mk_instr r1 r2 n) rs1 m =
    exec_load_aux chunk (Val.add rs1#r2 (Vint n)) r1 rs1 m) ->
  exists rs',
      exec_straight ge c rs m k rs' m
   /\ rs'#(preg_of dst) = v
   /\ forall r, data_preg r = true -> r <> preg_of dst -> rs'#r = rs#r.
Proof.
  intros. monadInv H. erewrite freg_of_eq by eauto.
  eapply transl_memory_access_correct; eauto.
  destruct a; discriminate || trivial.
  intros; simpl. econstructor; split. apply exec_straight_one.
  rewrite H2. unfold exec_load_aux. rewrite H. rewrite H1. eauto. auto.
  split. Simpl. intros; Simpl.
  simpl; auto.
Qed.

Lemma transl_store_int_correct:
  forall mr mk_instr is_immed src addr args k c (rs: regset) a chunk m m',
  transl_memory_access_int mk_instr is_immed src addr args k = OK c ->
  eval_addressing ge (rs#SP) addr (map rs (map preg_of args)) = Some a ->
  Mem.storev chunk m a rs#(preg_of src) = Some m' ->
  (forall (r1 r2: ireg) (sa: shift_op) (rs1: regset),
    exec_basic ge (mk_instr r1 r2 sa) rs1 m =
    exec_store_aux chunk (Val.add rs1#r2 (eval_shift_op sa rs1)) r1 rs1 m) ->
  exists rs',
      exec_straight ge c rs m k rs' m'
   /\ forall r, data_preg r = true -> preg_notin r mr -> rs'#r = rs#r.
Proof.
  intros. assert (DR: data_preg (preg_of src) = true) by eauto with asmgen.
  monadInv H. erewrite ireg_of_eq in * by eauto.
  eapply transl_memory_access_correct; eauto.
  destruct a; discriminate || trivial.
  intros; simpl. econstructor; split. apply exec_straight_one.
  rewrite H2. unfold exec_store_aux. simpl eval_shift_op. rewrite H. rewrite H3; eauto with asmgen.
  rewrite H1. eauto. auto.
  intros; auto with asmgen.
  econstructor; split. apply exec_straight_one.
  rewrite H2. unfold exec_store_aux. rewrite H. rewrite H1. eauto. auto.
Qed.

Lemma transl_store_float_correct:
  forall mr mk_instr is_immed src addr args k c (rs: regset) a chunk m m',
  transl_memory_access_float mk_instr is_immed src addr args k = OK c ->
  eval_addressing ge (rs#SP) addr (map rs (map preg_of args)) = Some a ->
  Mem.storev chunk m a rs#(preg_of src) = Some m' ->
  (forall (r1: freg) (r2: ireg) (n: int) (rs1: regset),
    exec_basic ge (mk_instr r1 r2 n) rs1 m =
    exec_store_aux chunk (Val.add rs1#r2 (Vint n)) r1 rs1 m) ->
  exists rs',
      exec_straight ge c rs m k rs' m'
   /\ forall r, data_preg r = true -> preg_notin r mr -> rs'#r = rs#r.
Proof.
  intros. assert (DR: data_preg (preg_of src) = true) by eauto with asmgen.
  monadInv H. erewrite freg_of_eq in * by eauto.
  eapply transl_memory_access_correct; eauto.
  destruct a; discriminate || trivial.
  intros; simpl. econstructor; split. apply exec_straight_one.
  rewrite H2. unfold exec_store_aux. rewrite H. rewrite H3; auto with asmgen. rewrite H1. eauto. auto.
  intros; auto with asmgen.
  simpl; auto.
Qed.

Lemma transl_load_correct:
  forall trap chunk addr args dst k c (rs: regset) a m v,
  transl_load trap chunk addr args dst k = OK c ->
  eval_addressing ge (rs#SP) addr (map rs (map preg_of args)) = Some a ->
  Mem.loadv chunk m a = Some v ->
  exists rs',
      exec_straight ge c rs m k rs' m
   /\ rs'#(preg_of dst) = v
   /\ forall r, data_preg r = true -> r <> preg_of dst -> rs'#r = rs#r.
Proof.
  intros.
  destruct trap; simpl in H. 2: discriminate.
  destruct chunk; simpl in H; try discriminate;
  eauto using transl_load_int_correct, transl_load_float_correct.
Qed.

Lemma transl_store_correct:
  forall chunk addr args src k c (rs: regset) a m m',
  transl_store chunk addr args src k = OK c ->
  eval_addressing ge (rs#SP) addr (map rs (map preg_of args)) = Some a ->
  Mem.storev chunk m a rs#(preg_of src) = Some m' ->
  exists rs',
      exec_straight ge c rs m k rs' m'
   /\ forall r, data_preg r = true -> preg_notin r (destroyed_by_store chunk addr) -> rs'#r = rs#r.
Proof.
  intros. destruct chunk; simpl in H; try discriminate;
  eauto using transl_store_int_correct, transl_store_float_correct.
Qed.

Lemma extract_memcpy_builtin_arg_correct:
  forall ba r i rs m b ofs,
  evalopt_builtin_arg ge (fun r: dreg => rs r) (rs#SP) m (map_builtin_arg dreg_of ba) = Some (Vptr b i) ->
  extract_memcpy_builtin_arg ba = OK (r, ofs) ->
  Val.add (rs#r) (Vint ofs) = Vptr b i.
Proof.
  intros.
  destruct ba; try destruct ba1, ba2; simpl in *; try congruence;
  monadInv H0.
  - apply ireg_of_eq' in EQ. rewrite EQ in H. symmetry in H. inv H.
    simpl. unfold Ptrofs.of_int. rewrite Int.unsigned_zero, Ptrofs.add_zero.
    auto.
  - revert H. unfold Val.offset_ptr. autodestruct. simpl.
    rewrite Ptrofs.of_int_to_int; auto. intros. inv H. auto.
  - apply ireg_of_eq' in EQ. rewrite EQ in H. inv H. auto.
Qed.

Lemma extract_memcpy_builtin_arg_data:
  forall ba x ofs,
  extract_memcpy_builtin_arg ba = OK (x, ofs) ->
  data_preg x = true.
Proof.
  intros. destruct ba; try destruct ba1, ba2; simpl in *; try monadInv H;
  try congruence; destruct x0; inv EQ; auto.
Qed.

Lemma indexed_memcpy_access_correct:
  forall (mk_instr: ireg -> ireg -> int -> int -> basic)
         (mk_immed: int -> int) (ras rad: ireg) ofss ofsd k (rs: regset) m m' l,
  data_preg ras = true -> data_preg rad = true ->
  (forall (r1 r2: ireg) (rs1: regset) n1 n2 k,
    data_preg r1 = true -> data_preg r2 = true ->
    r1 = ras \/ r1 = IR2 \/ r1 = IR3 -> r2 = rad \/ r2 = IR2 \/ r2 = IR3 ->
    Val.add rs1#r1 (Vint n1) = Val.add rs#ras (Vint ofss) ->
    Val.add rs1#r2 (Vint n2) = Val.add rs#rad (Vint ofsd) ->
    (forall r: preg,
      data_preg r = true -> preg_notin r (R2 :: R3 :: l) -> rs1 r = rs r) ->
    exists rs',
      exec_straight ge (mk_instr r1 r2 n1 n2 :: k) rs1 m k rs' m'
    /\
      (forall r: preg,
        data_preg r = true -> preg_notin r (R2 :: R3 :: l) -> rs r = rs' r)) ->
  exists rs',
    exec_straight ge
      (indexed_memcpy_access mk_instr mk_immed ras rad ofss ofsd k)
      rs m k rs' m'
  /\
    (forall r: preg,
      data_preg r = true -> preg_notin r (R2 :: R3 :: l) -> rs r = rs' r).
Proof.
  intros until l. intros HDS HDD SEM.
  unfold indexed_memcpy_access;
  destruct (Int.eq ofss (mk_immed ofss)) eqn:HIMM1,
           (Int.eq ofsd (mk_immed ofsd)) eqn:HIMM2,
           (PregEq.eq ras IR3), (PregEq.eq rad IR3),
           (PregEq.eq ras IR2), (PregEq.eq rad IR2);
  try (apply Int.same_if_eq in HIMM1; rewrite <- HIMM1);
  try (apply Int.same_if_eq in HIMM2; rewrite <- HIMM2);
  simpl; try congruence; auto.
  1-3,8:
    destruct (addimm_correct IR2 rad (Int.sub ofsd (mk_immed ofsd))
             (mk_instr ras IR2 ofss (mk_immed ofsd) ::bi k) rs m)
    as (rs1 & A & B & C);
    specialize SEM with ras IR2 rs1 ofss (mk_immed ofsd) k.
  10,13,14,16,18:
    destruct (addimm_correct IR2 ras (Int.sub ofss (mk_immed ofss))
             (mk_instr IR2 rad (mk_immed ofss) ofsd ::bi k) rs m)
    as (rs1 & A & B & C);
    specialize SEM with IR2 rad rs1 (mk_immed ofss) ofsd k.
  5-9:
    destruct (addimm_correct IR3 rad (Int.sub ofsd (mk_immed ofsd))
             (mk_instr ras IR3 ofss (mk_immed ofsd) ::bi k) rs m)
    as (rs1 & A & B & C);
    specialize SEM with ras IR3 rs1 ofss (mk_immed ofsd) k.
  15-18:
    destruct (addimm_correct IR3 ras (Int.sub ofss (mk_immed ofss))
             (mk_instr IR3 rad (mk_immed ofss) ofsd ::bi k) rs m)
    as (rs1 & A & B & C);
    specialize SEM with IR3 rad rs1 (mk_immed ofss) ofsd k.
  1-18:
    destruct SEM as (rs2 & D & E); try congruence; simpl; auto;
    try (rewrite C; auto; try congruence; fail);
    try rewrite B, Val.add_assoc; f_equal; simpl;
    try rewrite Int.sub_add_opp, Int.add_assoc,
                (Int.add_commut (Int.neg _)),
                Int.add_neg_zero, Int.add_zero; auto;
    try (destruct l; intros ? ?; (intros (? & ? & ?) || intros (? & ?));
         apply C; try apply data_if_preg; auto);
    exists rs2; split; auto; eapply exec_straight_trans; eauto.
  1:
    destruct (addimm_correct IR2 rad (Int.sub ofsd (mk_immed ofsd))
             (addimm IR3 ras (Int.sub ofss (mk_immed ofss))
             (mk_instr IR3 IR2 (mk_immed ofss) (mk_immed ofsd) ::bi k)) rs m)
    as (rs1 & A & B & C);
    destruct (addimm_correct IR3 ras (Int.sub ofss (mk_immed ofss))
             (mk_instr IR3 IR2 (mk_immed ofss) (mk_immed ofsd) ::bi k) rs1 m)
    as (rs2 & D & E & F);
    specialize SEM with IR3 IR2 rs2 (mk_immed ofss) (mk_immed ofsd) k.
  2,3,6,8:
    destruct (addimm_correct IR3 ras (Int.sub ofss (mk_immed ofss))
             (addimm IR2 rad (Int.sub ofsd (mk_immed ofsd))
             (mk_instr IR3 IR2 (mk_immed ofss) (mk_immed ofsd) ::bi k)) rs m)
    as (rs1 & A & B & C);
    destruct (addimm_correct IR2 rad (Int.sub ofsd (mk_immed ofsd))
             (mk_instr IR3 IR2 (mk_immed ofss) (mk_immed ofsd) ::bi k) rs1 m)
    as (rs2 & D & E & F);
    specialize SEM with IR3 IR2 rs2 (mk_immed ofss) (mk_immed ofsd) k.
  6-9:
    destruct (addimm_correct IR2 ras (Int.sub ofss (mk_immed ofss))
             (addimm IR3 rad (Int.sub ofsd (mk_immed ofsd))
             (mk_instr IR2 IR3 (mk_immed ofss) (mk_immed ofsd) ::bi k)) rs m)
    as (rs1 & A & B & C);
    destruct (addimm_correct IR3 rad (Int.sub ofsd (mk_immed ofsd))
             (mk_instr IR2 IR3 (mk_immed ofss) (mk_immed ofsd) ::bi k) rs1 m)
    as (rs2 & D & E & F);
    specialize SEM with IR2 IR3 rs2 (mk_immed ofss) (mk_immed ofsd) k.
  all:
    destruct SEM as (rs3 & G & H); try congruence; simpl;
    try (rewrite F; simpl; try congruence; rewrite B, Val.add_assoc; f_equal);
    try (rewrite E, Val.add_assoc; f_equal; try (rewrite C; try congruence));
    simpl;
    try rewrite Int.sub_add_opp, Int.add_assoc, (Int.add_commut (Int.neg _)),
                Int.add_neg_zero, Int.add_zero; auto;
    try (destruct l; intros ? ?; (intros (? & ? & ?) || intros (? & ?));
         rewrite F; try apply data_if_preg; auto; apply C; try apply data_if_preg; auto);
    exists rs3; split; auto; do 2 (eapply exec_straight_trans; eauto).
Qed.

Lemma exec_memcpy_aux_general:
  forall (rs rs1: regset) (r1 r2 r1' r2': ireg) i1 i2 i1' i2' sz m m' va1 va2 vargs b1 b2 l
  (HVARGS: va2 :: va1 :: nil = vargs)
  (VA1: evalopt_builtin_arg ge (fun r : dreg => rs r) (rs IR13) m (map_builtin_arg dreg_of b1) = Some va1)
  (VA2: evalopt_builtin_arg ge (fun r : dreg => rs r) (rs IR13) m (map_builtin_arg dreg_of b2) = Some va2)
  (HRS: extract_memcpy_builtin_arg b1 = OK (r1, i1))
  (HRD: extract_memcpy_builtin_arg b2 = OK (r2, i2))
  (VD1: Val.add (rs1 r1') (Vint i1') = Val.add (rs r1) (Vint i1))
  (VD2: Val.add (rs1 r2') (Vint i2') = Val.add (rs r2) (Vint i2))
  (HMEM: memcpy_run (atomic sz) vargs m = Some m')
  (HL: forall r: preg, In r (map DR l) -> (DR r2) <> r),
  exec_memcpy_aux l r1' r2' (Ofs i1') (Ofs i2') (Zpos sz) rs1 m =
  Next (undef_regs (map DR l) rs1) m'.
Proof.
  Local Opaque DecidableClass.decide.
  intros. inv HVARGS. revert HMEM.
  unfold memcpy_run, memcpy, memmove. simpl. do 4 autodestruct.
  intros HLOAD OK ? ? HSTORE. subst.
  unfold exec_memcpy_aux, mcpy_rs_use_src, mcpy_rs,
         mcpy_rs_use_addr, mcpy_rs_ret_addr.
  eapply extract_memcpy_builtin_arg_correct in VA1; eauto.
  eapply extract_memcpy_builtin_arg_correct in VA2; eauto.
  rewrite VA1 in VD1. rewrite VA2 in VD2. rewrite VD1, VD2.
  simpl. rewrite !Pregmap.gss, HLOAD, HSTORE. auto.
Qed.

Lemma transl_memcpy_correct:
  forall sz sz' args vargs vargs' m k c (rs: regset) m',
  Val.lessdef_list vargs vargs' ->
  Z_to_memcpy_typ (Zpos sz) = Some sz' ->
  transl_memcpy sz' args k = OK c ->
  evalopt_builtin_args ge (fun r: dreg => rs r) (rs SP) m (map (map_builtin_arg dreg_of) args) = Some vargs' ->
  memcpy_run (atomic sz) vargs' m = Some m' ->
  exists rs',
    exec_straight ge c rs m k rs' m'
    /\ forall r, data_preg r = true -> preg_notin r (destroyed_by_memcpy sz') -> rs#r = rs'#r.
Proof.
  assert (forall x r, proj_sumbool (preg_eq x r) = true -> x = r) as HPREGEQ.
  { intros. destruct (preg_eq x r); auto. simpl in *. congruence. }
  intros. apply memcpy_typ_Z in H0.
  destruct args as [|a1[|a2[]]]; simpl in H1; try congruence.
  monadInv H1.
  unfold select_memcpy_inst in *.
  assert (Z.to_pos (memcpy_typ_to_Z sz') = Z.to_pos (Z.pos sz)).
  { destruct sz'; rewrite H0; auto. }
  simpl in H2. revert H2. do 2 autodestruct. intros.
  destruct sz'; inv H0.
  1-3:
    apply indexed_memcpy_access_correct; eauto;
    try eapply extract_memcpy_builtin_arg_data; eauto;
    intros; eexists; split;
    try (apply exec_straight_one; simpl;
      destruct (PregEq.eq r2 IR14) as [HEQ1|HEQ1]; simpl;
      try (apply data_preg_not_R14 in HEQ1; intuition);
      inversion H2; eapply exec_memcpy_aux_general; eauto;
      intros; inv H10;
      exploit extract_memcpy_builtin_arg_data;
      try apply data_preg_not_R14; eauto);
    intros; simpl; rewrite Pregmap.gso, H9;
    try apply data_if_preg; try apply data_preg_not_R14; auto.
  repeat autodestruct; intros.
  (* sz = 8 /\ fpu = true *)
  - unfold destroyed_by_memcpy. rewrite EQ3.
    apply indexed_memcpy_access_correct; eauto.
    1,2: eapply extract_memcpy_builtin_arg_data; eauto.
    intros. eexists. split.
    + apply exec_straight_one. simpl.
      inversion H2. eapply exec_memcpy_aux_general; eauto.
      intros. inv H10. congruence. inv H12.
    + intros. revert H11. simpl. intros (? & ? & ?).
      rewrite Pregmap.gso, H9; simpl; auto.
 (* sz = 8 /\ fpu = false /\ thumb = true *)
  - unfold destroyed_by_memcpy. rewrite EQ5, EQ4.
    apply indexed_memcpy_access_correct; eauto.
    1,2: eapply extract_memcpy_builtin_arg_data; eauto.
    clear EQ3. inv e.
    intros. eexists. split.
    + apply exec_straight_one. simpl.
      rewrite EQ4, orb_true_l. apply data_preg_not_R14 in H4.
      destruct (PregEq.eq r2 IR10), (PregEq.eq r2 IR14);
      try congruence; simpl. inv e. destruct H6 as [|[]]; congruence.
      inversion H2. eapply exec_memcpy_aux_general; eauto.
      intros. inv H10. congruence. inv H12. congruence. inv H10.
    + intros. revert H11. simpl. intros (? & ? & ? & ?).
      rewrite !Pregmap.gso, H9; auto.
      simpl. auto.
      apply data_preg_not_R14. auto.
  (* sz = 8 /\ fpu = false /\ thumb = true (2) *)
  - unfold destroyed_by_memcpy. rewrite EQ5, EQ4.
    apply indexed_memcpy_access_correct; eauto.
    1,2: eapply extract_memcpy_builtin_arg_data; eauto.
    intros. eexists. split.
    + apply exec_straight_one. simpl.
      rewrite EQ4, orb_true_l. apply data_preg_not_R14 in H4.
      destruct (PregEq.eq r2 IR11), (PregEq.eq r2 IR14);
      try congruence; simpl. inv e. destruct H6 as [|[]]; congruence.
      inversion H2. eapply exec_memcpy_aux_general; eauto.
      intros. inv H10. congruence. inv H12.
      eapply extract_memcpy_builtin_arg_data in EQ1; eauto.
      apply data_preg_not_R14. auto.
      inv H10.
    + intros. revert H11. simpl. intros (? & ? & ? & ?).
      rewrite !Pregmap.gso, H9; simpl; auto.
      apply data_preg_not_R14. auto.
  (* sz = 8 /\ fpu = false /\ thumb = false (1) *)
  - unfold destroyed_by_memcpy. rewrite EQ5, EQ4.
    apply indexed_memcpy_access_correct; eauto.
    1,2: eapply extract_memcpy_builtin_arg_data; eauto.
    intros. eexists. split.
    + apply exec_straight_one. simpl.
      unfold ls_double_valid_regs, ls_double_next_reg.
      destruct (PregEq.eq IR11 IR11); try congruence.
      assert (x1 <> IR10 /\ x1 <> IR11) as [HR1 HR2].
      { apply orb_prop in EQ3. destruct EQ3; apply HPREGEQ in H10;
        inv H10; intuition congruence. }
      rewrite EQ4. simpl. clear e.
      destruct (PregEq.eq r2 IR10), (PregEq.eq r2 IR11);
      try congruence; simpl.
      1,2: inv e; destruct H6 as [|[]]; congruence.
      inversion H2. eapply exec_memcpy_aux_general; eauto.
      intros. inv H10. congruence. inv H12. congruence. inv H10.
    + intros. revert H11. simpl. intros (? & ? & ? & ? & ? & ?).
      rewrite !Pregmap.gso, H9; auto.
      simpl. repeat split; auto.
  (* sz = 8 /\ fpu = false /\ thumb = false (2) *)
  - unfold destroyed_by_memcpy. rewrite EQ5, EQ4.
    apply indexed_memcpy_access_correct; eauto.
    1,2: eapply extract_memcpy_builtin_arg_data; eauto.
    intros. eexists. split.
    + apply exec_straight_one. simpl.
      unfold ls_double_valid_regs, ls_double_next_reg.
      destruct (PregEq.eq IR9 IR9); try congruence.
      rewrite EQ4. simpl. clear e.
      destruct (PregEq.eq x1 IR8). inv e. simpl in *. congruence.
      destruct (PregEq.eq x1 IR9). inv e. simpl in *. congruence.
      destruct (PregEq.eq r2 IR8), (PregEq.eq r2 IR9);
      try congruence; simpl.
      1,2: inv e; destruct H6 as [|[]]; congruence.
      inversion H2. eapply exec_memcpy_aux_general; eauto.
      intros. inv H10. congruence. inv H12. congruence. inv H10.
    + intros. revert H11. simpl. intros (? & ? & ? & ? & ? & ?).
      rewrite !Pregmap.gso, H9; auto.
      simpl. repeat split; auto.
Qed.

Lemma make_epilogue_correct:
  forall tge f m stk soff cs m' ms rs tm,
  Mach.load_stack m (Vptr stk soff) Tptr (fn_link_ofs f) = Some (parent_sp cs) ->
  Mach.load_stack m (Vptr stk soff) Tptr (fn_retaddr_ofs f) =
    Some (PAC.pointer_auth_encode (fn_retaddr_pac f) (parent_ra cs) (parent_sp cs)) ->
  Mem.free m stk 0 (fn_stacksize f) = Some m' ->
  agree ms (Vptr stk soff) rs ->
  Mem.extends m tm ->
  match_stack tge cs ->
  (function_preserves_IR14 f = true -> rs#RA = parent_ra cs) ->
  exists rs' tm',
       exec_straight ge (make_epilogue f) rs tm nil rs' tm'
    /\ agree ms (parent_sp cs) rs'
    /\ Mem.extends m' tm'
    /\ rs'#SP = parent_sp cs
    /\ rs'#RA = parent_ra cs
    /\ (forall r, if_preg r = true -> r <> SP -> r <> RA -> rs'#r = rs#r).
Proof.
  intros until tm. intros LP LRA FREE AG MEXT MCS RAIR14.
  unfold make_epilogue.
  exploit Mem.loadv_extends. eauto. eexact LP. auto.
  unfold chunk_of_type. rewrite (sp_val _ _ _ AG). intros [parent' [LP' LDP']].
  simpl in LP'.
  exploit lessdef_parent_sp; eauto. intros EQ; subst parent'; clear LDP'.
  exploit Mem.loadv_extends. eauto. eexact LRA. auto.
  unfold chunk_of_type. rewrite (sp_val _ _ _ AG). intros [ra' [LRA' LDRA']].
  simpl in LRA'.
  exploit lessdef_parent_ra; eauto. intros EQ; subst ra'; clear LDRA'.
  exploit Mem.free_parallel_extends; eauto. intros (tm' & FREE' & MEXT').
  destruct (optim_leaf tt && function_preserves_IR14 f) eqn:LEAF.
  - (* Leaf optimization: IR14 already contains parent_ra, skip load *)
    apply andb_true_iff in LEAF. destruct LEAF as [_ HPRES].
    specialize (RAIR14 HPRES).
    econstructor. econstructor. split.
    apply exec_straight_one. simpl. rewrite LP'.
    rewrite <- (sp_val _ _ _ AG). rewrite FREE'. eauto. split.
    apply agree_change_sp with (Vptr stk soff).
    apply agree_exten with rs; auto.
    eapply parent_sp_def; eauto.
    split. auto.
    split. Simpl.
    split. Simpl.
    intros. Simpl.
  - (* Normal case: load RA from stack *)
    exploit loadind_int_correct. exact LRA'. intros [rs1 [A1 [B1 C1]]].
    econstructor. econstructor. split.
    eapply exec_straight_trans. eexact A1.
    apply exec_straight_one. simpl. rewrite (C1 SP) by auto with asmgen. rewrite LP'.
    rewrite <- (sp_val _ _ _ AG). rewrite FREE'. eauto. split.
    apply agree_change_sp with (Vptr stk soff).
    apply agree_exten with rs; auto.
    intros; apply C1; auto with asmgen.
    eapply parent_sp_def; eauto.
    split. auto.
    split. Simpl.
    split. Simpl.
    intros. Simpl.
Qed.

Correctness of preserves_IR14


Lemma undef_flags_IR14:
  forall rs, undef_flags rs IR14 = rs IR14.
Proof.
  reflexivity.
Qed.

Lemma compare_int_IR14:
  forall rs v1 v2 m,
  compare_int rs v1 v2 m IR14 = rs IR14.
Proof.
  intros. unfold compare_int. Simpl.
Qed.

Lemma compare_int_add_IR14:
  forall rs v1 v2 m,
  compare_int_add rs v1 v2 m IR14 = rs IR14.
Proof.
  intros. unfold compare_int_add. apply compare_int_IR14.
Qed.

Lemma compare_int_test_IR14:
  forall rs v1 v2,
  compare_int_test rs v1 v2 IR14 = rs IR14.
Proof.
  intros. unfold compare_int_test. Simpl.
Qed.

Lemma compare_float_IR14:
  forall rs v1 v2,
  compare_float rs v1 v2 IR14 = rs IR14.
Proof.
  intros. unfold compare_float.
  destruct v1; destruct v2; Simpl.
Qed.

Lemma compare_float32_IR14:
  forall rs v1 v2,
  compare_float32 rs v1 v2 IR14 = rs IR14.
Proof.
  intros. unfold compare_float32.
  destruct v1; destruct v2; Simpl.
Qed.

Remark indexed_memory_access_eq:
  forall mk_instr mk_immed base n k,
  Int.eq n (mk_immed n) = true ->
  indexed_memory_access mk_instr mk_immed base n k = mk_instr base n :: k.
Proof.
  intros. unfold indexed_memory_access. rewrite H. reflexivity.
Qed.

Lemma exec_load_aux_IR14:
  forall chunk addr (rd: ireg) rs m rs' m',
  exec_load_aux chunk addr rd rs m = Next rs' m' ->
  IR rd <> IR IR14 ->
  rs' IR14 = rs IR14.
Proof.
  unfold exec_load_aux; intros.
  destruct (Mem.loadv _ _ _); inv H.
  apply Pregmap.gso. congruence.
Qed.

Lemma exec_store_aux_IR14:
  forall chunk addr (rf: preg) rs m rs' m',
  exec_store_aux chunk addr rf rs m = Next rs' m' ->
  rs' IR14 = rs IR14.
Proof.
  unfold exec_store_aux; intros.
  destruct (Mem.storev _ _ _ _); inv H. auto.
Qed.

Lemma exec_straight_length:
  forall c rs m c' rs' m',
  exec_straight ge c rs m c' rs' m' ->
  (length c > length c')%nat.
Proof.
  induction 1; simpl; lia.
Qed.

Lemma exec_straight_singleton:
  forall i k rs m rs' m',
  exec_straight ge (i :: k) rs m k rs' m' ->
  exec_basic ge i rs m = Next rs' m'.
Proof.
  intros. inv H; auto.
  exfalso. exploit exec_straight_length; eauto. simpl; lia.
Qed.

Lemma exec_straight_singleton_bi:
  forall (i: basic) k rs m rs' m',
  exec_straight ge (i ::bi k) rs m k rs' m' ->
  exec_basic ge i rs m = Next rs' m'.
Proof.
  intros. apply exec_straight_singleton in H. auto.
Qed.

Lemma exec_straight_IR14:
  forall c rs m k rs' m',
  exec_straight ge c rs m k rs' m' ->
  (forall i rs1 m1 rs2 m2,
     In i c -> exec_basic ge i rs1 m1 = Next rs2 m2 -> rs2 IR14 = rs1 IR14) ->
  rs' IR14 = rs IR14.
Proof.
  induction 1; intros.
  - eapply H0; eauto. left; auto.
  - transitivity (rs2 IR14).
    + apply IHexec_straight. intros. eapply H1; eauto. right; auto.
    + eapply H1; eauto. left; auto.
Qed.

Lemma exec_straight_determ:
  forall c rs m c' rs1 m1 rs2 m2,
  exec_straight ge c rs m c' rs1 m1 ->
  exec_straight ge c rs m c' rs2 m2 ->
  rs1 = rs2 /\ m1 = m2.
Proof.
  induction 1; intros H2; inv H2;
  try (exfalso; exploit exec_straight_length; eauto; simpl; lia).
  - match goal with H1: exec_basic _ _ _ _ = _, H2: exec_basic _ _ _ _ = _ |- _ =>
      rewrite H1 in H2; inv H2; auto end.
  - match goal with H1: exec_basic _ _ _ _ = _, H2: exec_basic _ _ _ _ = _ |- _ =>
      rewrite H1 in H2; inv H2; eauto end.
Qed.

Remark compare_int_IR14':
  forall rs v1 v2 m r,
  arith_comparison_ro_compare r rs v1 v2 m IR14 = rs IR14.
Proof.
  intros. destruct r. apply compare_int_IR14. apply compare_int_IR14. apply compare_int_test_IR14.
Qed.

Remark compare_float_IR14':
  forall rs v1 v2 r,
  arith_comparison_ff_compare r rs v1 v2 IR14 = rs IR14.
Proof.
  intros. destruct r. apply compare_float_IR14. apply compare_float32_IR14.
Qed.

Remark compare_float_zero_IR14:
  forall r rs v1 v2,
  arith_comparison_f_compare r rs v1 v2 IR14 = rs IR14.
Proof.
  intros. destruct r; simpl.
  apply compare_float_IR14. apply compare_float32_IR14.
Qed.

Lemma compare_int_if_preg:
  forall rs v1 v2 m r, if_preg r = true -> compare_int rs v1 v2 m r = rs r.
Proof.
  intros. unfold compare_int. destruct r; try discriminate; reflexivity.
Qed.

Lemma compare_int_test_if_preg:
  forall rs v1 v2 r, if_preg r = true -> compare_int_test rs v1 v2 r = rs r.
Proof.
  intros. unfold compare_int_test. destruct r; try discriminate; reflexivity.
Qed.

Lemma compare_float_if_preg:
  forall rs v1 v2 r, if_preg r = true -> compare_float rs v1 v2 r = rs r.
Proof.
  intros. unfold compare_float. destruct v1; destruct v2;
  (destruct r; try discriminate; reflexivity).
Qed.

Lemma compare_float32_if_preg:
  forall rs v1 v2 r, if_preg r = true -> compare_float32 rs v1 v2 r = rs r.
Proof.
  intros. unfold compare_float32. destruct v1; destruct v2;
  (destruct r; try discriminate; reflexivity).
Qed.

Lemma transl_cond_correct_strong:
  forall cond args k c rs m,
  transl_cond cond args k = OK c ->
  cond_preserves_IR14 cond = true ->
  exists rs',
     exec_straight ge c rs m k rs' m
  /\ forall r, if_preg r = true -> rs'#r = rs#r.
Proof.
  intros until m; intros TR PRES.
  unfold cond_preserves_IR14 in PRES.
  unfold transl_cond in TR; destruct cond; ArgsInv.
  (* Ccomp *)
  - econstructor; split. apply exec_straight_one; simpl; eauto.
    intros. apply compare_int_if_preg; auto.
  (* Ccompu *)
  - econstructor; split. apply exec_straight_one; simpl; eauto.
    intros. apply compare_int_if_preg; auto.
  (* Ccompshift *)
  - econstructor; split. apply exec_straight_one; simpl; eauto.
    intros. apply compare_int_if_preg; auto.
  (* Ccompushift *)
  - econstructor; split. apply exec_straight_one; simpl; eauto.
    intros. apply compare_int_if_preg; auto.
  (* Ccompimm *)
  - apply orb_true_iff in PRES; destruct PRES as [PRES|PRES];
    [ rewrite PRES
    | match type of PRES with is_immed_arith OTHER (Int.neg ?nn) = true =>
        destruct (is_immed_arith OTHER nn); [| rewrite PRES] end ];
    (econstructor; split;
     [apply exec_straight_one; simpl; eauto
     | intros; apply compare_int_if_preg; auto]).
  (* Ccompuimm *)
  - apply orb_true_iff in PRES; destruct PRES as [PRES|PRES];
    [ rewrite PRES
    | match type of PRES with is_immed_arith OTHER (Int.neg ?nn) = true =>
        destruct (is_immed_arith OTHER nn); [| rewrite PRES] end ];
    (econstructor; split;
     [apply exec_straight_one; simpl; eauto
     | intros; apply compare_int_if_preg; auto]).
  (* Ccompf *)
  - econstructor; split. apply exec_straight_one; simpl; eauto.
    intros. apply compare_float_if_preg; auto.
  (* Cnotcompf *)
  - econstructor; split. apply exec_straight_one; simpl; eauto.
    intros. apply compare_float_if_preg; auto.
  (* Ccompfzero *)
  - econstructor; split. apply exec_straight_one; simpl; eauto.
    intros. apply compare_float_if_preg; auto.
  (* Cnotcompfzero *)
  - econstructor; split. apply exec_straight_one; simpl; eauto.
    intros. apply compare_float_if_preg; auto.
  (* Ccompfs *)
  - econstructor; split. apply exec_straight_one; simpl; eauto.
    intros. apply compare_float32_if_preg; auto.
  (* Cnotcompfs *)
  - econstructor; split. apply exec_straight_one; simpl; eauto.
    intros. apply compare_float32_if_preg; auto.
  (* Ccompfszero *)
  - econstructor; split. apply exec_straight_one; simpl; eauto.
    intros. apply compare_float32_if_preg; auto.
  (* Cnotcompfszero *)
  - econstructor; split. apply exec_straight_one; simpl; eauto.
    intros. apply compare_float32_if_preg; auto.
  (* Cmaskzero *)
  - rewrite PRES.
    econstructor; split. apply exec_straight_one; simpl; eauto.
    intros. apply compare_int_test_if_preg; auto.
  (* Cmasknotzero *)
  - rewrite PRES.
    econstructor; split. apply exec_straight_one; simpl; eauto.
    intros. apply compare_int_test_if_preg; auto.
  (* Ccompcarryu — PRES is false, contradiction. *)
  - discriminate PRES.
  (* Ccompcarry *)
  - discriminate PRES.
Qed.

Lemma transl_cond_preserves_IR14:
  forall cond args k c rs m,
  transl_cond cond args k = OK c ->
  cond_preserves_IR14 cond = true ->
  forall rs' m',
  exec_straight ge c rs m k rs' m' ->
  rs' IR14 = rs IR14.
Proof.
  intros until m. intros TR PRES rs' m' EXEC.
  exploit transl_cond_correct_strong; eauto.
  intros (rs2 & EXEC2 & STRONG).
  exploit exec_straight_determ. eexact EXEC. eexact EXEC2.
  intros [<- _]. apply STRONG. reflexivity.
Qed.

Lemma exec_straight_pnop_IR14:
  forall rs m rs' m',
  exec_straight ge (Pnop false :: nil) rs m nil rs' m' ->
  rs' IR14 = rs IR14.
Proof.
  intros. inv H. simpl in *. inv H4. auto.
  inv H8.
Qed.

Lemma transl_cbranch_preserves_IR14:
  forall cond args lbl near_labels bdy ctl rs m,
  transl_cbranch cond args lbl near_labels = OK (bdy, ctl) ->
  cond_preserves_IR14 cond = true ->
  forall rs' m',
  exec_straight_opt ge bdy rs m nil rs' m' ->
  rs' IR14 = rs IR14.
Proof.
  intros until m. intros TR PRES rs' m' EXEC.
  inv EXEC; [auto | ].
  unfold transl_cbranch in TR.
  destruct cond; try (monadInv TR; eapply transl_cond_preserves_IR14; eauto; fail).
  - (* Ccompimm *)
    destruct c; try (monadInv TR; eapply transl_cond_preserves_IR14; eauto; fail);
    (destruct args as [|a1 [|]]; try discriminate;
     try (monadInv TR; eapply transl_cond_preserves_IR14; eauto; fail);
     destruct (thumb tt && short_branches tt && Int.eq i Int.zero && is_near_label lbl near_labels);
     [monadInv TR; destruct (is_low_ireg _);
      [inv EQ0; eapply exec_straight_pnop_IR14; eauto |
       monadInv EQ0; eapply transl_cond_preserves_IR14; eauto] |
      monadInv TR; eapply transl_cond_preserves_IR14; eauto]).
  - (* Ccompuimm *)
    destruct c; try (monadInv TR; eapply transl_cond_preserves_IR14; eauto; fail);
    (destruct args as [|a1 [|]]; try discriminate;
     try (monadInv TR; eapply transl_cond_preserves_IR14; eauto; fail);
     destruct (thumb tt && short_branches tt && Int.eq i Int.zero && is_near_label lbl near_labels);
     [monadInv TR; destruct (is_low_ireg _);
      [inv EQ0; eapply exec_straight_pnop_IR14; eauto |
       monadInv EQ0; eapply transl_cond_preserves_IR14; eauto] |
      monadInv TR; eapply transl_cond_preserves_IR14; eauto]).
Qed.

IR14 preservation for transl_op


Local Hint Extern 1 (FR _ <> IR _) => discriminate : asmgen.
Local Hint Extern 1 (IR _ <> FR _) => discriminate : asmgen.
Local Hint Extern 1 (_ <> _) => apply not_eq_sym : asmgen.

Lemma exec_straight_two_IR14:
  forall i1 i2 k0 rs0 m0 rs0' m0',
  exec_straight ge (i1 :: i2 :: k0) rs0 m0 k0 rs0' m0' ->
  (forall rs1 m1 rs2 m2, exec_basic ge i1 rs1 m1 = Next rs2 m2 -> rs2 IR14 = rs1 IR14) ->
  (forall rs1 m1 rs2 m2, exec_basic ge i2 rs1 m1 = Next rs2 m2 -> rs2 IR14 = rs1 IR14) ->
  rs0' IR14 = rs0 IR14.
Proof.
  intros i1 i2 k0 rs0 m0 rs0' m0' EXEC HI1 HI2.
  remember (i1 :: i2 :: k0) as c. remember k0 as c'.
  revert i1 i2 k0 Heqc Heqc' HI1 HI2.
  induction EXEC; intros; subst.
  - injection Heqc; intros; subst.
    exfalso. assert (length (i2 :: k0) = length k0) by congruence.
    simpl in *. lia.
  - injection Heqc; intros; subst.
    inversion EXEC; subst.
    + transitivity (rs2 IR14); [eapply HI2; eauto | eapply HI1; eauto].
    + exfalso. exploit exec_straight_length; eauto. simpl. lia.
Qed.

Lemma exec_straight_three_IR14:
  forall i1 i2 i3 k0 rs0 m0 rs0' m0',
  exec_straight ge (i1 :: i2 :: i3 :: k0) rs0 m0 k0 rs0' m0' ->
  (forall rs1 m1 rs2 m2, exec_basic ge i1 rs1 m1 = Next rs2 m2 -> rs2 IR14 = rs1 IR14) ->
  (forall rs1 m1 rs2 m2, exec_basic ge i2 rs1 m1 = Next rs2 m2 -> rs2 IR14 = rs1 IR14) ->
  (forall rs1 m1 rs2 m2, exec_basic ge i3 rs1 m1 = Next rs2 m2 -> rs2 IR14 = rs1 IR14) ->
  rs0' IR14 = rs0 IR14.
Proof.
  intros i1 i2 i3 k0 rs0 m0 rs0' m0' EXEC HI1 HI2 HI3.
  remember (i1 :: i2 :: i3 :: k0) as c. remember k0 as c'.
  revert i1 i2 i3 k0 Heqc Heqc' HI1 HI2 HI3.
  induction EXEC; intros; subst.
  - injection Heqc; intros; subst.
    exfalso. assert (length (i2 :: i3 :: k0) = length k0) by congruence.
    simpl in *. lia.
  - injection Heqc; intros; subst.
    transitivity (rs2 IR14); [|eapply HI1; eauto].
    eapply exec_straight_two_IR14; eauto.
Qed.

Default-fallback IR14 preservation.
Lemma transl_op_cmp_default_preserves_IR14:
  forall cmp args res r k bc (rs: regset) m,
  ireg_of res = OK r ->
  transl_op_cmp_default cmp args r k = OK bc ->
  cond_preserves_IR14 cmp = true ->
  forall rs' m',
  exec_straight ge bc rs m k rs' m' ->
  rs' IR14 = rs IR14.
Proof.
  intros cmp args res r k bc rs m IRG TR PRES rs' m' EXEC.
  unfold transl_op_cmp_default in TR.
  exploit transl_cond_correct_strong; eauto.
  intros (rs1 & EX_cond & STRONG).
  exploit exec_straight_determ.
  eexact EXEC.
  eapply exec_straight_trans. eassumption. apply exec_straight_one; reflexivity.
  intros [-> _].
  simpl. Simpl.
Qed.

Cne special-case IR14 preservation.
Lemma transl_op_cmp_cne_preserves_IR14:
  forall cmp args res r k bc (rs: regset) m,
  ireg_of res = OK r ->
  transl_op_cmp_cne cmp args r k = Some (OK bc) ->
  forall rs' m',
  exec_straight ge bc rs m k rs' m' ->
  rs' IR14 = rs IR14.
Proof.
  intros cmp args res r k bc rs m IRG TR rs' m' EXEC.
  unfold transl_op_cmp_cne in TR.
  destruct cmp; try discriminate.
  - (* Ccomp c *)
    destruct c; try discriminate.
    (* Ccomp Cne *)
    destruct args as [|a1 [|a2 [|? ?]]]; try discriminate.
    inv TR. monadInv H0.
    eapply exec_straight_two_IR14.
    ** exact EXEC.
    ** intros ?rs1 ?m1 ?rs2 ?m2 HEX. simpl in HEX. inv HEX.
       rewrite Pregmap.gso by (intro; apply (ireg_of_not_R14' _ _ IRG); congruence).
       apply compare_int_IR14.
    ** intros ?rs1 ?m1 ?rs2 ?m2 HEX. simpl in HEX. inv HEX. Simpl.
  - (* Ccompimm c i *)
    destruct c; try discriminate.
    (* Ccompimm Cne i *)
    destruct args as [|a1 [|? ?]]; try discriminate.
    destruct (is_immed_arith OTHER i) eqn:HIM.
    { inv TR. monadInv H0.
      eapply exec_straight_two_IR14.
      ** exact EXEC.
      ** intros ?rs1 ?m1 ?rs2 ?m2 HEX. simpl in HEX. inv HEX.
         rewrite Pregmap.gso by (intro; apply (ireg_of_not_R14' _ _ IRG); congruence).
         apply compare_int_IR14.
      ** intros ?rs1 ?m1 ?rs2 ?m2 HEX. simpl in HEX. inv HEX. Simpl. }
    destruct (is_immed_arith OTHER (Int.neg i)) eqn:HIMN; try discriminate.
    inv TR. monadInv H0.
    eapply exec_straight_two_IR14.
    ** exact EXEC.
    ** intros ?rs1 ?m1 ?rs2 ?m2 HEX. simpl in HEX. inv HEX.
       rewrite Pregmap.gso by (intro; apply (ireg_of_not_R14' _ _ IRG); congruence).
       apply compare_int_add_IR14.
    ** intros ?rs1 ?m1 ?rs2 ?m2 HEX. simpl in HEX. inv HEX. Simpl.
Qed.

Ceq CLZ special-case IR14 preservation.
Lemma transl_op_cmp_ceq_clz_preserves_IR14:
  forall cmp args res r k bc (rs: regset) m,
  ireg_of res = OK r ->
  transl_op_cmp_ceq_clz cmp args r k = Some (OK bc) ->
  forall rs' m',
  exec_straight ge bc rs m k rs' m' ->
  rs' IR14 = rs IR14.
Proof.
  intros cmp args res r k bc rs m IRG TR rs' m' EXEC.
  unfold transl_op_cmp_ceq_clz in TR.
  destruct cmp; try discriminate.
  - (* Ccomp c *)
    destruct c; try discriminate.
    (* Ccomp Ceq *)
    destruct args as [|a1 [|a2 [|? ?]]]; try discriminate.
    inv TR. monadInv H0.
    eapply exec_straight_three_IR14.
    ** exact EXEC.
    ** intros ?rs1 ?m1 ?rs2 ?m2 HEX. simpl in HEX. inv HEX. Simpl.
    ** intros ?rs1 ?m1 ?rs2 ?m2 HEX. simpl in HEX. inv HEX. Simpl.
    ** intros ?rs1 ?m1 ?rs2 ?m2 HEX. simpl in HEX. inv HEX. Simpl.
  - (* Ccompshift c sh *)
    destruct c; try discriminate.
    (* Ccompshift Ceq sh *)
    destruct args as [|a1 [|a2 [|? ?]]]; try discriminate.
    inv TR. monadInv H0.
    eapply exec_straight_three_IR14.
    ** exact EXEC.
    ** intros ?rs1 ?m1 ?rs2 ?m2 HEX. simpl in HEX. inv HEX. Simpl.
    ** intros ?rs1 ?m1 ?rs2 ?m2 HEX. simpl in HEX. inv HEX. Simpl.
    ** intros ?rs1 ?m1 ?rs2 ?m2 HEX. simpl in HEX. inv HEX. Simpl.
  - (* Ccompimm c i *)
    destruct c; try discriminate.
    (* Ccompimm Ceq i *)
    destruct args as [|a1 [|? ?]]; try discriminate.
    destruct (Int.eq i Int.zero) eqn:HNZ.
    { inv TR. monadInv H0.
      eapply exec_straight_two_IR14.
      ** exact EXEC.
      ** intros ?rs1 ?m1 ?rs2 ?m2 HEX. simpl in HEX. inv HEX. Simpl.
      ** intros ?rs1 ?m1 ?rs2 ?m2 HEX. simpl in HEX. inv HEX. Simpl. }
    destruct (is_immed_arith OTHER i) eqn:HIM.
    { inv TR. monadInv H0.
      eapply exec_straight_three_IR14.
      ** exact EXEC.
      ** intros ?rs1 ?m1 ?rs2 ?m2 HEX. simpl in HEX. inv HEX. Simpl.
      ** intros ?rs1 ?m1 ?rs2 ?m2 HEX. simpl in HEX. inv HEX. Simpl.
      ** intros ?rs1 ?m1 ?rs2 ?m2 HEX. simpl in HEX. inv HEX. Simpl. }
    destruct (is_immed_arith OTHER (Int.neg i)) eqn:HIMN; try discriminate.
    inv TR. monadInv H0.
    eapply exec_straight_three_IR14.
    ** exact EXEC.
    ** intros ?rs1 ?m1 ?rs2 ?m2 HEX. simpl in HEX. inv HEX. Simpl.
    ** intros ?rs1 ?m1 ?rs2 ?m2 HEX. simpl in HEX. inv HEX. Simpl.
    ** intros ?rs1 ?m1 ?rs2 ?m2 HEX. simpl in HEX. inv HEX. Simpl.
Qed.

Top-level dispatcher: matches the structure of transl_op_cmp.
Lemma transl_op_cmp_preserves_IR14:
  forall cmp args res r k bc (rs: regset) m,
  ireg_of res = OK r ->
  transl_op_cmp cmp args r k = OK bc ->
  cond_preserves_IR14 cmp = true ->
  forall rs' m',
  exec_straight ge bc rs m k rs' m' ->
  rs' IR14 = rs IR14.
Proof.
  intros cmp args res r k bc rs m IRG TR PRES rs' m' EXEC.
  unfold transl_op_cmp in TR.
  destruct (transl_op_cmp_cne cmp args r k) as [bc_cne|] eqn:CNE.
  - (* Cne special-case fired *)
    rewrite TR in CNE.
    eapply transl_op_cmp_cne_preserves_IR14; eauto.
  - destruct (Compopts.cmp_via_clz tt).
    + destruct (transl_op_cmp_ceq_clz cmp args r k) as [bc_clz|] eqn:CEQ.
      * rewrite TR in CEQ.
        eapply transl_op_cmp_ceq_clz_preserves_IR14; eauto.
      * eapply transl_op_cmp_default_preserves_IR14; eauto.
    + eapply transl_op_cmp_default_preserves_IR14; eauto.
Qed.

Lemma transl_op_preserves_IR14:
  forall op args res k c (rs: regset) m,
  transl_op op args res k = OK c ->
  preserves_IR14 (MBop op args res) = true ->
  forall rs' m',
  exec_straight ge c rs m k rs' m' ->
  rs' IR14 = rs IR14.
Proof.
  intros until m; intros TR PRES rs' m' EXEC.
  simpl in PRES.
  unfold transl_op in TR; destruct op; ArgsInv;
    repeat (match goal with
      | H: (if ?b then _ else _) = OK _ |- _ => destruct b; try discriminate
      | H: match ?c with _ => _ end = OK _ |- _ =>
          first [is_var c; destruct c | fail 1]; try discriminate
      | H: OK _ = OK _ |- _ => inv H
    end);
    simpl in PRES; try discriminate.
  (* 0. Ocmp: dispatch to transl_op_cmp_preserves_IR14 *)
  all: try (eapply transl_op_cmp_preserves_IR14;
            [eassumption|eassumption|exact PRES|exact EXEC]; fail).
  (* 1. Simple single-instruction cases *)
  all: try (apply exec_straight_singleton_bi in EXEC;
            simpl in EXEC; unfold arith_uf_fn in EXEC; simpl in EXEC;
            inv EXEC; Simpl; auto; fail).
  (* 2. Two-instruction cases (Ocast8signed/16signed without thumb2) *)
  all: try (eapply exec_straight_two_IR14; eauto;
            intros; simpl in *; inv H; Simpl; fail).
  (* 3. Helper-lemma cases: loadimm, addimm, etc. *)
  all: try (match goal with
    | EXEC: exec_straight _ (loadimm ?r ?n ?kk) ?rrs ?mm ?kk _ _ |- _ =>
        generalize (loadimm_correct r n kk rrs mm)
    | EXEC: exec_straight _ (addimm ?r1 ?r2 ?n ?kk) ?rrs ?mm ?kk _ _ |- _ =>
        generalize (addimm_correct r1 r2 n kk rrs mm)
    | EXEC: exec_straight _ (rsubimm ?r1 ?r2 ?n ?kk) ?rrs ?mm ?kk _ _ |- _ =>
        generalize (rsubimm_correct r1 r2 n kk rrs mm)
    | EXEC: exec_straight _ (orimm ?r1 ?r2 ?n ?kk) ?rrs ?mm ?kk _ _ |- _ =>
        generalize (orimm_correct r1 r2 n kk rrs mm)
    | EXEC: exec_straight _ (xorimm ?r1 ?r2 ?n ?kk) ?rrs ?mm ?kk _ _ |- _ =>
        generalize (xorimm_correct r1 r2 n kk rrs mm)
    | EXEC: exec_straight _ (andimm ?r1 ?r2 ?n ?kk) ?rrs ?mm ?kk _ _ |- _ =>
        generalize (andimm_correct r1 r2 n kk rrs mm)
    end;
    intros (?rs1 & ?EX & _ & ?OTH);
    exploit exec_straight_determ; [eexact EXEC | eassumption | intros [<- _]];
    match goal with OTH: forall r' : preg, _ |- _ => apply OTH end;
    eauto with asmgen; fail).
  (* 4. Cases with if in EXEC: destruct then retry singleton/two-instr *)
  all: try (
    repeat (match goal with
      | EXEC: exec_straight _ (if ?b then _ else _) _ _ _ _ _ |- _ =>
          destruct b
      | EXEC: exec_straight _ (match ?x with _ => _ end) _ _ _ _ _ |- _ =>
          destruct x; try discriminate
    end);
    first [
      apply exec_straight_singleton_bi in EXEC;
      simpl in EXEC; unfold arith_uf_fn in EXEC; simpl in EXEC;
      inv EXEC; Simpl; auto
    | eapply exec_straight_two_IR14; eauto;
      intros; simpl in *; inv H; Simpl
    | match goal with
      | EXEC: exec_straight _ (loadimm ?r ?n ?kk) ?rrs ?mm ?kk _ _ |- _ =>
          generalize (loadimm_correct r n kk rrs mm)
      | EXEC: exec_straight _ (addimm ?r1 ?r2 ?n ?kk) ?rrs ?mm ?kk _ _ |- _ =>
          generalize (addimm_correct r1 r2 n kk rrs mm)
      | EXEC: exec_straight _ (andimm ?r1 ?r2 ?n ?kk) ?rrs ?mm ?kk _ _ |- _ =>
          generalize (andimm_correct r1 r2 n kk rrs mm)
      end;
      intros (?rs1 & ?EX & _ & ?OTH);
      exploit exec_straight_determ; [eexact EXEC | eassumption | intros [<- _]];
      match goal with OTH: forall r' : preg, _ |- _ => apply OTH end;
      eauto with asmgen
    ]; fail).
  (* 4b. Ocmp Ceq 3-instr clz sequence *)
  all: try (eapply exec_straight_three_IR14; eauto;
            intros; simpl in *; inv H; Simpl; fail).
  (* 5. Ocmp/Osel with transl_cond *)
  all: try (
    exploit transl_cond_correct_strong; eauto;
    intros (?rs1 & ?EX_cond & ?STRONG);
    eapply exec_straight_determ; [eexact EXEC |
      eapply exec_straight_trans; [eassumption | apply exec_straight_one; reflexivity]
    | intros [<- _]];
    simpl; Simpl;
    match goal with H: forall r: preg, if_preg r = true -> _ |- _ => apply H end;
    reflexivity; fail).
  (* 6. Omove: destruct preg_of with eqn *)
  all: try (
    destruct (preg_of res) eqn:?; try destruct d; try discriminate;
    destruct (preg_of m0) eqn:?; try destruct d; try discriminate;
    match goal with H: OK _ = OK _ |- _ => inv H end;
    apply exec_straight_singleton_bi in EXEC;
    simpl in EXEC; unfold arith_uf_fn in EXEC; simpl in EXEC;
    inv EXEC;
    (try rewrite undef_flags_rs_eq');
    apply Pregmap.gso;
    intro; apply (preg_of_not_R14 res); congruence; fail).
  (* 7. Oshrximm with n = 0: subst then singleton *)
  all: try (apply Int.same_if_eq in PRES; subst;
    repeat (match goal with
      | EXEC: exec_straight _ (if ?b then _ else _) _ _ _ _ _ |- _ =>
          destruct b end);
    apply exec_straight_singleton_bi in EXEC;
    simpl in EXEC; unfold arith_uf_fn in EXEC; simpl in EXEC;
    inv EXEC; Simpl; fail).
  all: try (
    apply exec_straight_singleton_bi in EXEC;
    simpl in EXEC; unfold arith_uf_fn in EXEC; simpl in EXEC;
    first [match goal with H: match ?d with _ => _ end = _ |- _ =>
             destruct d; try discriminate end; inv EXEC; Simpl
          | inv EXEC; Simpl; auto]; fail).
  (* 8. Handle Osel/Ocmp/Odiv/Odivu: destruct preg_of with eqn, ArgsInv *)
  all: try (let Hpr := fresh "Hpr" in
    destruct (preg_of res) eqn:Hpr; try destruct d; try discriminate; ArgsInv).
  all: try (repeat (match goal with
    | H: (if ?b then _ else _) = OK _ |- _ => destruct b; try discriminate
    | H: OK _ = OK _ |- _ => inv H
  end)).
  (* Eliminate absurd Hpr: ireg = freg *)
  all: try (match goal with Hpr: ?a = ?b |- _ =>
    match type of a with ireg => match type of b with freg => discriminate Hpr end end end).
  (* Unify ireg variables: Hpr: x = i means i is just x *)
  all: try (match goal with Hpr: ?a = ?b |- _ =>
    match type of a with ireg => match type of b with ireg =>
      is_var a; is_var b; subst a end end end).
  (* Odiv/Odivu: singleton + hardware_idiv + Val.divs/divu *)
  all: try (
    apply exec_straight_singleton_bi in EXEC;
    simpl in EXEC;
    destruct (Archi.hardware_idiv tt);
    match goal with H: match ?d with _ => _ end = _ |- _ =>
      destruct d; try discriminate end;
    inv EXEC; Simpl; auto; fail).
  (* Osel singleton (Pmov/Pfcpyd with preg_of_not_R14) *)
  all: try (
    apply exec_straight_singleton_bi in EXEC;
    simpl in EXEC; unfold arith_uf_fn in EXEC; simpl in EXEC;
    inv EXEC; (try rewrite undef_flags_rs_eq');
    apply Pregmap.gso;
    intro; apply (preg_of_not_R14 res); congruence; fail).
  (* Ocmpcarryu / Ocmpcarry: 3-instr cmp+sbcs+movite — IR14 preserved.
     None of Pcmp, Psbcs, Pmovite write IR14 (Pcmp writes only flags;
     Psbcs writes flags + [x = ireg_of res] which is <> IR14 by
     preg_of_not_R14; Pmovite writes only [x]). *)

  all: try (
    eapply exec_straight_three_IR14;
    [ eexact EXEC | | | ];
    intros rs1 m4 rs2 m5 EXEC1; simpl in EXEC1; inv EXEC1;
    first [ unfold compare_int;
            rewrite Pregmap.gso by discriminate;
            rewrite Pregmap.gso by discriminate;
            rewrite Pregmap.gso by discriminate;
            rewrite Pregmap.gso by discriminate;
            reflexivity
          | rewrite Pregmap.gso
              by (intro Hx; apply (preg_of_not_R14 res);
                  rewrite (ireg_of_eq _ _ EQ); congruence);
            unfold compare_int_sbc;
            rewrite Pregmap.gso by discriminate;
            rewrite Pregmap.gso by discriminate;
            rewrite Pregmap.gso by discriminate;
            rewrite Pregmap.gso by discriminate;
            reflexivity
          | rewrite Pregmap.gso
              by (intro Hx; apply (preg_of_not_R14 res);
                  rewrite (ireg_of_eq _ _ EQ); congruence);
            reflexivity
          ]).
  (* Ocmp/Osel with transl_cond: build execution, use determinism *)
  all:
    match goal with
    | EQ0: transl_cond _ _ (_ ::bi _) = OK _ |- _ =>
        generalize (transl_cond_correct_strong _ _ _ _ rs m EQ0 PRES)
    | EQ2: transl_cond _ _ (_ ::bi _) = OK _ |- _ =>
        generalize (transl_cond_correct_strong _ _ _ _ rs m EQ2 PRES)
    end;
    intros (rs1 & EX_cond & STRONG);
    assert (EX_full: exec_straight ge c rs m k
      (exec_arith_instr _ rs1 m) m) by
      (eapply exec_straight_trans; [eexact EX_cond | apply exec_straight_one; reflexivity]);
    exploit exec_straight_determ; [eexact EXEC | eexact EX_full | intros [H1 H2]]; subst;
    transitivity (rs1 IR14);
    [simpl; apply Pregmap.gso;
     first [eauto with asmgen | discriminate
           | intro; apply (preg_of_not_R14 res); congruence]
    | apply STRONG; reflexivity].
  (* Any stragglers *)
  all: intro H; apply (preg_of_not_R14 res); congruence.
Qed.

Lemma undef_regs_pres:
  forall rl (rs: regset) r,
  ~ In r rl ->
  undef_regs rl rs r = rs r.
Proof.
  induction rl; simpl; intros; auto.
  rewrite IHrl by tauto.
  apply Pregmap.gso. intuition.
Qed.

Lemma exec_memcpy_aux_Ofs_IR14:
  forall dregs ras rad ofs1 ofs2 sz (rs: regset) m rs' m',
  exec_memcpy_aux dregs ras rad (Ofs ofs1) (Ofs ofs2) sz rs m = Next rs' m' ->
  ~ In (IR IR14 : dreg) dregs ->
  rs' IR14 = rs IR14.
Proof.
  intros dregs ras rad ofs1 ofs2 sz rs m rs' m' H NOTIN.
  unfold exec_memcpy_aux in H.
  set (rss := mcpy_rs_use_src dregs ras (Ofs ofs1) rs) in H.
  set (rsd := mcpy_rs dregs ras rad (Ofs ofs1) (Ofs ofs2) rs mcpy_rs_use_addr) in H.
  set (rsr := mcpy_rs dregs ras rad (Ofs ofs1) (Ofs ofs2) rs mcpy_rs_ret_addr) in H.
  destruct (rss # ras); try discriminate.
  destruct (rsd # rad); try discriminate.
  destruct (Mem.loadbytes _ _ _ _); try discriminate.
  destruct (Mem.storebytes _ _ _ _); try discriminate.
  inv H.
  unfold rsr, mcpy_rs, mcpy_rs_ret_addr.
  apply undef_regs_pres. intro HIN. apply NOTIN.
  rewrite in_map_iff in HIN. destruct HIN as [d [Heq Hin]].
  assert (d = IR IR14) by congruence. subst. exact Hin.
Qed.

Lemma addimm_preserves_IR14:
  forall r1 r2 n k (rs: regset) m rs' m',
  exec_straight ge (addimm r1 r2 n k) rs m k rs' m' ->
  r1 <> IR14 ->
  rs' IR14 = rs IR14.
Proof.
  intros.
  destruct (addimm_correct r1 r2 n k rs m) as (rs1 & EX1 & _ & OTH1).
  exploit exec_straight_determ. eexact H. eexact EX1. intros [<- _].
  apply OTH1; eauto with asmgen.
Qed.

Lemma exec_straight_suffix:
  forall c rs m c2 rs2 m2 c3 rs3 m3,
  exec_straight ge c rs m c3 rs3 m3 ->
  exec_straight ge c rs m c2 rs2 m2 ->
  (length c2 > length c3)%nat ->
  exec_straight ge c2 rs2 m2 c3 rs3 m3.
Proof.
  induction 1; intros EX2 HLEN; inv EX2;
  match goal with
  | H1: exec_basic _ ?i _ _ = _, H2: exec_basic _ ?i _ _ = _ |- _ =>
      rewrite H1 in H2; inv H2
  end.
  - lia.
  - exploit exec_straight_length; eauto; lia.
  - auto.
  - eapply IHexec_straight; eauto.
Qed.

Lemma indexed_memcpy_access_preserves_IR14:
  forall mk_instr mk_immed ras rad n1 n2 k (rs: regset) m rs' m',
  exec_straight ge (indexed_memcpy_access mk_instr mk_immed ras rad n1 n2 k) rs m k rs' m' ->
  (forall ras' rad' n1' n2' rs0 m0 rs0' m0',
     exec_basic ge (mk_instr ras' rad' n1' n2') rs0 m0 = Next rs0' m0' -> rs0' IR14 = rs0 IR14) ->
  rs' IR14 = rs IR14.
Proof.
  intros. unfold indexed_memcpy_access in H.
  destruct (Int.eq n1 (mk_immed n1)) eqn:HN1;
  destruct (Int.eq n2 (mk_immed n2)) eqn:HN2;
  destruct (negb (PregEq.eq ras IR3) && negb (PregEq.eq rad IR2)) in H;
  destruct (PregEq.eq ras IR2 && PregEq.eq rad IR2) in H;
  destruct (PregEq.eq ras IR3 && PregEq.eq rad IR3) in H;
  simpl in H;
  try (apply exec_straight_singleton_bi in H; eapply H0; eassumption; fail).
  (* All remaining: addimm + continuation *)
  all: match type of H with exec_straight ge (addimm ?r1 ?r2 ?n ?kk) _ _ _ _ _ =>
    destruct (addimm_correct r1 r2 n kk rs m) as (rs1 & EX1 & _ & OTH1) end.
  all: assert (HR1: rs1 IR14 = rs IR14) by (apply OTH1; [discriminate | reflexivity]).
  all: match type of EX1 with exec_straight ge _ _ _ ?kk _ _ =>
    pose proof (exec_straight_suffix _ _ _ _ _ _ _ _ _ H EX1 ltac:(
      first [ simpl; lia
            | match kk with addimm ?r1 ?r2 ?n ?cont =>
                destruct (addimm_correct r1 r2 n cont rs1 m) as (? & ?HEX & _);
                pose proof (exec_straight_length _ _ _ _ _ _ HEX); simpl in *; lia end ]
    )) as EXEC2 end.
  (* One addimm: EXEC2 is mk_instr ::bi k *)
  all: try (apply exec_straight_singleton_bi in EXEC2;
            transitivity (rs1 IR14); [eapply H0; eassumption | exact HR1]; fail).
  (* Two addimm: EXEC2 is addimm ... (mk_instr ::bi k) *)
  all: match type of EXEC2 with exec_straight ge (addimm ?r1 ?r2 ?n ?kk) _ _ _ _ _ =>
    destruct (addimm_correct r1 r2 n kk rs1 m) as (rs2 & EX2 & _ & OTH2) end.
  all: assert (HR2: rs2 IR14 = rs1 IR14) by (apply OTH2; [discriminate | reflexivity]).
  all: pose proof (exec_straight_suffix _ _ _ _ _ _ _ _ _ EXEC2 EX2 ltac:(simpl; lia)) as EXEC3.
  all: apply exec_straight_singleton_bi in EXEC3.
  all: transitivity (rs2 IR14); [eapply H0; eassumption |].
  all: transitivity (rs1 IR14); [exact HR2 | exact HR1].
Qed.

Lemma transl_memcpy_preserves_IR14:
  forall sz bargs k c (rs: regset) m,
  transl_memcpy sz bargs k = OK c ->
  preserves_IR14 (MBmemcpy sz bargs) = true ->
  forall rs' m',
  exec_straight ge c rs m k rs' m' ->
  rs' IR14 = rs IR14.
Proof.
  intros sz bargs k c rs m TR PRES rs' m' EXEC.
  destruct sz; simpl in PRES; try discriminate.
  unfold transl_memcpy in TR.
  destruct bargs as [| ba1 [| ba2 [|]]]; monadInv TR.
  apply orb_true_iff in PRES.
  destruct (Archi.expanse_memcpy_fpu) eqn:EFPU;
  destruct (thumb tt) eqn:ETHUMB;
  try (exfalso; destruct PRES; discriminate).
  all: cbv delta [select_memcpy_inst] in EXEC;
       rewrite ?EFPU in EXEC; cbv beta iota in EXEC.
  all: try rewrite ETHUMB in EXEC.
  all: cbv beta zeta in EXEC.
  all: try (destruct (preg_eq x1 IR8 || preg_eq x1 IR9); cbv beta iota in EXEC).
  all: eapply indexed_memcpy_access_preserves_IR14; [eexact EXEC |].
  all: intros; cbv beta in *.
  all: first
    [ eapply exec_memcpy_aux_Ofs_IR14; [eassumption |]; simpl; intuition discriminate
    | simpl in *;
      match goal with H: (if ?b then _ else _) = _ |- _ => destruct b; try discriminate end;
      eapply exec_memcpy_aux_Ofs_IR14; [eassumption |]; simpl; intuition discriminate ].
Qed.

Lemma transl_instr_basic_preserves_IR14:
  forall f i r12 k c (rs: regset) m,
  transl_instr_basic f i r12 k = OK c ->
  preserves_IR14 i = true ->
  forall rs' m',
  exec_straight ge c rs m k rs' m' ->
  rs' IR14 = rs IR14.
Proof.
  intros f i r12 k c rs m TR PRES rs' m' EXEC.
  destruct i; simpl in PRES; simpl in TR;
  [ (* MBgetstack *) unfold loadind in TR
  | (* MBsetstack *) unfold storeind in TR
  | (* MBgetparam *) discriminate
  | (* MBop *) eapply transl_op_preserves_IR14; eauto; fail
  | (* MBload *) idtac
  | (* MBstore *) idtac
  | (* MBmemcpy *) idtac
  | (* MBsavecallee *) discriminate
  | (* MBrestorecallee *) discriminate ].
  (* MBgetstack / MBsetstack: single instruction via indexed_memory_access *)
  1-2:
    destruct t; try discriminate;
    destruct (preg_of _) eqn:Hpr; try destruct d; try discriminate;
    inv TR;
    rewrite (indexed_memory_access_eq _ _ _ _ _ PRES) in EXEC;
    apply exec_straight_singleton_bi in EXEC;
    simpl in EXEC;
    ( (eapply exec_load_aux_IR14; [eassumption | intro; apply (preg_of_not_R14 m0); congruence])
    || (eapply exec_store_aux_IR14; eassumption)
    || (unfold exec_load_aux in EXEC; destruct (Mem.loadv _ _ _); [inv EXEC; apply Pregmap.gso; discriminate | discriminate])
    || (unfold exec_store_aux in EXEC; destruct (Mem.storev _ _ _ _); [inv EXEC; auto | discriminate]) ).
  (* MBload: destruct chunk, unfold transl_memory_access, destruct addr *)
  - unfold transl_load in TR. destruct t; [| monadInv TR].
    destruct m0; try discriminate; simpl in PRES;
    ( (unfold transl_memory_access_int in TR; monadInv TR;
       unfold transl_memory_access in EQ0)
    || (unfold transl_memory_access_float in TR; monadInv TR;
        unfold transl_memory_access in EQ0) );
    destruct a; try discriminate; ArgsInv; simpl in PRES;
    try (match goal with H: OK _ = OK _ |- _ => inv H end);
    try (rewrite (indexed_memory_access_eq _ _ _ _ _ PRES) in EXEC);
    apply exec_straight_singleton_bi in EXEC;
    simpl in EXEC;
    ( eapply exec_load_aux_IR14; [eassumption | eauto with asmgen]
    || (unfold exec_load_aux in EXEC; destruct (Mem.loadv _ _ _);
        [inv EXEC; apply Pregmap.gso; discriminate | discriminate]) ).
  (* MBstore: same pattern *)
  - unfold transl_store in TR.
    destruct m0; try discriminate; simpl in PRES;
    ( (unfold transl_memory_access_int in TR; monadInv TR;
       unfold transl_memory_access in EQ0)
    || (unfold transl_memory_access_float in TR; monadInv TR;
        unfold transl_memory_access in EQ0) );
    destruct a; try discriminate; ArgsInv; simpl in PRES;
    try (match goal with H: OK _ = OK _ |- _ => inv H end);
    try (rewrite (indexed_memory_access_eq _ _ _ _ _ PRES) in EXEC);
    apply exec_straight_singleton_bi in EXEC;
    simpl in EXEC;
    ( eapply exec_store_aux_IR14; eassumption
    || (unfold exec_store_aux in EXEC; destruct (Mem.storev _ _ _ _);
        [inv EXEC; auto | discriminate]) ).
  (* MBmemcpy *)
  - eapply transl_memcpy_preserves_IR14; eauto.
Qed.

End CONSTRUCTORS.