(* *********************************************************************)
(*                                                                     *)
(*              The Compcert verified compiler                         *)
(*                                                                     *)
(*          Xavier Leroy, INRIA Paris-Rocquencourt                     *)
(*                                                                     *)
(*  Copyright Institut National de Recherche en Informatique et en     *)
(*  Automatique.  All rights reserved.  This file is distributed       *)
(*  under the terms of the GNU General Public License as published by  *)
(*  the Free Software Foundation, either version 2 of the License, or  *)
(*  (at your option) any later version.  This file is also distributed *)
(*  under the terms of the INRIA Non-Commercial License Agreement.     *)
(*                                                                     *)
(* *********************************************************************)
(** Observable events, execution traces, and semantics of external calls. *)
Require Intv.
(** * Events and traces *)
(** The observable behaviour of programs is stated in terms of input/output events, which represent the actions of the program that the external world can observe.  CompCert leaves much flexibility as to the exact content of events: the only requirement is that they do not expose memory states nor pointer values (other than pointers to global variables), because these are not preserved literally during compilation.  For concreteness, we use the following type for events.  Each event represents either:  - A system call (e.g. an input/output operation), recording the name of the system call, its parameters, and its result.  - A volatile load from a global memory location, recording the chunk and address being read and the value just read.  - A volatile store to a global memory location, recording the chunk and address being written and the value stored there.  - An annotation, recording the text of the annotation and the values of the arguments.  The values attached to these events are of the following form. As mentioned above, we do not expose pointer values directly. Pointers relative to a global variable are shown with the name of the variable instead of the block identifier. *)
Inductive eventval: Type :=
| EVint: int -> eventval
| EVlong: int64 -> eventval
| EVfloat: float -> eventval
| EVsingle: float32 -> eventval
| EVptr_global: ident -> ptrofs -> eventval.
Inductive event: Type :=
| Event_syscall: string -> list eventval -> eventval -> event
| Event_vload: memory_chunk -> ident -> ptrofs -> eventval -> event
| Event_vstore: memory_chunk -> ident -> ptrofs -> eventval -> event
| Event_annot: string -> list eventval -> event.
(** The dynamic semantics for programs collect traces of events. Traces are of two kinds: finite (type [trace]) or infinite (type [traceinf]). *)
Definition trace := list event.
Definition E0 : trace := nil.
Definition Eapp (t1 t2: trace) : trace := t1 ++ t2.
CoInductive traceinf : Type :=
| Econsinf: event -> traceinf -> traceinf.
Fixpoint Eappinf (t: trace) (T: traceinf) {struct t} : traceinf :=
match t with
| nil => T
| ev :: t' => Econsinf ev (Eappinf t' T)
end.
(** Concatenation of traces is written [**] in the finite case or [***] in the infinite case. *)
Infix "**" := Eapp (at level 60, right associativity).
Infix "***" := Eappinf (at level 60, right associativity).
Hint Rewrite E0_left E0_right Eapp_assoc
E0_left_inf Eappinf_assoc: trace_rewrite.
Opaque trace E0 Eapp Eappinf.
(** The following [traceEq] tactic proves equalities between traces or infinite traces. *)
Ltac substTraceHyp :=
match goal with
| [ H: (@eq trace ?x ?y) |- _ ] =>
subst x || clear H
end.
Ltac decomposeTraceEq :=
match goal with
| [ |- (_ ** _) = (_ ** _) ] =>
apply (f_equal2 Eapp); auto; decomposeTraceEq
| _ =>
auto
end.
Ltac traceEq :=
repeat substTraceHyp; autorewrite with trace_rewrite; decomposeTraceEq.
(** Bisimilarity between infinite traces. *)
CoInductive traceinf_sim: traceinf -> traceinf -> Prop :=
| traceinf_sim_cons: forall e T1 T2,
traceinf_sim T1 T2 ->
traceinf_sim (Econsinf e T1) (Econsinf e T2).
CoInductive traceinf_sim': traceinf -> traceinf -> Prop :=
| traceinf_sim'_cons: forall t T1 T2,
t <> E0 -> traceinf_sim' T1 T2 -> traceinf_sim' (t *** T1) (t *** T2).
(** An alternate presentation of infinite traces as infinite concatenations of nonempty finite traces. *)
CoInductive traceinf': Type :=
| Econsinf': forall (t: trace) (T: traceinf'), t <> E0 -> traceinf'.
Program Definition split_traceinf' (t: trace) (T: traceinf') (NE: t <> E0): event * traceinf' :=
match t with
| nil => _
| e :: nil => (e, T)
| e :: t' => (e, Econsinf' t' T _)
end.
Next Obligation.
elimtype False. elim NE. auto.
Qed.
Next Obligation.
red; intro. elim (H e). rewrite H0. auto.
Qed.
CoFixpoint traceinf_of_traceinf' (T': traceinf') : traceinf :=
match T' with
| Econsinf' t T'' NOTEMPTY =>
let (e, tl) := split_traceinf' t T'' NOTEMPTY in
Econsinf e (traceinf_of_traceinf' tl)
end.
Remark unroll_traceinf':
forall T, T = match T with Econsinf' t T' NE => Econsinf' t T' NE end.
Remark unroll_traceinf:
forall T, T = match T with Econsinf t T' => Econsinf t T' end.

Lemma traceinf_traceinf'_app:
forall t T NE,
traceinf_of_traceinf' (Econsinf' t T NE) = t *** traceinf_of_traceinf' T.