Section LOCATIONS.
Variable ge: genv.
Variable sp: block.
Inductive nlive: nmem -> block -> Z -> Prop :=
| nlive_intro: forall stk gl b ofs
(STK: b = sp -> ~ISet.In ofs stk)
(GL: forall id iv,
Genv.find_symbol ge id = Some b ->
gl!id = Some iv ->
~ISet.In ofs iv),
nlive (NMem stk gl) b ofs.
(** All locations are live *)
Definition nmem_all := NMem ISet.empty (PTree.empty _).
(** Add a range of live locations to [nm].  The range starts at the abstract pointer [p] and has length [sz]. *)
Definition nmem_add (nm: nmem) (p: aptr) (sz: Z) : nmem :=
match nm with
| NMemDead => nmem_all       (**r very conservative, should never happen *)
| NMem stk gl =>
match p with
| Gl id ofs =>
match gl!id with
| Some iv => NMem stk (PTree.set id (ISet.remove (Ptrofs.unsigned ofs) (Ptrofs.unsigned ofs + sz) iv) gl)
| None => nm
end
| Glo id =>
NMem stk (PTree.remove id gl)
| Stk ofs =>
NMem (ISet.remove (Ptrofs.unsigned ofs) (Ptrofs.unsigned ofs + sz) stk) gl
| Stack =>
NMem ISet.empty gl
| _ => nmem_all
end
end.
(** Remove a range of locations from [nm], marking these locations as dead. The range starts at the abstract pointer [p] and has length [sz]. *)
Definition nmem_remove (nm: nmem) (p: aptr) (sz: Z) : nmem :=
match nm with
| NMemDead => NMemDead
| NMem stk gl =>
match p with
| Gl id ofs =>
let iv' :=
match gl!id with
| Some iv => ISet.add (Ptrofs.unsigned ofs) (Ptrofs.unsigned ofs + sz) iv
| None    => ISet.interval (Ptrofs.unsigned ofs) (Ptrofs.unsigned ofs + sz)
end in
NMem stk (PTree.set id iv' gl)
| Stk ofs =>
NMem (ISet.add (Ptrofs.unsigned ofs) (Ptrofs.unsigned ofs + sz) stk) gl
| _ => nm
end
end.
(** Test (conservatively) whether some locations in the range delimited by [p] and [sz] can be live in [nm]. *)
Definition nmem_contains (nm: nmem) (p: aptr) (sz: Z) :=
match nm with
| NMemDead => false
| NMem stk gl =>
match p with
| Gl id ofs =>
match gl!id with
| Some iv => negb (ISet.contains (Ptrofs.unsigned ofs) (Ptrofs.unsigned ofs + sz) iv)
| None => true
end
| Stk ofs =>
negb (ISet.contains (Ptrofs.unsigned ofs) (Ptrofs.unsigned ofs + sz) stk)
| _ => true  (**r conservative answer *)
end
end.
(** Kill all stack locations between 0 and [sz], and mark everything else as live.  This reflects the effect of freeing the stack block at a [Ireturn] or [Itailcall] instruction. *)
Definition nmem_dead_stack (sz: Z) :=
NMem (ISet.interval 0 sz) (PTree.empty _).
(** Least upper bound *)
Definition nmem_lub (nm1 nm2: nmem) : nmem :=
match nm1, nm2 with
| NMemDead, _ => nm2
| _, NMemDead => nm1
| NMem stk1 gl1, NMem stk2 gl2 =>
NMem (ISet.inter stk1 stk2)
(PTree.combine
(fun o1 o2 =>
match o1, o2 with
| Some iv1, Some iv2 => Some(ISet.inter iv1 iv2)
| _, _ => None
end)
gl1 gl2)
end.
(** Boolean-valued equality test *)
Definition nmem_beq (nm1 nm2: nmem) : bool :=
match nm1, nm2 with
| NMemDead, NMemDead => true
| NMem stk1 gl1, NMem stk2 gl2 => ISet.beq stk1 stk2 && PTree.beq ISet.beq gl1 gl2
| _, _ => false
end.

Lemma nmem_beq_sound:
forall nm1 nm2 b ofs,
nmem_beq nm1 nm2 = true ->
(nlive nm1 b ofs <-> nlive nm2 b ofs).