Definition equiv (se1 se2: t) : Prop :=
(forall id, find_symbol se2 id = find_symbol se1 id)
/\ (forall id, public_symbol se2 id = public_symbol se1 id)
/\ (forall b, block_is_volatile se2 b = block_is_volatile se1 b).
End Senv.
Module Genv.
(** * Global environments *)
Section GENV.
Variable F: Type.  (**r The type of function descriptions *)
Variable V: Type.  (**r The type of information attached to variables *)
(** The type of global environments. *)
Record t: Type := mkgenv {
genv_public: list ident;              (**r which symbol names are public *)
genv_symb: PTree.t block;             (**r mapping symbol -> block *)
genv_defs: PTree.t (globdef F V);     (**r mapping block -> definition *)
genv_next: block;                     (**r next symbol pointer *)
genv_symb_range: forall id b, PTree.get id genv_symb = Some b -> Plt b genv_next;
genv_defs_range: forall b g, PTree.get b genv_defs = Some g -> Plt b genv_next;
genv_vars_inj: forall id1 id2 b,
PTree.get id1 genv_symb = Some b -> PTree.get id2 genv_symb = Some b -> id1 = id2
}.
(** ** Lookup functions *)
(** [find_symbol ge id] returns the block associated with the given name, if any *)
Definition find_symbol (ge: t) (id: ident) : option block :=
PTree.get id ge.(genv_symb).
(** [symbol_address ge id ofs] returns a pointer into the block associated with [id], at byte offset [ofs].  [Vundef] is returned if no block is associated to [id]. *)
Definition symbol_address (ge: t) (id: ident) (ofs: ptrofs) : val :=
match find_symbol ge id with
| Some b => Vptr b ofs
| None => Vundef
end.
(** [public_symbol ge id] says whether the name [id] is public and defined. *)
Definition public_symbol (ge: t) (id: ident) : bool :=
match find_symbol ge id with
| None => false
| Some _ => In_dec ident_eq id ge.(genv_public)
end.
(** [find_def ge b] returns the global definition associated with the given address. *)
Definition find_def (ge: t) (b: block) : option (globdef F V) :=
PTree.get b ge.(genv_defs).
(** [find_funct_ptr ge b] returns the function description associated with the given address. *)
Definition find_funct_ptr (ge: t) (b: block) : option F :=
match find_def ge b with Some (Gfun f) => Some f | _ => None end.
(** [find_funct] is similar to [find_funct_ptr], but the function address is given as a value, which must be a pointer with offset 0. *)
Definition find_funct (ge: t) (v: val) : option F :=
match v with
| Vptr b ofs => if Ptrofs.eq_dec ofs Ptrofs.zero then find_funct_ptr ge b else None
| _ => None
end.
(** [invert_symbol ge b] returns the name associated with the given block, if any *)
Definition invert_symbol (ge: t) (b: block) : option ident :=
PTree.fold
(fun res id b' => if eq_block b b' then Some id else res)
ge.(genv_symb) None.
(** [find_var_info ge b] returns the information attached to the variable at address [b]. *)
Definition find_var_info (ge: t) (b: block) : option (globvar V) :=
match find_def ge b with Some (Gvar v) => Some v | _ => None end.
(** [block_is_volatile ge b] returns [true] if [b] points to a global variable of volatile type, [false] otherwise. *)
Definition block_is_volatile (ge: t) (b: block) : bool :=
match find_var_info ge b with
| None => false
| Some gv => gv.(gvar_volatile)
end.
(** ** Constructing the global environment *)
Program Definition add_global (ge: t) (idg: ident * globdef F V) : t :=
@mkgenv
ge.(genv_public)
(PTree.set idg#1 ge.(genv_next) ge.(genv_symb))
(PTree.set ge.(genv_next) idg#2 ge.(genv_defs))
(Pos.succ ge.(genv_next))
_ _ _.
Next Obligation.
destruct ge; simpl in *.
rewrite PTree.gsspec in H. destruct (peq id i). inv H. apply Plt_succ.
apply Plt_trans_succ; eauto.
Qed.
Next Obligation.
destruct ge; simpl in *.
rewrite PTree.gsspec in H. destruct (peq b genv_next0).
inv H. apply Plt_succ.
apply Plt_trans_succ; eauto.
Qed.
Next Obligation.
destruct ge; simpl in *.
rewrite PTree.gsspec in H. rewrite PTree.gsspec in H0.
destruct (peq id1 i); destruct (peq id2 i).
congruence.
inv H. eelim Plt_strict. eapply genv_symb_range0; eauto.
inv H0. eelim Plt_strict. eapply genv_symb_range0; eauto.
eauto.
Qed.
Definition add_globals (ge: t) (gl: list (ident * globdef F V)) : t :=
List.fold_left add_global gl ge.

(* Specifications: *)
Lemma add_globals_app:
forall gl2 gl1 ge,
add_globals ge (gl1 ++ gl2) = add_globals (add_globals ge gl1) gl2.