Module BTL_SEsimplify


Require Import Coqlib Floats Values Memory.
Require Import Integers AST.
Require Import Op Registers.
Require Import BTL.
Require Import BTL_SEtheory.
Require Import BTL_SEsimuref.
Require Import Asmgen Asmgenproof1.
Require Import OptionMonad ImpHCons.
Require Import IntPromotionCommon.
Require Import FunInd.

Import Notations.
Import SvalNotations.
Import PureComparisons.

Local Open Scope option_monad_scope.

Expansions and rewrites of macro-instructions using "fake" symbolic values


Auxiliary functions

Definition make_lfsv_cmp (is_inv: bool) (fsv1 fsv2: sval) : list_sval :=
  let (fsvfirst, fsvsec) := if is_inv then (fsv1, fsv2) else (fsv2, fsv1) in
  let lfsv := fScons fsvfirst fSnil in
  fScons fsvsec lfsv.

Definition make_lfsv_single (fsv: sval) : list_sval :=
  fScons fsv fSnil.

Definition make_optR (is_x0 is_inv: bool) : option oreg :=
  if is_x0 then
    (if is_inv then Some (X0_L)
    else Some (X0_R))
  else None.

Rewriting functions for comparisons

Definition is_normal_cmp cmp :=
  match cmp with | Cne => false | _ => true end.

Definition is_inv_cmp_int (cmp: comparison) : bool :=
  match cmp with | Cle | Cgt => true | _ => false end.

Definition is_inv_cmp_float (cmp: comparison) : bool :=
  match cmp with | Cge | Cgt => true | _ => false end.

Definition cond_int32s (cmp: comparison) (lsv: list_sval) (optR: option oreg) :=
  match cmp with
  | Ceq => fSop (OEseqw optR) lsv
  | Cne => fSop (OEsnew optR) lsv
  | Clt | Cgt => fSop (OEsltw optR) lsv
  | Cle | Cge =>
      let fsv := (fSop (OEsltw optR) lsv) in
      let lfsv := make_lfsv_single fsv in
      fSop (OExoriw Int.one) lfsv
  end.

Definition cond_int32u (cmp: comparison) (lsv: list_sval) (optR: option oreg) :=
  match cmp with
  | Ceq => fSop (OEsequw optR) lsv
  | Cne => fSop (OEsneuw optR) lsv
  | Clt | Cgt => fSop (OEsltuw optR) lsv
  | Cle | Cge =>
      let fsv := (fSop (OEsltuw optR) lsv) in
      let lfsv := make_lfsv_single fsv in
      fSop (OExoriw Int.one) lfsv
  end.

Definition cond_int64s (cmp: comparison) (lsv: list_sval) (optR: option oreg) :=
  match cmp with
  | Ceq => fSop (OEseql optR) lsv
  | Cne => fSop (OEsnel optR) lsv
  | Clt | Cgt => fSop (OEsltl optR) lsv
  | Cle | Cge =>
      let fsv := (fSop (OEsltl optR) lsv) in
      let lfsv := make_lfsv_single fsv in
      fSop (OExoriw Int.one) lfsv
  end.

Definition cond_int64u (cmp: comparison) (lsv: list_sval) (optR: option oreg) :=
  match cmp with
  | Ceq => fSop (OEsequl optR) lsv
  | Cne => fSop (OEsneul optR) lsv
  | Clt | Cgt => fSop (OEsltul optR) lsv
  | Cle | Cge =>
      let fsv := (fSop (OEsltul optR) lsv) in
      let lfsv := make_lfsv_single fsv in
      fSop (OExoriw Int.one) lfsv
  end.

Definition cond_float (cmp: comparison) (lsv: list_sval) :=
  match cmp with
  | Ceq | Cne => fSop OEfeqd lsv
  | Clt | Cgt => fSop OEfltd lsv
  | Cle | Cge => fSop OEfled lsv
  end.

Definition cond_single (cmp: comparison) (lsv: list_sval) :=
  match cmp with
  | Ceq | Cne => fSop OEfeqs lsv
  | Clt | Cgt => fSop OEflts lsv
  | Cle | Cge => fSop OEfles lsv
  end.

Immediate loads

Definition load_hilo32 (hi lo: int) :=
  if Int.eq lo Int.zero then
    fSop (OEluiw hi) fSnil
  else
    let fsv := fSop (OEluiw hi) fSnil in
    let lfsv := make_lfsv_single fsv in
    fSop (OEaddiw None lo) lfsv.

Definition load_hilo64 (hi lo: int64) :=
  if Int64.eq lo Int64.zero then
    fSop (OEluil hi) fSnil
  else
    let fsv := fSop (OEluil hi) fSnil in
    let lfsv := make_lfsv_single fsv in
    fSop (OEaddil None lo) lfsv.

