Definition compare : forall x y : t, Compare lt eq x y.Definition eq_dec: forall (x y: t), {x = y} + {x <> y} := OrderedEquation.eq_dec.Module EqSet := FSetAVLplus.Make(OrderedEquation).Module EqSet2 := FSetAVLplus.Make(OrderedEquation').(** We use a redundant representation for sets of equations, comprising
  two AVL finite sets, containing the same elements, but ordered along
  the two orders defined above.  Playing on properties of lexicographic
  orders, this redundant representation enables us to quickly find
  all equations involving a given RTL pseudoregister, or all equations
  involving a given LTL location or overlapping location. *)

Record eqs := mkeqs {
  eqs1 :> EqSet.t;
  eqs2 : EqSet2.t;
  eqs_same: forall q, EqSet2.In q eqs2 <-> EqSet.In q eqs1
}.(** We use a redundant representation for sets of equations, comprising
  two AVL finite sets, containing the same elements, but ordered along
  the two orders defined above.  Playing on properties of lexicographic
  orders, this redundant representation enables us to quickly find
  all equations involving a given RTL pseudoregister, or all equations
  involving a given LTL location or overlapping location. *)

Record eqs := mkeqs {
  eqs1 :> EqSet.t;
  eqs2 : EqSet2.t;
  eqs_same: forall q, EqSet2.In q eqs2 <-> EqSet.In q eqs1
}.(** We use a redundant representation for sets of equations, comprising
  two AVL finite sets, containing the same elements, but ordered along
  the two orders defined above.  Playing on properties of lexicographic
  orders, this redundant representation enables us to quickly find
  all equations involving a given RTL pseudoregister, or all equations
  involving a given LTL location or overlapping location. *)

Record eqs := mkeqs {
  eqs1 :> EqSet.t;
  eqs2 : EqSet2.t;
  eqs_same: forall q, EqSet2.In q eqs2 <-> EqSet.In q eqs1
}.(** We use a redundant representation for sets of equations, comprising
  two AVL finite sets, containing the same elements, but ordered along
  the two orders defined above.  Playing on properties of lexicographic
  orders, this redundant representation enables us to quickly find
  all equations involving a given RTL pseudoregister, or all equations
  involving a given LTL location or overlapping location. *)

Record eqs := mkeqs {
  eqs1 :> EqSet.t;
  eqs2 : EqSet2.t;
  eqs_same: forall q, EqSet2.In q eqs2 <-> EqSet.In q eqs1
}.(** We use a redundant representation for sets of equations, comprising
  two AVL finite sets, containing the same elements, but ordered along
  the two orders defined above.  Playing on properties of lexicographic
  orders, this redundant representation enables us to quickly find
  all equations involving a given RTL pseudoregister, or all equations
  involving a given LTL location or overlapping location. *)

Record eqs := mkeqs {
  eqs1 :> EqSet.t;
  eqs2 : EqSet2.t;
  eqs_same: forall q, EqSet2.In q eqs2 <-> EqSet.In q eqs1
}.(** * Operations on sets of equations *)

(** The empty set of equations. *)

Program Definition empty_eqs := mkeqs EqSet.empty EqSet2.empty _.(** Adding or removing an equation from a set. *)

