Section PRESERVATION.
Variable prog tprog: program.
Hypothesis TRANSL: match_prog prog tprog.
Let ge := Genv.globalenv prog.
Let tge := Genv.globalenv tprog.
Proof (Genv.find_symbol_transf TRANSL).
Proof (Genv.find_funct_transf TRANSL).
Proof (Genv.find_funct_ptr_transf TRANSL).
Proof (Genv.senv_transf TRANSL).
(** Consider an execution of a call/move/nop/return sequence in the original code and the corresponding tailcall in the transformed code. The transition sequences are of the following form (left: original code, right: transformed code). [f] is the calling function and [fd] the called function. << State stk f (Icall instruction)       State stk' f' (Itailcall)  Callstate (frame::stk) fd args        Callstate stk' fd' args' .                                       . .                                       . .                                       . Returnstate (frame::stk) res          Returnstate stk' res'  State stk f (move/nop/return seq) . . . State stk f (return instr)  Returnstate stk res >> The simulation invariant must therefore account for two kinds of mismatches between the transition sequences: - The call stack of the original program can contain more frames than that of the transformed program (e.g. [frame] in the example above). - The regular states corresponding to executing the move/nop/return sequence must all correspond to the single [Returnstate stk' res'] state of the transformed program.  We first define the simulation invariant between call stacks. The first two cases are standard, but the third case corresponds to a frame that was eliminated by the transformation. *)
Inductive match_stackframes: list stackframe -> list stackframe -> Prop :=
| match_stackframes_nil:
match_stackframes nil nil
| match_stackframes_normal: forall stk stk' res sp pc rs rs' f,
match_stackframes stk stk' ->
regs_lessdef rs rs' ->
match_stackframes
(Stackframe res f (Vptr sp Ptrofs.zero) pc rs :: stk)
(Stackframe res (transf_function f) (Vptr sp Ptrofs.zero) pc rs' :: stk')
| match_stackframes_tail: forall stk stk' res sp pc rs f,
match_stackframes stk stk' ->
is_return_spec f pc res ->
f.(fn_stacksize) = 0 ->
match_stackframes
(Stackframe res f (Vptr sp Ptrofs.zero) pc rs :: stk)
stk'.
(** Here is the invariant relating two states.  The first three cases are standard.  Note the ``less defined than'' conditions over values and register states, and the corresponding ``extends'' relation over memory states. *)
Inductive match_states: state -> state -> Prop :=
| match_states_normal:
forall s sp pc rs m s' rs' m' f
(STACKS: match_stackframes s s')
(RLD: regs_lessdef rs rs')
(MLD: Mem.extends m m'),
match_states (State s f (Vptr sp Ptrofs.zero) pc rs m)
(State s' (transf_function f) (Vptr sp Ptrofs.zero) pc rs' m')
| match_states_call:
forall s f args m s' args' m',
match_stackframes s s' ->
Val.lessdef_list args args' ->
Mem.extends m m' ->
match_states (Callstate s f args m)
(Callstate s' (transf_fundef f) args' m')
| match_states_return:
forall s v m s' v' m',
match_stackframes s s' ->
Val.lessdef v v' ->
Mem.extends m m' ->
match_states (Returnstate s v m)
(Returnstate s' v' m')
| match_states_interm:
forall s sp pc rs m s' m' f r v'
(STACKS: match_stackframes s s')
(MLD: Mem.extends m m'),
is_return_spec f pc r ->
f.(fn_stacksize) = 0 ->
Val.lessdef (rs#r) v' ->
match_states (State s f (Vptr sp Ptrofs.zero) pc rs m)
(Returnstate s' v' m').
(** The last case of [match_states] corresponds to the execution of a move/nop/return sequence in the original code that was eliminated by the transformation: << State stk f (move/nop/return seq)  ~~  Returnstate stk' res' . . . State stk f (return instr)         ~~  Returnstate stk' res' >> To preserve non-terminating behaviors, we need to make sure that the execution of this sequence in the original code cannot diverge.  For this, we introduce the following complicated measure over states, which will decrease strictly whenever the original code makes a transition but the transformed code does not. *)
Definition measure (st: state) : nat :=
match st with
| State s f sp pc rs m => (List.length s * (niter + 2) + return_measure f.(fn_code) pc + 1)%nat
| Callstate s f args m => 0%nat
| Returnstate s v m => (List.length s * (niter + 2))%nat
end.
Ltac TransfInstr :=
match goal with
| H: (PTree.get _ (fn_code _) = _) |- _ =>
destruct (transf_instr_lookup _ _ _ H) as [i' [TINSTR TSPEC]]; inv TSPEC
end.
Ltac EliminatedInstr :=
match goal with
| H: (is_return_spec _ _ _) |- _ => inv H; try congruence
| _ => idtac
end.
(** The proof of semantic preservation, then, is a simulation diagram of the ``option'' kind. *)

Lemma transf_step_correct:
forall s1 t s2, step ge s1 t s2 ->
forall s1' (MS: match_states s1 s1'),
(exists s2', step tge s1' t s2' /\ match_states s2 s2')
\/ (measure s2 < measure s1 /\ t = E0 /\ match_states s2 s1')%nat.