Definition loadimm32 (n: int) :=
  match make_immed32 n with
  | Imm32_single imm =>
      fSop (OEaddiw (Some X0_R) imm) fSnil
  | Imm32_pair hi lo => load_hilo32 hi lo
  end.

Definition loadimm64 (n: int64) :=
  match make_immed64 n with
  | Imm64_single imm =>
      fSop (OEaddil (Some X0_R) imm) fSnil
  | Imm64_pair hi lo => load_hilo64 hi lo
  | Imm64_large imm => fSop (OEloadli imm) fSnil
  end.

Operations


Definition opimm32 (fsv1: sval) (n: int) (op: operation) (opimm: int -> operation) :=
  match make_immed32 n with
  | Imm32_single imm =>
      let lfsv := make_lfsv_single fsv1 in
      fSop (opimm imm) lfsv
  | Imm32_pair hi lo =>
      let fsv := load_hilo32 hi lo in
      let lfsv := make_lfsv_cmp false fsv1 fsv in
      fSop op lfsv
  end.

Definition opimm64 (fsv1: sval) (n: int64) (op: operation) (opimm: int64 -> operation) :=
  match make_immed64 n with
  | Imm64_single imm =>
      let lfsv := make_lfsv_single fsv1 in
      fSop (opimm imm) lfsv
  | Imm64_pair hi lo =>
      let fsv := load_hilo64 hi lo in
      let lfsv := make_lfsv_cmp false fsv1 fsv in
      fSop op lfsv
  | Imm64_large imm =>
      let fsv := fSop (OEloadli imm) fSnil in
      let lfsv := make_lfsv_cmp false fsv1 fsv in
      fSop op lfsv
  end.

Definition addimm32 (fsv1: sval) (n: int) (or: option oreg) := opimm32 fsv1 n Oadd (OEaddiw or).
Definition andimm32 (fsv1: sval) (n: int) := opimm32 fsv1 n Oand OEandiw.
Definition orimm32 (fsv1: sval) (n: int) := opimm32 fsv1 n Oor OEoriw.
Definition xorimm32 (fsv1: sval) (n: int) := opimm32 fsv1 n Oxor OExoriw.
Definition sltimm32 (fsv1: sval) (n: int) := opimm32 fsv1 n (OEsltw None) OEsltiw.
Definition sltuimm32 (fsv1: sval) (n: int) := opimm32 fsv1 n (OEsltuw None) OEsltiuw.
Definition addimm64 (fsv1: sval) (n: int64) (or: option oreg) := opimm64 fsv1 n Oaddl (OEaddil or).
Definition andimm64 (fsv1: sval) (n: int64) := opimm64 fsv1 n Oandl OEandil.
Definition orimm64 (fsv1: sval) (n: int64) := opimm64 fsv1 n Oorl OEoril.
Definition xorimm64 (fsv1: sval) (n: int64) := opimm64 fsv1 n Oxorl OExoril.
Definition sltimm64 (fsv1: sval) (n: int64) := opimm64 fsv1 n (OEsltl None) OEsltil.
Definition sltuimm64 (fsv1: sval) (n: int64) := opimm64 fsv1 n (OEsltul None) OEsltiul.

Definition expanse_condimm_int32s (cmp: comparison) (fsv1: sval) (n: int) :=
  let is_inv := is_inv_cmp_int cmp in
  if Int.eq n Int.zero then
    let optR := make_optR true is_inv in
    let lfsv := make_lfsv_cmp is_inv fsv1 fsv1 in
    cond_int32s cmp lfsv optR
  else
    match cmp with
    | Ceq | Cne =>
        let optR := make_optR true is_inv in
        let fsv := xorimm32 fsv1 n in
        let lfsv := make_lfsv_cmp false fsv fsv in
        cond_int32s cmp lfsv optR
    | Clt => sltimm32 fsv1 n
    | Cle =>
        if Int.eq n (Int.repr Int.max_signed) then
          let fsv := loadimm32 Int.one in
          let lfsv := make_lfsv_cmp false fsv1 fsv in
          fSop (OEmayundef MUint) lfsv
        else sltimm32 fsv1 (Int.add n Int.one)
    | _ =>
        let optR := make_optR false is_inv in
        let fsv := loadimm32 n in
        let lfsv := make_lfsv_cmp is_inv fsv1 fsv in
        cond_int32s cmp lfsv optR
    end.

Definition expanse_condimm_int32u (cmp: comparison) (fsv1: sval) (n: int) :=
  let is_inv := is_inv_cmp_int cmp in
  if Int.eq n Int.zero then
    let optR := make_optR true is_inv in
    let lfsv := make_lfsv_cmp is_inv fsv1 fsv1 in
    cond_int32u cmp lfsv optR
  else
    match cmp with
    | Clt => sltuimm32 fsv1 n
    | _ =>
        let optR := make_optR false is_inv in
        let fsv := loadimm32 n in
        let lfsv := make_lfsv_cmp is_inv fsv1 fsv in
        cond_int32u cmp lfsv optR
    end.