Program Definition add_equation (q: equation) (e: eqs) :=
  mkeqs (EqSet.add q (eqs1 e)) (EqSet2.add q (eqs2 e)) _.Program Definition remove_equation (q: equation) (e: eqs) :=
  mkeqs (EqSet.remove q (eqs1 e)) (EqSet2.remove q (eqs2 e)) _.(** [reg_unconstrained r e] is true if [e] contains no equations involving
  the RTL pseudoregister [r].  In other words, all equations [r' = l [kind]]
  in [e] are such that [r' <> r]. *)

Definition select_reg_l (r: reg) (q: equation) := Pos.leb r (ereg q).Definition select_reg_h (r: reg) (q: equation) := Pos.leb (ereg q) r.Definition reg_unconstrained (r: reg) (e: eqs) : bool :=
  negb (EqSet.mem_between (select_reg_l r) (select_reg_h r) (eqs1 e)).(** [loc_unconstrained l e] is true if [e] contains no equations involving
  the LTL location [l] or a location that partially overlaps with [l].
  In other words, all equations [r = l' [kind]] in [e] are such that
  [Loc.diff l' l]. *)

Definition select_loc_l (l: loc) :=
  let lb := OrderedLoc.diff_low_bound l in
  fun (q: equation) => match OrderedLoc.compare (eloc q) lb with LT _ => false | _ => true end.Definition select_loc_h (l: loc) :=
  let lh := OrderedLoc.diff_high_bound l in
  fun (q: equation) => match OrderedLoc.compare (eloc q) lh with GT _ => false | _ => true end.Definition loc_unconstrained (l: loc) (e: eqs) : bool :=
  negb (EqSet2.mem_between (select_loc_l l) (select_loc_h l) (eqs2 e)).Definition reg_loc_unconstrained (r: reg) (l: loc) (e: eqs) : bool :=
  reg_unconstrained r e && loc_unconstrained l e.(** [subst_reg r1 r2 e] simulates the effect of assigning [r2] to [r1] on [e].
  All equations of the form [r1 = l [kind]] are replaced by [r2 = l [kind]].
*)

Definition subst_reg (r1 r2: reg) (e: eqs) : eqs :=
  EqSet.fold
    (fun q e => add_equation (Eq (ekind q) r2 (eloc q)) (remove_equation q e))
    (EqSet.elements_between (select_reg_l r1) (select_reg_h r1) (eqs1 e))
    e.(** [subst_reg_kind r1 k1 r2 k2 e] simulates the effect of assigning
  the [k2] part of [r2] to the [k1] part of [r1] on [e].
  All equations of the form [r1 = l [k1]] are replaced by [r2 = l [k2]].
*)

Definition subst_reg_kind (r1: reg) (k1: equation_kind) (r2: reg) (k2: equation_kind) (e: eqs) : eqs :=
  EqSet.fold
    (fun q e =>
      if IndexedEqKind.eq (ekind q) k1
      then add_equation (Eq k2 r2 (eloc q)) (remove_equation q e)
      else e)
    (EqSet.elements_between (select_reg_l r1) (select_reg_h r1) (eqs1 e))
    e.(** [subst_loc l1 l2 e] simulates the effect of assigning [l2] to [l1] on [e].
  All equations of the form [r = l1 [kind]] are replaced by [r = l2 [kind]].
  Return [None] if [e] contains an equation of the form [r = l] with [l]
  partially overlapping [l1].
*)

Definition subst_loc (l1 l2: loc) (e: eqs) : option eqs :=
  EqSet2.fold
    (fun q opte =>
      match opte with
      | None => None
      | Some e =>
          if Loc.eq l1 (eloc q) then
            Some (add_equation (Eq (ekind q) (ereg q) l2) (remove_equation q e))
          else
            None
      end)
     (EqSet2.elements_between (select_loc_l l1) (select_loc_h l1) (eqs2 e))
     (Some e).(** [subst_loc_part l1 l2 k e] simulates the effect of assigning
  [l2] to the [k] part of [l1] on [e].
  All equations of the form [r = l1 [k]] are replaced by [r = l2 [Full]].
  Return [None] if [e] contains an equation of the form [r = l] with [l]
  partially overlapping [l1], or an equation of the form [r = l1] with
  a kind different from [k1].
*)

Definition subst_loc_part (l1: loc) (l2: loc) (k: equation_kind) (e: eqs) : option eqs :=
  EqSet2.fold
    (fun q opte =>
      match opte with
      | None => None
      | Some e =>
          if Loc.eq l1 (eloc q) then
            if IndexedEqKind.eq (ekind q) k
            then Some (add_equation (Eq Full (ereg q) l2) (remove_equation q e))
            else None
          else
            None
      end)
     (EqSet2.elements_between (select_loc_l l1) (select_loc_h l1) (eqs2 e))
     (Some e).(** [subst_loc_pair l1 l2 l2'] simulates the effect of assigning
  [makelong l2 l2'] to [l1].  All equations of the form [r = l1 [Full]]
  are replaced by the two equations [r = l2 [High], r = l2' [Low]].
  Return [None] if [e] contains an equation of the form [r = l] with [l]
  partially overlapping [l1], or an equation of the form [r = l1] with
  a kind different from [Full]. *)

Definition subst_loc_pair (l1 l2 l2': loc) (e: eqs) : option eqs :=
  EqSet2.fold
    (fun q opte =>
      match opte with
      | None => None
      | Some e =>
          if Loc.eq l1 (eloc q) then
            if IndexedEqKind.eq (ekind q) Full
            then Some (add_equation (Eq High (ereg q) l2)
                        (add_equation (Eq Low (ereg q) l2')
                           (remove_equation q e)))
            else None
          else
            None
      end)
     (EqSet2.elements_between (select_loc_l l1) (select_loc_h l1) (eqs2 e))
     (Some e).(** [loc_type_compat env l e] checks that for all equations [r = l] in [e],
  the type [env r] of [r] is compatible with the type of [l]. *)

Definition sel_type (k: equation_kind) (ty: typ) : typ :=
  match k with
  | Full => ty
  | Low | High => Tint
  end.Definition loc_type_compat (env: regenv) (l: loc) (e: eqs) : bool :=
  EqSet2.for_all_between
    (fun q => subtype (sel_type (ekind q) (env (ereg q))) (Loc.type l))
    (select_loc_l l) (select_loc_h l) (eqs2 e).(** [long_type_compat env l e] checks that for all equations [r = l] in [e].
  then type [env r] of [r] is compatible with the type [Tlong]. *)

Definition long_type_compat (env: regenv) (l: loc) (e: eqs) : bool :=
  EqSet2.for_all_between
    (fun q => subtype (env (ereg q)) Tlong)
    (select_loc_l l) (select_loc_h l) (eqs2 e).(** [add_equations [r1...rN] [m1...mN] e] adds to [e] the [N] equations
    [ri = R mi [Full]].  Return [None] if the two lists have different lengths.
*)

Fixpoint add_equations (rl: list reg) (ml: list mreg) (e: eqs) : option eqs :=
  match rl, ml with
  | nil, nil => Some e
  | r1 :: rl, m1 :: ml => add_equations rl ml (add_equation (Eq Full r1 (R m1)) e)
  | _, _ => None
  end.(** [add_equations_ros] adds an equation, if needed, between an optional
  pseudoregister and an optional machine register.  It is used for the
  function argument of the [Icall] and [Itailcall] instructions. *)

Definition add_equation_ros (ros: reg + ident) (ros': mreg + ident) (e: eqs) : option eqs :=
  match ros, ros' with
  | inl r, inl mr => Some(add_equation (Eq Full r (R mr)) e)
  | inr id, inr id' => assertion (ident_eq id id'); Some e
  | _, _ => None
  end.(** [add_equations_builtin_arg] adds the needed equations for arguments
    to builtin functions. *)

Fixpoint add_equations_builtin_arg
     (env: regenv) (arg: builtin_arg reg) (arg': builtin_arg loc) (e: eqs) : option eqs :=
  match arg, arg' with
  | BA r, BA l =>
      Some (add_equation (Eq Full r l) e)
  | BA r, BA_splitlong (BA lhi) (BA llo) =>
      assertion (typ_eq (env r) Tlong);
      assertion (Archi.splitlong);
      Some (add_equation (Eq Low r llo) (add_equation (Eq High r lhi) e))
  | BA_int n, BA_int n' =>
      assertion (Int.eq_dec n n'); Some e
  | BA_long n, BA_long n' =>
      assertion (Int64.eq_dec n n'); Some e
  | BA_float f, BA_float f' =>
      assertion (Float.eq_dec f f'); Some e
  | BA_single f, BA_single f' =>
      assertion (Float32.eq_dec f f'); Some e
  | BA_loadstack chunk ofs, BA_loadstack chunk' ofs' =>
      assertion (chunk_eq chunk chunk');
      assertion (Ptrofs.eq_dec ofs ofs');
      Some e
  | BA_addrstack ofs, BA_addrstack ofs' =>
      assertion (Ptrofs.eq_dec ofs ofs');
      Some e
  | BA_loadglobal chunk id ofs, BA_loadglobal chunk' id' ofs' =>
      assertion (chunk_eq chunk chunk');
      assertion (ident_eq id id');
      assertion (Ptrofs.eq_dec ofs ofs');
      Some e
  | BA_addrglobal id ofs, BA_addrglobal id' ofs' =>
      assertion (ident_eq id id');
      assertion (Ptrofs.eq_dec ofs ofs');
      Some e
  | BA_splitlong hi lo, BA_splitlong hi' lo' =>
      do e1 <- add_equations_builtin_arg env hi hi' e;
      add_equations_builtin_arg env lo lo' e1
  | BA_addptr a1 a2, BA_addptr a1' a2' =>
      do e1 <- add_equations_builtin_arg env a1 a1' e;
      add_equations_builtin_arg env a2 a2' e1
  | _, _ =>
      None
  end.Fixpoint add_equations_builtin_args
   (env: regenv) (args: list (builtin_arg reg))
   (args': list (builtin_arg loc)) (e: eqs) : option eqs :=
  match args, args' with
  | nil, nil => Some e
  | a1 :: al, a1' :: al' =>
      do e1 <- add_equations_builtin_arg env a1 a1' e;
      add_equations_builtin_args env al al' e1
  | _, _ => None
  end.(** For [EF_debug] builtins, some arguments can be removed. *)

Fixpoint add_equations_debug_args
   (env: regenv) (args: list (builtin_arg reg))
   (args': list (builtin_arg loc)) (e: eqs) : option eqs :=
  match args, args' with
  | _, nil => Some e
  | a1 :: al, a1' :: al' =>
      match add_equations_builtin_arg env a1 a1' e with
      | None => add_equations_debug_args env al args' e
      | Some e1 => add_equations_debug_args env al al' e1
      end
  | _, _ => None
  end.(** Checking of the result of a builtin *)

Definition remove_equations_builtin_res
    (env: regenv) (res: builtin_res reg) (res': builtin_res mreg) (e: eqs) : option eqs :=
  match res, res' with
  | BR r, BR r' => Some (remove_equation (Eq Full r (R r')) e)
  | BR r, BR_splitlong (BR rhi) (BR rlo) =>
      assertion (typ_eq (env r) Tlong);
      if mreg_eq rhi rlo then None else
        Some (remove_equation (Eq Low r (R rlo))
                (remove_equation (Eq High r (R rhi)) e))
  | BR_none, BR_none => Some e
  | _, _ => None
  end.(** [can_undef ml] returns true if all machine registers in [ml] are
  unconstrained and can harmlessly be undefined. *)

Fixpoint can_undef (ml: list mreg) (e: eqs) : bool :=
  match ml with
  | nil => true
  | m1 :: ml => loc_unconstrained (R m1) e && can_undef ml e
  end.Fixpoint can_undef_except (l: loc) (ml: list mreg) (e: eqs) : bool :=
  match ml with
  | nil => true
  | m1 :: ml =>
      (Loc.eq l (R m1) || loc_unconstrained (R m1) e) && can_undef_except l ml e
  end.(** [no_caller_saves e] returns [e] if all caller-save locations are
  unconstrained in [e].  In other words, [e] contains no equations
  involving a caller-save register or [Outgoing] stack slot. *)

Definition no_caller_saves (e: eqs) : bool :=
  EqSet.for_all
   (fun eq =>
     match eloc eq with
       | R r => is_callee_save r
       | S Outgoing _ _ => false
       | S _ _ _ => true
       end)
    e.(** [compat_left r l e] returns true if all equations in [e] that involve
    [r] are of the form [r = l [Full]]. *)

Definition compat_left (r: reg) (l: loc) (e: eqs) : bool :=
  EqSet.for_all_between
    (fun q =>
        match ekind q with
        | Full => Loc.eq l (eloc q)
        | _ => false
        end)
    (select_reg_l r) (select_reg_h r)
    (eqs1 e).(** [compat_left2 r l1 l2 e] returns true if all equations in [e] that involve
    [r] are of the form [r = l1 [High]] or [r = l2 [Low]]. *)

Definition compat_left2 (r: reg) (l1 l2: loc) (e: eqs) : bool :=
  EqSet.for_all_between
    (fun q =>
        match ekind q with
        | High => Loc.eq l1 (eloc q)
        | Low => Loc.eq l2 (eloc q)
        | _ => false
        end)
    (select_reg_l r) (select_reg_h r)
    (eqs1 e).(** [ros_compatible_tailcall ros] returns true if [ros] is a function
  name or a caller-save register.  This is used to check [Itailcall]
  instructions. *)

Definition ros_compatible_tailcall (ros: mreg + ident) : bool :=
  match ros with
  | inl r => negb (is_callee_save r)
  | inr id => true
  end.(** * The validator *)

Definition destroyed_by_move (src dst: loc) :=
  match src, dst with
  | S sl ofs ty, _ => destroyed_by_getstack sl
  | _, S sl ofs ty => destroyed_by_setstack ty
  | _, _ => destroyed_by_op Omove
  end.Definition well_typed_move (env: regenv) (dst: loc) (e: eqs) : bool :=
  match dst with
  | R r => true
  | S sl ofs ty => loc_type_compat env dst e
  end.(** Simulate the effect of a sequence of moves [mv] on a set of
  equations [e].  The set [e] is the equations that must hold
  after the sequence of moves.  Return the set of equations that
  must hold before the sequence of moves.  Return [None] if the
  set of equations [e] cannot hold after the sequence of moves. *)

Fixpoint track_moves (env: regenv) (mv: moves) (e: eqs) : option eqs :=
  match mv with
  | nil => Some e
  | MV src dst :: mv =>
      do e1 <- track_moves env mv e;
      assertion (can_undef_except dst (destroyed_by_move src dst)) e1;
      assertion (well_typed_move env dst e1);
      subst_loc dst src e1
  | MVmakelong src1 src2 dst :: mv =>
      assertion (negb Archi.ptr64);
      do e1 <- track_moves env mv e;
      assertion (long_type_compat env (R dst) e1);
      subst_loc_pair (R dst) (R src1) (R src2) e1
  | MVlowlong src dst :: mv =>
      assertion (negb Archi.ptr64);
      do e1 <- track_moves env mv e;
      subst_loc_part (R dst) (R src) Low e1
  | MVhighlong src dst :: mv =>
      assertion (negb Archi.ptr64);
      do e1 <- track_moves env mv e;
      subst_loc_part (R dst) (R src) High e1
  end.(** [transfer_use_def args res args' res' undefs e] returns the set
  of equations that must hold "before" in order for the equations [e]
  to hold "after" the execution of RTL and LTL code of the following form:
<<
                RTL                            LTL
         use pseudoregs args            use machine registers args'
         define pseudoreg res           undefine machine registers undef
                                        define machine register res'
>>
  As usual, [None] is returned if the equations [e] cannot hold after
  this execution.
*)

Definition transfer_use_def (args: list reg) (res: reg) (args': list mreg) (res': mreg)
                            (undefs: list mreg) (e: eqs) : option eqs :=
  let e1 := remove_equation (Eq Full res (R res')) e in
  assertion (reg_loc_unconstrained res (R res') e1);
  assertion (can_undef undefs e1);
  add_equations args args' e1.Definition kind_first_word := if Archi.big_endian then High else Low.Definition kind_second_word := if Archi.big_endian then Low else High.(** The core transfer function.  It takes a set [e] of equations that must
  hold "after" and a block shape [shape] representing a matching pair
  of an RTL instruction and an LTL basic block.  It returns the set of
  equations that must hold "before" these instructions, or [None] if
  impossible. *)

Definition transfer_aux (f: RTL.function) (env: regenv)
                        (shape: block_shape) (e: eqs) : option eqs :=
  match shape with
  | BSnop mv s =>
      track_moves env mv e
  | BSmove src dst mv s =>
      track_moves env mv (subst_reg dst src e)
  | BSmakelong src1 src2 dst mv s =>
      let e1 := subst_reg_kind dst High src1 Full e in
      let e2 := subst_reg_kind dst Low src2 Full e1 in
      assertion (reg_unconstrained dst e2);
      track_moves env mv e2
  | BSlowlong src dst mv s =>
      let e1 := subst_reg_kind dst Full src Low e in
      assertion (reg_unconstrained dst e1);
      track_moves env mv e1
  | BShighlong src dst mv s =>
      let e1 := subst_reg_kind dst Full src High e in
      assertion (reg_unconstrained dst e1);
      track_moves env mv e1
  | BSop op args res mv1 args' res' mv2 s =>
      do e1 <- track_moves env mv2 e;
      do e2 <- transfer_use_def args res args' res' (destroyed_by_op op) e1;
      track_moves env mv1 e2
  | BSopdead op args res mv s =>
      assertion (reg_unconstrained res e);
      track_moves env mv e
  | BSload chunk addr args dst mv1 args' dst' mv2 s =>
      do e1 <- track_moves env mv2 e;
      do e2 <- transfer_use_def args dst args' dst' (destroyed_by_load chunk addr) e1;
      track_moves env mv1 e2
  | BSload2 addr addr' args dst mv1 args1' dst1' mv2 args2' dst2' mv3 s =>
      do e1 <- track_moves env mv3 e;
      let e2 := remove_equation (Eq kind_second_word dst (R dst2')) e1 in
      assertion (loc_unconstrained (R dst2') e2);
      assertion (can_undef (destroyed_by_load Mint32 addr') e2);
      do e3 <- add_equations args args2' e2;
      do e4 <- track_moves env mv2 e3;
      let e5 := remove_equation (Eq kind_first_word dst (R dst1')) e4 in
      assertion (loc_unconstrained (R dst1') e5);
      assertion (can_undef (destroyed_by_load Mint32 addr) e5);
      assertion (reg_unconstrained dst e5);
      do e6 <- add_equations args args1' e5;
      track_moves env mv1 e6
  | BSload2_1 addr args dst mv1 args' dst' mv2 s =>
      do e1 <- track_moves env mv2 e;
      let e2 := remove_equation (Eq kind_first_word dst (R dst')) e1 in
      assertion (reg_loc_unconstrained dst (R dst') e2);
      assertion (can_undef (destroyed_by_load Mint32 addr) e2);
      do e3 <- add_equations args args' e2;
      track_moves env mv1 e3
  | BSload2_2 addr addr' args dst mv1 args' dst' mv2 s =>
      do e1 <- track_moves env mv2 e;
      let e2 := remove_equation (Eq kind_second_word dst (R dst')) e1 in
      assertion (reg_loc_unconstrained dst (R dst') e2);
      assertion (can_undef (destroyed_by_load Mint32 addr') e2);
      do e3 <- add_equations args args' e2;
      track_moves env mv1 e3
  | BSloaddead chunk addr args dst mv s =>
      assertion (reg_unconstrained dst e);
      track_moves env mv e
  | BSstore chunk addr args src mv args' src' s =>
      assertion (can_undef (destroyed_by_store chunk addr) e);
      do e1 <- add_equations (src :: args) (src' :: args') e;
      track_moves env mv e1
  | BSstore2 addr addr' args src mv1 args1' src1' mv2 args2' src2' s =>
      assertion (can_undef (destroyed_by_store Mint32 addr') e);
      do e1 <- add_equations args args2'
                  (add_equation (Eq kind_second_word src (R src2')) e);
      do e2 <- track_moves env mv2 e1;
      assertion (can_undef (destroyed_by_store Mint32 addr) e2);
      do e3 <- add_equations args args1'
                  (add_equation (Eq kind_first_word src (R src1')) e2);
      track_moves env mv1 e3
  | BScall sg ros args res mv1 ros' mv2 s =>
      let args' := loc_arguments sg in
      let res' := loc_result sg in
      do e1 <- track_moves env mv2 e;
      do e2 <- remove_equations_res res res' e1;
      assertion (forallb (fun l => reg_loc_unconstrained res l e2)
                         (map R (regs_of_rpair res')));
      assertion (no_caller_saves e2);
      do e3 <- add_equation_ros ros ros' e2;
      do e4 <- add_equations_args args (sig_args sg) args' e3;
      track_moves env mv1 e4
  | BStailcall sg ros args mv1 ros' =>
      let args' := loc_arguments sg in
      assertion (tailcall_is_possible sg);
      assertion (opt_typ_eq sg.(sig_res) f.(RTL.fn_sig).(sig_res));
      assertion (ros_compatible_tailcall ros');
      do e1 <- add_equation_ros ros ros' empty_eqs;
      do e2 <- add_equations_args args (sig_args sg) args' e1;
      track_moves env mv1 e2
  | BSbuiltin ef args res mv1 args' res' mv2 s =>
      do e1 <- track_moves env mv2 e;
      do e2 <- remove_equations_builtin_res env res res' e1;
      assertion (forallb (fun r => reg_unconstrained r e2)
                         (params_of_builtin_res res));
      assertion (forallb (fun mr => loc_unconstrained (R mr) e2)
                         (params_of_builtin_res res'));
      assertion (can_undef (destroyed_by_builtin ef) e2);
      do e3 <-
        match ef with
        | EF_debug _ _ _ => add_equations_debug_args env args args' e2
        | _              => add_equations_builtin_args env args args' e2
        end;
      track_moves env mv1 e3
  | BScond cond args mv args' s1 s2 =>
      assertion (can_undef (destroyed_by_cond cond) e);
      do e1 <- add_equations args args' e;
      track_moves env mv e1
  | BSjumptable arg mv arg' tbl =>
      assertion (can_undef destroyed_by_jumptable e);
      track_moves env mv (add_equation (Eq Full arg (R arg')) e)
  | BSreturn None mv =>
      track_moves env mv empty_eqs
  | BSreturn (Some arg) mv =>
      let arg' := loc_result (RTL.fn_sig f) in
      do e1 <- add_equations_res arg (sig_res (RTL.fn_sig f)) arg' empty_eqs;
      track_moves env mv e1
  end.Definition t := res eqs.Definition eq (x y: t) :=
    match x, y with
    | OK a, OK b => EqSet.Equal a b
    | Error _, Error _ => True
    | _, _ => False
    end.

Lemma eq_sym: forall x y, eq x y -> eq y x.