(** * Alternate operational semantics (big-step) *)
(** We now define another semantics for Cminor without [goto] that follows the ``big-step'' style of semantics, also known as natural semantics. In this style, just like expressions evaluate to values, statements evaluate to``outcomes'' indicating how execution should proceed afterwards. *)
Inductive outcome: Type :=
| Out_normal: outcome                (**r continue in sequence *)
| Out_exit: nat -> outcome           (**r terminate [n+1] enclosing blocks *)
| Out_return: option val -> outcome  (**r return immediately to caller *)
| Out_tailcall_return: val -> outcome. (**r already returned to caller via a tailcall *)
Definition outcome_block (out: outcome) : outcome :=
match out with
| Out_exit O => Out_normal
| Out_exit (S n) => Out_exit n
| out => out
end.
Definition outcome_result_value
(out: outcome) (retsig: option typ) (vres: val) : Prop :=
match out with
| Out_normal => vres = Vundef
| Out_return None => vres = Vundef
| Out_return (Some v) => retsig <> None /\ vres = v
| Out_tailcall_return v => vres = v
| _ => False
end.
Definition outcome_free_mem
(out: outcome) (m: mem) (sp: block) (sz: Z) (m': mem) :=
match out with
| Out_tailcall_return _ => m' = m
| _ => Mem.free m sp 0 sz = Some m'
end.
Section NATURALSEM.
Variable ge: genv.
(** Evaluation of a function invocation: [eval_funcall ge m f args t m' res] means that the function [f], applied to the arguments [args] in memory state [m], returns the value [res] in modified memory state [m']. [t] is the trace of observable events generated during the invocation. *)
Inductive eval_funcall:
mem -> fundef -> list val -> trace ->
mem -> val -> Prop :=
| eval_funcall_internal:
forall m f vargs m1 sp e t e2 m2 out vres m3,
Mem.alloc m 0 f.(fn_stackspace) = (m1, sp) ->
set_locals f.(fn_vars) (set_params vargs f.(fn_params)) = e ->
exec_stmt f (Vptr sp Ptrofs.zero) e m1 f.(fn_body) t e2 m2 out ->
outcome_result_value out f.(fn_sig).(sig_res) vres ->
outcome_free_mem out m2 sp f.(fn_stackspace) m3 ->
eval_funcall m (Internal f) vargs t m3 vres
| eval_funcall_external:
forall ef m args t res m',
external_call ef ge args m t res m' ->
eval_funcall m (External ef) args t m' res
(** Execution of a statement: [exec_stmt ge f sp e m s t e' m' out] means that statement [s] executes with outcome [out]. [e] is the initial environment and [m] is the initial memory state. [e'] is the final environment, reflecting variable assignments performed by [s].  [m'] is the final memory state, reflecting memory stores performed by [s].  [t] is the trace of I/O events performed during the execution.  The other parameters are as in [eval_expr]. *)
with exec_stmt:
function -> val ->
env -> mem -> stmt -> trace ->
env -> mem -> outcome -> Prop :=
| exec_Sskip:
forall f sp e m,
exec_stmt f sp e m Sskip E0 e m Out_normal
| exec_Sassign:
forall f sp e m id a v,
eval_expr ge sp e m a v ->
exec_stmt f sp e m (Sassign id a) E0 (PTree.set id v e) m Out_normal
| exec_Sstore:
forall f sp e m chunk addr a vaddr v m',
eval_expr ge sp e m addr vaddr ->
eval_expr ge sp e m a v ->
Mem.storev chunk m vaddr v = Some m' ->
exec_stmt f sp e m (Sstore chunk addr a) E0 e m' Out_normal
| exec_Scall:
forall f sp e m optid sig a bl vf vargs fd t m' vres e',
eval_expr ge sp e m a vf ->
eval_exprlist ge sp e m bl vargs ->
Genv.find_funct ge vf = Some fd ->
funsig fd = sig ->
eval_funcall m fd vargs t m' vres ->
e' = set_optvar optid vres e ->
exec_stmt f sp e m (Scall optid sig a bl) t e' m' Out_normal
| exec_Sbuiltin:
forall f sp e m optid ef bl t m' vargs vres e',
eval_exprlist ge sp e m bl vargs ->
external_call ef ge vargs m t vres m' ->
e' = set_optvar optid vres e ->
exec_stmt f sp e m (Sbuiltin optid ef bl) t e' m' Out_normal
| exec_Sifthenelse:
forall f sp e m a s1 s2 v b t e' m' out,
eval_expr ge sp e m a v ->
Val.bool_of_val v b ->
exec_stmt f sp e m (if b then s1 else s2) t e' m' out ->
exec_stmt f sp e m (Sifthenelse a s1 s2) t e' m' out
| exec_Sseq_continue:
forall f sp e m t s1 t1 e1 m1 s2 t2 e2 m2 out,
exec_stmt f sp e m s1 t1 e1 m1 Out_normal ->
exec_stmt f sp e1 m1 s2 t2 e2 m2 out ->
t = t1 ** t2 ->
exec_stmt f sp e m (Sseq s1 s2) t e2 m2 out
| exec_Sseq_stop:
forall f sp e m t s1 s2 e1 m1 out,
exec_stmt f sp e m s1 t e1 m1 out ->
out <> Out_normal ->
exec_stmt f sp e m (Sseq s1 s2) t e1 m1 out
| exec_Sloop_loop:
forall f sp e m s t t1 e1 m1 t2 e2 m2 out,
exec_stmt f sp e m s t1 e1 m1 Out_normal ->
exec_stmt f sp e1 m1 (Sloop s) t2 e2 m2 out ->
t = t1 ** t2 ->
exec_stmt f sp e m (Sloop s) t e2 m2 out
| exec_Sloop_stop:
forall f sp e m t s e1 m1 out,
exec_stmt f sp e m s t e1 m1 out ->
out <> Out_normal ->
exec_stmt f sp e m (Sloop s) t e1 m1 out
| exec_Sblock:
forall f sp e m s t e1 m1 out,
exec_stmt f sp e m s t e1 m1 out ->
exec_stmt f sp e m (Sblock s) t e1 m1 (outcome_block out)
| exec_Sexit:
forall f sp e m n,
exec_stmt f sp e m (Sexit n) E0 e m (Out_exit n)
| exec_Sswitch:
forall f sp e m islong a cases default v n,
eval_expr ge sp e m a v ->
switch_argument islong v n ->
exec_stmt f sp e m (Sswitch islong a cases default)
E0 e m (Out_exit (switch_target n default cases))
| exec_Sreturn_none:
forall f sp e m,
exec_stmt f sp e m (Sreturn None) E0 e m (Out_return None)
| exec_Sreturn_some:
forall f sp e m a v,
eval_expr ge sp e m a v ->
exec_stmt f sp e m (Sreturn (Some a)) E0 e m (Out_return (Some v))
| exec_Stailcall:
forall f sp e m sig a bl vf vargs fd t m' m'' vres,
eval_expr ge (Vptr sp Ptrofs.zero) e m a vf ->
eval_exprlist ge (Vptr sp Ptrofs.zero) e m bl vargs ->
Genv.find_funct ge vf = Some fd ->
funsig fd = sig ->
Mem.free m sp 0 f.(fn_stackspace) = Some m' ->
eval_funcall m' fd vargs t m'' vres ->
exec_stmt f (Vptr sp Ptrofs.zero) e m (Stailcall sig a bl) t e m'' (Out_tailcall_return vres).
Scheme eval_funcall_ind2 := Minimality for eval_funcall Sort Prop
with exec_stmt_ind2 := Minimality for exec_stmt Sort Prop.
Combined Scheme eval_funcall_exec_stmt_ind2
from eval_funcall_ind2, exec_stmt_ind2.
(** Coinductive semantics for divergence. [evalinf_funcall ge m f args t] means that the function [f] diverges when applied to the arguments [args] in memory state [m].  The infinite trace [t] is the trace of observable events generated during the invocation. *)
CoInductive evalinf_funcall:
mem -> fundef -> list val -> traceinf -> Prop :=
| evalinf_funcall_internal:
forall m f vargs m1 sp e t,
Mem.alloc m 0 f.(fn_stackspace) = (m1, sp) ->
set_locals f.(fn_vars) (set_params vargs f.(fn_params)) = e ->
execinf_stmt f (Vptr sp Ptrofs.zero) e m1 f.(fn_body) t ->
evalinf_funcall m (Internal f) vargs t
(** [execinf_stmt ge sp e m s t] means that statement [s] diverges. [e] is the initial environment, [m] is the initial memory state, and [t] the trace of observable events performed during the execution. *)
with execinf_stmt:
function -> val -> env -> mem -> stmt -> traceinf -> Prop :=
| execinf_Scall:
forall f sp e m optid sig a bl vf vargs fd t,
eval_expr ge sp e m a vf ->
eval_exprlist ge sp e m bl vargs ->
Genv.find_funct ge vf = Some fd ->
funsig fd = sig ->
evalinf_funcall m fd vargs t ->
execinf_stmt f sp e m (Scall optid sig a bl) t
| execinf_Sifthenelse:
forall f sp e m a s1 s2 v b t,
eval_expr ge sp e m a v ->
Val.bool_of_val v b ->
execinf_stmt f sp e m (if b then s1 else s2) t ->
execinf_stmt f sp e m (Sifthenelse a s1 s2) t
| execinf_Sseq_1:
forall f sp e m t s1 s2,
execinf_stmt f sp e m s1 t ->
execinf_stmt f sp e m (Sseq s1 s2) t
| execinf_Sseq_2:
forall f sp e m t s1 t1 e1 m1 s2 t2,
exec_stmt f sp e m s1 t1 e1 m1 Out_normal ->
execinf_stmt f sp e1 m1 s2 t2 ->
t = t1 *** t2 ->
execinf_stmt f sp e m (Sseq s1 s2) t
| execinf_Sloop_body:
forall f sp e m s t,
execinf_stmt f sp e m s t ->
execinf_stmt f sp e m (Sloop s) t
| execinf_Sloop_loop:
forall f sp e m s t t1 e1 m1 t2,
exec_stmt f sp e m s t1 e1 m1 Out_normal ->
execinf_stmt f sp e1 m1 (Sloop s) t2 ->
t = t1 *** t2 ->
execinf_stmt f sp e m (Sloop s) t
| execinf_Sblock:
forall f sp e m s t,
execinf_stmt f sp e m s t ->
execinf_stmt f sp e m (Sblock s) t
| execinf_Stailcall:
forall f sp e m sig a bl vf vargs fd m' t,
eval_expr ge (Vptr sp Ptrofs.zero) e m a vf ->
eval_exprlist ge (Vptr sp Ptrofs.zero) e m bl vargs ->
Genv.find_funct ge vf = Some fd ->
funsig fd = sig ->
Mem.free m sp 0 f.(fn_stackspace) = Some m' ->
evalinf_funcall m' fd vargs t ->
execinf_stmt f (Vptr sp Ptrofs.zero) e m (Stailcall sig a bl) t.
End NATURALSEM.
(** Big-step execution of a whole program *)
Inductive bigstep_program_terminates (p: program): trace -> int -> Prop :=
| bigstep_program_terminates_intro:
forall b f m0 t m r,
let ge := Genv.globalenv p in
Genv.init_mem p = Some m0 ->
Genv.find_symbol ge p.(prog_main) = Some b ->
Genv.find_funct_ptr ge b = Some f ->
funsig f = signature_main ->
eval_funcall ge m0 f nil t m (Vint r) ->
bigstep_program_terminates p t r.
Inductive bigstep_program_diverges (p: program): traceinf -> Prop :=
| bigstep_program_diverges_intro:
forall b f m0 t,
let ge := Genv.globalenv p in
Genv.init_mem p = Some m0 ->
Genv.find_symbol ge p.(prog_main) = Some b ->
Genv.find_funct_ptr ge b = Some f ->
funsig f = signature_main ->
evalinf_funcall ge m0 f nil t ->
bigstep_program_diverges p t.
Definition bigstep_semantics (p: program) :=
Bigstep_semantics (bigstep_program_terminates p) (bigstep_program_diverges p).
(** ** Correctness of the big-step semantics with respect to the transition semantics *)
Section BIGSTEP_TO_TRANSITION.
Variable prog: program.
Let ge := Genv.globalenv prog.
Inductive outcome_state_match
(sp: val) (e: env) (m: mem) (f: function) (k: cont):
outcome -> state -> Prop :=
| osm_normal:
outcome_state_match sp e m f k
Out_normal
(State f Sskip k sp e m)
| osm_exit: forall n,
outcome_state_match sp e m f k
(Out_exit n)
(State f (Sexit n) k sp e m)
| osm_return_none: forall k',
call_cont k' = call_cont k ->
outcome_state_match sp e m f k
(Out_return None)
(State f (Sreturn None) k' sp e m)
| osm_return_some: forall k' a v,
call_cont k' = call_cont k ->
eval_expr ge sp e m a v ->
outcome_state_match sp e m f k
(Out_return (Some v))
(State f (Sreturn (Some a)) k' sp e m)
| osm_tail: forall v,
outcome_state_match sp e m f k
(Out_tailcall_return v)
(Returnstate v (call_cont k) m).
Remark is_call_cont_call_cont:
forall k, is_call_cont (call_cont k).
Remark call_cont_is_call_cont:
forall k, is_call_cont k -> call_cont k = k.

Lemma eval_funcall_steps:
forall m fd args t m' res,
eval_funcall ge m fd args t m' res ->
forall k,
is_call_cont k ->
star step ge (Callstate fd args k m)
t (Returnstate res k m').