Definition expanse_condimm_int64s (cmp: comparison) (fsv1: sval) (n: int64) :=
  let is_inv := is_inv_cmp_int cmp in
  if Int64.eq n Int64.zero then
    let optR := make_optR true is_inv in
    let lfsv := make_lfsv_cmp is_inv fsv1 fsv1 in
    cond_int64s cmp lfsv optR
  else
    match cmp with
    | Ceq | Cne =>
        let optR := make_optR true is_inv in
        let fsv := xorimm64 fsv1 n in
        let lfsv := make_lfsv_cmp false fsv fsv in
        cond_int64s cmp lfsv optR
    | Clt => sltimm64 fsv1 n
    | Cle =>
        if Int64.eq n (Int64.repr Int64.max_signed) then
          let fsv := loadimm32 Int.one in
          let lfsv := make_lfsv_cmp false fsv1 fsv in
          fSop (OEmayundef MUlong) lfsv
        else sltimm64 fsv1 (Int64.add n Int64.one)
    | _ =>
        let optR := make_optR false is_inv in
        let fsv := loadimm64 n in
        let lfsv := make_lfsv_cmp is_inv fsv1 fsv in
        cond_int64s cmp lfsv optR
    end.

Definition expanse_condimm_int64u (cmp: comparison) (fsv1: sval) (n: int64) :=
  let is_inv := is_inv_cmp_int cmp in
  if Int64.eq n Int64.zero then
    let optR := make_optR true is_inv in
    let lfsv := make_lfsv_cmp is_inv fsv1 fsv1 in
    cond_int64u cmp lfsv optR
  else
    match cmp with
    | Clt => sltuimm64 fsv1 n
    | _ =>
        let optR := make_optR false is_inv in
        let fsv := loadimm64 n in
        let lfsv := make_lfsv_cmp is_inv fsv1 fsv in
        cond_int64u cmp lfsv optR
    end.

Definition expanse_cond_fp (cnot: bool) fn_cond cmp (lsv: list_sval) :=
  let normal := is_normal_cmp cmp in
  let normal' := if cnot then negb normal else normal in
  let fsv := fn_cond cmp lsv in
  let lfsv := make_lfsv_single fsv in
  if normal' then fsv else fSop (OExoriw Int.one) lfsv.

Function target_op_expanse (op: operation) (lsv: list sval) :=
  match op, lsv with
  | Ocmp (Ccomp c), sv1 :: sv2 :: nil =>
      let is_inv := is_inv_cmp_int c in
      let optR := make_optR false is_inv in
      let lfsv := make_lfsv_cmp is_inv sv1 sv2 in
      Some (cond_int32s c lfsv optR)
  | Ocmp (Ccompu c), sv1 :: sv2 :: nil =>
      let is_inv := is_inv_cmp_int c in
      let optR := make_optR false is_inv in
      let lfsv := make_lfsv_cmp is_inv sv1 sv2 in
      Some (cond_int32u c lfsv optR)
  | Ocmp (Ccompimm c imm), sv1 :: nil =>
      Some (expanse_condimm_int32s c sv1 imm)
  | Ocmp (Ccompuimm c imm), sv1 :: nil =>
      Some (expanse_condimm_int32u c sv1 imm)
  | Ocmp (Ccompl c), sv1 :: sv2 :: nil =>
      let is_inv := is_inv_cmp_int c in
      let optR := make_optR false is_inv in
      let lfsv := make_lfsv_cmp is_inv sv1 sv2 in
      Some (cond_int64s c lfsv optR)
  | Ocmp (Ccomplu c), sv1 :: sv2 :: nil =>
      let is_inv := is_inv_cmp_int c in
      let optR := make_optR false is_inv in
      let lfsv := make_lfsv_cmp is_inv sv1 sv2 in
      Some (cond_int64u c lfsv optR)
  | Ocmp (Ccomplimm c imm), sv1 :: nil =>
      Some (expanse_condimm_int64s c sv1 imm)
  | Ocmp (Ccompluimm c imm), sv1 :: nil =>
      Some (expanse_condimm_int64u c sv1 imm)
  | Ocmp (Ccompf c), sv1 :: sv2 :: nil =>
      let is_inv := is_inv_cmp_float c in
      let lfsv := make_lfsv_cmp is_inv sv1 sv2 in
      Some (expanse_cond_fp false cond_float c lfsv)
  | Ocmp (Cnotcompf c), sv1 :: sv2 :: nil =>
      let is_inv := is_inv_cmp_float c in
      let lfsv := make_lfsv_cmp is_inv sv1 sv2 in
      Some (expanse_cond_fp true cond_float c lfsv)
  | Ocmp (Ccompfs c), sv1 :: sv2 :: nil =>
      let is_inv := is_inv_cmp_float c in
      let lfsv := make_lfsv_cmp is_inv sv1 sv2 in
      Some (expanse_cond_fp false cond_single c lfsv)
  | Ocmp (Cnotcompfs c), sv1 :: sv2 :: nil =>
      let is_inv := is_inv_cmp_float c in
      let lfsv := make_lfsv_cmp is_inv sv1 sv2 in
       Some (expanse_cond_fp true cond_single c lfsv)
  | Ofloatconst f, nil =>
      let fsv := loadimm64 (Float.to_bits f) in
      let lfsv := make_lfsv_single fsv in
      Some (fSop (Ofloat_of_bits) lfsv)
  | Osingleconst f, nil =>
      let fsv := loadimm32 (Float32.to_bits f) in
      let lfsv := make_lfsv_single fsv in
      Some (fSop (Osingle_of_bits) lfsv)
  | Ointconst n, nil =>
      Some (loadimm32 n)
  | Olongconst n, nil =>
      Some (loadimm64 n)
  | Oaddimm n, sv1 :: nil =>
      Some (addimm32 sv1 n None)
  | Oaddlimm n, sv1 :: nil =>
      Some (addimm64 sv1 n None)
  | Oandimm n, sv1 :: nil =>
      Some (andimm32 sv1 n)
  | Oandlimm n, sv1 :: nil =>
      Some (andimm64 sv1 n)
  | Oorimm n, sv1 :: nil =>
      Some (orimm32 sv1 n)
  | Oorlimm n, sv1 :: nil =>
      Some (orimm64 sv1 n)
  | Oxorimm n, sv1 :: nil =>
      Some (xorimm32 sv1 n)
  | Oxorlimm n, sv1 :: nil =>
      Some (xorimm64 sv1 n)
  | Ocast8signed, sv1 :: nil =>
      let lfsv := make_lfsv_single sv1 in
      let fsv := fSop (Oshlimm (Int.repr 24)) lfsv in
      let hl' := make_lfsv_single fsv in
      Some (fSop (Oshrimm (Int.repr 24)) hl')
  | Ocast16signed, sv1 :: nil =>
      let lfsv := make_lfsv_single sv1 in
      let fsv := fSop (Oshlimm (Int.repr 16)) lfsv in
      let hl' := make_lfsv_single fsv in
      Some (fSop (Oshrimm (Int.repr 16)) hl')
  | Ocast32unsigned, sv1 :: nil =>
      let lfsv := make_lfsv_single sv1 in
      let cast32s_s := fSop Ocast32signed lfsv in
      let cast32s_l := make_lfsv_single cast32s_s in
      let sllil_s := fSop (Oshllimm (Int.repr 32)) cast32s_l in
      let sllil_l := make_lfsv_single sllil_s in
      Some (fSop (Oshrluimm (Int.repr 32)) sllil_l)
  | Oshrximm n, sv1 :: nil =>
      let lfsv := make_lfsv_single sv1 in
      if Int.eq n Int.zero then
        let lhl := make_lfsv_cmp false sv1 sv1 in
        Some (fSop (OEmayundef (MUshrx n)) lhl)
      else
        if Int.eq n Int.one then
          let srliw_s := fSop (Oshruimm (Int.repr 31)) lfsv in
          let srliw_l := make_lfsv_cmp false sv1 srliw_s in
          let addw_s := fSop Oadd srliw_l in
          let addw_l := make_lfsv_single addw_s in
          let sraiw_s := fSop (Oshrimm Int.one) addw_l in
          let sraiw_l := make_lfsv_cmp false sraiw_s sraiw_s in
          Some (fSop (OEmayundef (MUshrx n)) sraiw_l)
        else
          let sraiw_s := fSop (Oshrimm (Int.repr 31)) lfsv in
          let sraiw_l := make_lfsv_single sraiw_s in
          let srliw_s := fSop (Oshruimm (Int.sub Int.iwordsize n)) sraiw_l in
          let srliw_l := make_lfsv_cmp false sv1 srliw_s in
          let addw_s := fSop Oadd srliw_l in
          let addw_l := make_lfsv_single addw_s in
          let sraiw_s' := fSop (Oshrimm n) addw_l in
          let sraiw_l' := make_lfsv_cmp false sraiw_s' sraiw_s' in
          Some (fSop (OEmayundef (MUshrx n)) sraiw_l')
  | Oshrxlimm n, sv1 :: nil =>
      let lfsv := make_lfsv_single sv1 in
      if Int.eq n Int.zero then
        let lhl := make_lfsv_cmp false sv1 sv1 in
        Some (fSop (OEmayundef (MUshrxl n)) lhl)
      else
        if Int.eq n Int.one then
          let srlil_s := fSop (Oshrluimm (Int.repr 63)) lfsv in
          let srlil_l := make_lfsv_cmp false sv1 srlil_s in
          let addl_s := fSop Oaddl srlil_l in
          let addl_l := make_lfsv_single addl_s in
          let srail_s := fSop (Oshrlimm Int.one) addl_l in
          let srail_l := make_lfsv_cmp false srail_s srail_s in
          Some (fSop (OEmayundef (MUshrxl n)) srail_l)
        else
          let srail_s := fSop (Oshrlimm (Int.repr 63)) lfsv in
          let srail_l := make_lfsv_single srail_s in
          let srlil_s := fSop (Oshrluimm (Int.sub Int64.iwordsize' n)) srail_l in
          let srlil_l := make_lfsv_cmp false sv1 srlil_s in
          let addl_s := fSop Oaddl srlil_l in
          let addl_l := make_lfsv_single addl_s in
          let srail_s' := fSop (Oshrlimm n) addl_l in
          let srail_l' := make_lfsv_cmp false srail_s' srail_s' in
          Some (fSop (OEmayundef (MUshrxl n)) srail_l')
  | _, _ => None
  end.

Branches


Definition transl_cbranch_int32s (cmp: comparison) (optR: option oreg) :=
  match cmp with
  | Ceq => CEbeqw optR
  | Cne => CEbnew optR
  | Clt => CEbltw optR
  | Cle => CEbgew optR
  | Cgt => CEbltw optR
  | Cge => CEbgew optR
  end.

Definition transl_cbranch_int32u (cmp: comparison) (optR: option oreg) :=
  match cmp with
  | Ceq => CEbequw optR
  | Cne => CEbneuw optR
  | Clt => CEbltuw optR
  | Cle => CEbgeuw optR
  | Cgt => CEbltuw optR
  | Cge => CEbgeuw optR
  end.

Definition transl_cbranch_int64s (cmp: comparison) (optR: option oreg) :=
  match cmp with
  | Ceq => CEbeql optR
  | Cne => CEbnel optR
  | Clt => CEbltl optR
  | Cle => CEbgel optR
  | Cgt => CEbltl optR
  | Cge => CEbgel optR
  end.

Definition transl_cbranch_int64u (cmp: comparison) (optR: option oreg) :=
  match cmp with
  | Ceq => CEbequl optR
  | Cne => CEbneul optR
  | Clt => CEbltul optR
  | Cle => CEbgeul optR
  | Cgt => CEbltul optR
  | Cge => CEbgeul optR
  end.

Definition expanse_cbranch_fp (cnot: bool) fn_cond cmp (lfsv: list_sval) : (condition * list_sval) :=
  let normal := is_normal_cmp cmp in
  let normal' := if cnot then negb normal else normal in
  let fsv := fn_cond cmp lfsv in
  let lfsv' := make_lfsv_cmp false fsv fsv in
  if normal' then ((CEbnew (Some X0_R)), lfsv') else ((CEbeqw (Some X0_R)), lfsv').

Function target_cbranch_expanse (cond: condition) (args: list sval) :=
  match cond, args with
  | (Ccomp c), (a1 :: a2 :: nil) =>
      let is_inv := is_inv_cmp_int c in
      let cond := transl_cbranch_int32s c (make_optR false is_inv) in
      let lfsv := make_lfsv_cmp is_inv a1 a2 in
      Some (cond, lfsv)
  | (Ccompu c), (a1 :: a2 :: nil) =>
      let is_inv := is_inv_cmp_int c in
      let cond := transl_cbranch_int32u c (make_optR false is_inv) in
      let lfsv := make_lfsv_cmp is_inv a1 a2 in
      Some (cond, lfsv)
  | (Ccompimm c n), (a1 :: nil) =>
      let is_inv := is_inv_cmp_int c in
      (if Int.eq n Int.zero then
        let lfsv := make_lfsv_cmp is_inv a1 a1 in
        let cond := transl_cbranch_int32s c (make_optR true is_inv) in
        Some (cond, lfsv)
      else
        let fsv := loadimm32 n in
        let lfsv := make_lfsv_cmp is_inv a1 fsv in
        let cond := transl_cbranch_int32s c (make_optR false is_inv) in
        Some (cond, lfsv))
  | (Ccompuimm c n), (a1 :: nil) =>
      let is_inv := is_inv_cmp_int c in
      (if Int.eq n Int.zero then
        let lfsv := make_lfsv_cmp is_inv a1 a1 in
        let cond := transl_cbranch_int32u c (make_optR true is_inv) in
        Some (cond, lfsv)
      else
        let fsv := loadimm32 n in
        let lfsv := make_lfsv_cmp is_inv a1 fsv in
        let cond := transl_cbranch_int32u c (make_optR false is_inv) in
         Some (cond, lfsv))
  | (Ccompl c), (a1 :: a2 :: nil) =>
      let is_inv := is_inv_cmp_int c in
      let cond := transl_cbranch_int64s c (make_optR false is_inv) in
      let lfsv := make_lfsv_cmp is_inv a1 a2 in
      Some (cond, lfsv)
  | (Ccomplu c), (a1 :: a2 :: nil) =>
      let is_inv := is_inv_cmp_int c in
      let cond := transl_cbranch_int64u c (make_optR false is_inv) in
      let lfsv := make_lfsv_cmp is_inv a1 a2 in
      Some (cond, lfsv)
  | (Ccomplimm c n), (a1 :: nil) =>
      let is_inv := is_inv_cmp_int c in
      (if Int64.eq n Int64.zero then
        let lfsv := make_lfsv_cmp is_inv a1 a1 in
        let cond := transl_cbranch_int64s c (make_optR true is_inv) in
        Some (cond, lfsv)
      else
        let fsv := loadimm64 n in
        let lfsv := make_lfsv_cmp is_inv a1 fsv in
        let cond := transl_cbranch_int64s c (make_optR false is_inv) in
        Some (cond, lfsv))
  | (Ccompluimm c n), (a1 :: nil) =>
      let is_inv := is_inv_cmp_int c in
      (if Int64.eq n Int64.zero then
        let lfsv := make_lfsv_cmp is_inv a1 a1 in
        let cond := transl_cbranch_int64u c (make_optR true is_inv) in
        Some (cond, lfsv)
      else
        let fsv := loadimm64 n in
        let lfsv := make_lfsv_cmp is_inv a1 fsv in
        let cond := transl_cbranch_int64u c (make_optR false is_inv) in
        Some (cond, lfsv))
  | (Ccompf c), (a1 :: a2 :: nil) =>
      let is_inv := is_inv_cmp_float c in
      let lfsv := make_lfsv_cmp is_inv a1 a2 in
      Some (expanse_cbranch_fp false cond_float c lfsv)
  | (Cnotcompf c), (a1 :: a2 :: nil) =>
      let is_inv := is_inv_cmp_float c in
      let lfsv := make_lfsv_cmp is_inv a1 a2 in
      Some (expanse_cbranch_fp true cond_float c lfsv)
  | (Ccompfs c), (a1 :: a2 :: nil) =>
      let is_inv := is_inv_cmp_float c in
      let lfsv := make_lfsv_cmp is_inv a1 a2 in
      Some (expanse_cbranch_fp false cond_single c lfsv)
  | (Cnotcompfs c), (a1 :: a2 :: nil) =>
      let is_inv := is_inv_cmp_float c in
      let lfsv := make_lfsv_cmp is_inv a1 a2 in
      Some (expanse_cbranch_fp true cond_single c lfsv)
  | _, _ => None
   end.


Affine terms and SR rewrites


Local Open Scope positive_scope.

Declare Scope hashlist_match.
Notation "'[]'" := (Snil _): hashlist_match.
Notation "'[' sv1 ';' sv2 ']'" := (Scons sv1 (Scons sv2 (Snil _) _) _): hashlist_match.

Declare Scope hashlist_full.
Notation "[ \ h ]" := (Snil h): hashlist_full.
Notation "[ sv1 \ h1 ; sv2 \ h2 ; \ h3 ]" := (Scons sv1 (Scons sv2 (Snil h1) h2) h3) (h2 at next level): hashlist_full.

Local Open Scope hashlist_full.

Definition op_aff64_rv := Oaddl.

Function select_op_aff64_rv op :=
  match op with
  | Oaddl => true
  | _ => false
  end.

Function find_subterm_aff64_rv sv :=
  match sv with
  | Sinput true r h => Some (inr r)
  | Sop (Olongconst l) [ \ h1 ] h2 => Some (inl l)
  | _ => None
  end.

Definition find_term_const_aff64_rv sv1 sv2 :=
  match find_subterm_aff64_rv sv1, find_subterm_aff64_rv sv2 with
  | Some (inl l), _ => Some (l, sv2)
  | _, Some (inl l) => Some (l, sv1)
  | _, _ => None
  end.

Function find_mul_aff64_rv sv :=
  match sv with
  | Sop Omull [ sv1 \ h1 ; sv2 \ h2; \ h3 ] h4 =>
      find_term_const_aff64_rv sv1 sv2
  | _ => None
  end.

Definition build_const_aff64_rv l := fSop (Olongconst l) fSnil.
Definition build_mul_aff64_rv sv1 sv2 := fSop Omull (make_lfsv_cmp false sv1 sv2).
Definition build_flsv_single sv := fScons sv fSnil.

Definition scale_aff64_rv l1 sv :=
  match find_mul_aff64_rv sv with
  | Some (l2, sv') =>
      let const := build_const_aff64_rv (Int64.mul l1 l2) in
      build_mul_aff64_rv const sv'
  | None =>
      match find_subterm_aff64_rv sv with
      | Some (inl l2) =>
          build_const_aff64_rv (Int64.mul l1 l2)
      | _ =>
          let const := build_const_aff64_rv l1 in
          build_mul_aff64_rv const sv
      end
  end.

Function is_aff64_rv sv :=
  match sv with
  | Sfoldr op l sv0 h =>
      if select_op_aff64_rv op then Some ((Oaddl, l), sv0)
      else None
  | _ => None
  end.

Definition merge_aff64_rv sv1 sv2 :=
  match find_mul_aff64_rv sv1, find_mul_aff64_rv sv2 with
  | Some (l1, sv1), Some (l2, sv2) =>
      let const := build_const_aff64_rv (Int64.add l1 l2) in
      Some (build_mul_aff64_rv const sv1)
  | Some (l, sv), None
  | None, Some (l, sv) =>
      let const := build_const_aff64_rv (Int64.add Int64.one l) in
      Some (build_mul_aff64_rv const sv)
  | _, _ => match find_subterm_aff64_rv sv1 with
            | Some (inr _) =>
                let const := build_const_aff64_rv (Int64.repr 2) in
                Some (build_mul_aff64_rv const sv1)
            | _ => None
            end
  end.

Definition acc0_aff64_rv sv1 sv2 :=
  match find_subterm_aff64_rv sv1, find_subterm_aff64_rv sv2 with
  | Some (inl l1), Some (inl l2) => build_const_aff64_rv (Int64.add l1 l2)
  | _, _ => fSop op_aff64_rv (make_lfsv_cmp false sv1 sv2)
  end.

Definition compare_aff64_rv sv1 sv2 :=
  match find_mul_aff64_rv sv1, find_mul_aff64_rv sv2 with
  | Some (l1, sv1), Some (l2, sv2) => fast_cmp sval_get_hid sv1 sv2
  | Some (l, sv), None => fast_cmp sval_get_hid sv sv2
  | None, Some (l, sv) => fast_cmp sval_get_hid sv sv1
  | _, _ => fast_cmp sval_get_hid sv1 sv2
  end.

Declare Scope sum_type_scope.

Notation "'INL' X <- A 'IN' B" := (match A with inl X => B | inr _ => None end)
         (at level 200, X name, A at level 100, B at level 200)
         : sum_type_scope.

Local Open Scope sum_type_scope.

Definition foldrof_aff64_rv sv: (operation * list_sval * sval) :=
  match is_aff64_rv sv with
  | Some (op, lsv, sv0) => (op, lsv, sv0)
  | None =>
      match find_subterm_aff64_rv sv with
      | Some (inl l) => (op_aff64_rv, fSnil, build_const_aff64_rv l)
      | _ =>
          let lsv := build_flsv_single sv in
          (op_aff64_rv, lsv, build_const_aff64_rv Int64.zero)
      end
  end.

Local Open Scope lazy_bool_scope.

Definition add_foldr_aff64_rv sv1 sv2 :=
  let (t1, sv0_1) := foldrof_aff64_rv sv1 in let (op1, lsv1) := t1 in
  let (t2, sv0_2) := foldrof_aff64_rv sv2 in let (op2, lsv2) := t2 in
  if eq_operation op1 op2 &&& select_op_aff64_rv op1 then
    let lsv := merge merge_aff64_rv compare_aff64_rv lsv1 lsv2 in
    SOME s_l1_sv <- find_subterm_aff64_rv sv0_1 IN
    SOME s_l2_sv <- find_subterm_aff64_rv sv0_2 IN
    INL l1 <- s_l1_sv IN INL l2 <- s_l2_sv IN
    let const := build_const_aff64_rv (Int64.add l1 l2) in
    Some (fSfoldr op1 lsv const)
  else None.

Some examples for computing affine forms


Operation SR

Definition op_strength_reduction (op: operation) (lsv: list sval) :=
  match op, lsv with
  | Oshllimm n, sv :: nil =>
      if negb (Int.ltu n Int64.iwordsize') then None else
      let l := Int64.shl' Int64.one n in
      Some (rescale select_op_aff64_rv (scale_aff64_rv l) sv)
  | Oaddlimm l, sv :: nil =>
      let const := build_const_aff64_rv l in
      add_foldr_aff64_rv const sv
  | Oaddl, sv1 :: sv2 :: nil =>
      add_foldr_aff64_rv sv1 sv2
  | Omull, sv1 :: sv2 :: nil =>
      SOME l_sv <- find_term_const_aff64_rv sv1 sv2 IN
      let (l, sv) := l_sv in
      Some (rescale select_op_aff64_rv (scale_aff64_rv l) sv)
  | _, _ => None
  end.

Local Close Scope positive_scope.


Promotions rewrites


Definition promote_sval (sgn : bool) (sv : sval) : sval :=
  fSop (if sgn then Ocast32signed else Ocast32unsigned) (fScons sv fSnil).

Definition promote_svalb (sgn : bool) (sv : sval) (b : bool) : sval :=
  if b then promote_sval sgn sv else sv.

Definition ok_cast_sgn (sgn ceq sgn' : bool) : bool :=
  ceq || bool_eq sgn' sgn.

Definition unpromote_arg (sgn ceq : bool) (arg : sval) (prom : bool) : option sval :=
  if prom
  then match arg with
       | Sop Ocast32unsigned (Scons arg (Snil _) _) _ => ASSERT (ok_cast_sgn sgn ceq false) IN Some arg
       | Sop Ocast32signed (Scons arg (Snil _) _) _ => ASSERT (ok_cast_sgn sgn ceq true) IN Some arg
       | _ => None
       end
  else Some arg.

Fixpoint unpromote_args (sgn : bool) (args : list sval) (pargs : list bool) (cargs : list bool)
  : option (list_sval) :=
  match args, pargs, cargs with
  | nil, nil, nil => Some fSnil
  | arg :: args, prom :: pargs, ceq :: cargs =>
      SOME r0 <- unpromote_arg sgn ceq arg prom IN
      SOME rs <- unpromote_args sgn args pargs cargs IN
      Some (fScons r0 rs)
  | _, _, _ => None
  end.

Definition unpromote_op (op : operation) (args : list sval) (iinfo : option inst_info):
  option (sval * list okclause) :=
  SOME iinfo <- iinfo IN
  match iinfo.(inst_promotion) with
  | IPromotedOp op0 prom sgn ret_sgn =>
      SOME op' <- op_prom_sgb sgn prom IN
      ASSERT (eq_operation op' op) IN
      ASSERT (ok_cast_sgn sgn prom.(op_prom_res_eq) ret_sgn) IN
      SOME args0 <- unpromote_args sgn args
                        prom.(op_prom_args) prom.(op_prom_args_eq) IN
      let sv0 := fSop op0 args0 in
      let okc := fOKpromotableOp op0 prom args0 in
      Some (promote_svalb ret_sgn sv0 prom.(op_prom_res), okc :: nil)
  | _ => None
  end.

Definition unpromote_condition (cond : condition) (args : list sval) (iinfo : inst_info)
  : option (condition * list_sval * list okclause) :=
  match iinfo.(inst_promotion) with
  | IPromotedCond cond0 prom sgn =>
      SOME cond' <- cond_prom_sgb sgn prom IN
      ASSERT (eq_condition cond' cond) IN
      SOME args0 <- unpromote_args sgn args
                        (map (fun _ => true) args) prom.(cond_prom_args_eq) IN
      let okc := fOKpromotableCond cond0 prom args0 in
      Some (cond0, args0, okc :: nil)
  | _ => None
  end.


Choosing what to do according to rewriting rules set


Definition rewrite_ops (RRULES: rrules_set) (op: operation) (lsv: list sval) (iinfo : option inst_info):
  option (sval * list okclause) :=
  match RRULES with
  | RRexpansions => SOME sv <- target_op_expanse op lsv IN
                    Some (sv, nil)
  | RRlct true _ => SOME sv <- op_strength_reduction op lsv IN
                    Some (sv, nil)
  | RRpromotion => unpromote_op op lsv iinfo
  | _ => None
  end.

Definition rewrite_cbranches (RRULES: rrules_set)
  (cond: condition) (args: list sval) (iinfo : inst_info) : option (condition * list_sval * list okclause) :=
  match RRULES with
  | RRexpansions =>
      match target_cbranch_expanse cond args with
      | Some (cond', args') => Some (cond', args', nil)
      | None => None
      end
  | RRpromotion => unpromote_condition cond args iinfo
  | _ => None
  end.


Branch pruning


Local Open Scope impure_scope.

Function type_of_inequality cond: option typ :=
  match cond with
  | Ccompu Cne | Ccomplu Cne
  | Ccompf Cne | Cnotcompf Ceq | Ccompfs Cne | Cnotcompfs Ceq =>
      match type_of_condition cond with
      | ty :: l => Some ty
      | _ => None
      end
  | _ => None
  end.

Function prunable_inequality cond sv: bool :=
  match sv with
  | Sop (Owelldef ty1) (Scons (Sload _ NOTRAP _ _ _ _) (Snil _) _) _=>
      match type_of_inequality cond with
      | Some ty2 => subtype ty1 ty2
      | None => false
      end
  | _ => false
  end.

Definition branch_pruning cond lsv: ?? (option bool) :=
  match lsv_len_two lsv with
  | Some (sv1, sv2) =>
      DO b <~ phys_eq sv1 sv2;;
      if bool_eq b true then
        if prunable_inequality cond sv1 then RET (Some false)
        else RET None
      else RET None
  | None => RET None
  end.