Compilation to the Modern SECD abstract machine and its proof of correctness.
Require Omega.
Require Import List.
Require Import Sequences.
Source language
Syntax with de Bruijn indices.
Definition const:
Type :=
nat.
Inductive term:
Type :=
|
Var:
nat ->
term (* de Bruijn index *)
|
Const:
const ->
term
|
Fun:
term ->
term
|
Let:
term ->
term ->
term
|
App:
term ->
term ->
term.
Big-step semantics with environments and closures.
A value is either an integer or a closure of a term by an environment.
Inductive value:
Type :=
|
Int:
const ->
value
|
Clos:
term ->
list value ->
value.
An environment is a list of values. Element number N of the list is
the value of variable having de Bruijn index N.
Definition env :=
list value.
Looking up the N-th element of an environment.
Fixpoint lookup {
A:
Type} (
n:
nat) (
l:
list A) {
struct l}:
option A :=
match l,
n with
|
nil,
_ =>
None
|
x ::
l',
O =>
Some x
|
x ::
l',
S n' =>
lookup n'
l'
end.
eval e a v means "in environment e, term a evaluates to value v".
Inductive eval:
env ->
term ->
value ->
Prop :=
|
eval_Var:
forall e n v,
lookup n e =
Some v ->
eval e (
Var n)
v
|
eval_Const:
forall e i,
eval e (
Const i) (
Int i)
|
eval_Fun:
forall e a,
eval e (
Fun a) (
Clos a e)
|
eval_Let:
forall e a b va vb,
eval e a va ->
eval (
va ::
e)
b vb ->
eval e (
Let a b)
vb
|
eval_App:
forall e a b ca ea vb v,
eval e a (
Clos ca ea) ->
eval e b vb ->
eval (
vb ::
ea)
ca v ->
eval e (
App a b)
v.
evalinf e a v means "in environment e, term a diverges".
CoInductive evalinf:
env ->
term ->
Prop :=
|
evalinf_Let_l:
forall e a b,
evalinf e a ->
evalinf e (
Let a b)
|
evalinf_Let_r:
forall e a b va,
eval e a va ->
evalinf (
va ::
e)
b ->
evalinf e (
Let a b)
|
evalinf_App_l:
forall e a b,
evalinf e a ->
evalinf e (
App a b)
|
evalinf_App_r:
forall e a b v,
eval e a v ->
evalinf e b ->
evalinf e (
App a b)
|
evalinf_App_f:
forall e a b ca ea vb,
eval e a (
Clos ca ea) ->
eval e b vb ->
evalinf (
vb ::
ea)
ca ->
evalinf e (
App a b).
Abstract machine: the Modern SECD
Instruction set.
Inductive instr:
Type :=
|
IAccess:
nat ->
instr
|
IConst:
const ->
instr
|
IClosure:
list instr ->
instr
|
IApp:
instr
|
ITailApp:
instr
|
ILet:
instr
|
IEndLet:
instr
|
IReturn:
instr.
Definition code :=
list instr.
Machine values and environments.
Inductive mvalue:
Type :=
|
MInt:
const ->
mvalue
|
MClos:
code ->
list mvalue ->
mvalue.
Definition menv :=
list mvalue.
Stacks.
Inductive slot:
Type :=
|
Value:
mvalue ->
slot (* a value on the stack *)
|
Return:
code ->
menv ->
slot.
(* a return frame *)
Definition stack :=
list slot.
A state of the machine is a triple of a piece of code, a machine environment,
and a stack.
Definition state :
Type := (
code *
menv *
stack)%
type.
One transition of the machine.
Inductive transition:
state ->
state ->
Prop :=
|
trans_IAccess:
forall n k e s v,
lookup n e =
Some v ->
transition (
IAccess n ::
k,
e,
s)
(
k,
e,
Value v ::
s)
|
trans_IConst:
forall n k e s,
transition (
IConst n ::
k,
e,
s)
(
k,
e,
Value (
MInt n) ::
s)
|
trans_IClosure:
forall c k e s,
transition (
IClosure c ::
k,
e,
s)
(
k,
e,
Value (
MClos c e) ::
s)
|
trans_IApp:
forall k e v k'
e'
s,
transition (
IApp ::
k,
e,
Value v ::
Value (
MClos k'
e') ::
s)
(
k',
v ::
e',
Return k e ::
s)
|
trans_ITailApp:
forall k e v k'
e'
s,
transition (
ITailApp ::
k,
e,
Value v ::
Value (
MClos k'
e') ::
s)
(
k',
v ::
e',
s)
|
trans_ILet:
forall k e v s,
transition (
ILet ::
k,
e,
Value v ::
s)
(
k,
v ::
e,
s)
|
trans_IEndLet:
forall k e v s,
transition (
IEndLet ::
k,
v ::
e,
s)
(
k,
e,
s)
|
trans_IReturn:
forall k e v k'
e'
s,
transition (
IReturn ::
k,
e,
Value v ::
Return k'
e' ::
s)
(
k',
e',
Value v ::
s).
Executing code with the machine.
Definition mach_terminates (
k:
code) (
v:
mvalue) :
Prop :=
star transition (
k,
nil,
nil) (
nil,
nil,
Value v ::
nil).
Definition mach_diverges (
k:
code) :
Prop :=
infseq transition (
k,
nil,
nil).
Compilation and its correctness.
The compilation scheme.
compile a k prepends the code for term a to the code k.
The code for a evaluates a and deposits its value on top of the stack.
Fixpoint compile (
a:
term) (
k:
code) :
code :=
match a with
|
Var n =>
IAccess n ::
k
|
Const i =>
IConst i ::
k
|
Fun b =>
IClosure (
compile b (
IReturn ::
nil)) ::
k
|
Let b c =>
compile b (
ILet ::
compile c (
IEndLet ::
k))
|
App b c =>
compile b (
compile c (
IApp ::
k))
end.
Extension to values and environments
Fixpoint compile_value (
v:
value) :
mvalue :=
match v with
|
Int n =>
MInt n
|
Clos a e =>
let fix compenv (
e:
env) :
menv :=
match e with
|
nil =>
nil
|
v ::
e' =>
compile_value v ::
compenv e'
end in
MClos (
compile a (
IReturn ::
nil)) (
compenv e)
end.
Fixpoint compile_env (
e:
env) :
menv :=
match e with
|
nil =>
nil
|
v ::
e' =>
compile_value v ::
compile_env e'
end.
Lemma lookup_compile:
forall e n v,
lookup n e =
Some v ->
lookup n (
compile_env e) =
Some (
compile_value v).
Proof.
induction e; destruct n; simpl; intros; auto; congruence.
Qed.
Compiler correctness for terminating programs.
Lemma compile_eval:
forall e a v,
eval e a v ->
forall k s,
plus transition (
compile a k,
compile_env e,
s)
(
k,
compile_env e,
Value (
compile_value v) ::
s).
Proof.
Theorem compile_terminates:
forall a v,
eval nil a v ->
mach_terminates (
compile a nil) (
compile_value v).
Proof.
Compiler correctness for diverging programs.
Inductive diverging_state :
state ->
Prop :=
|
diverging_state_intro:
forall e a k s,
evalinf e a ->
diverging_state (
compile a k,
compile_env e,
s).
Lemma diverging_states_productive:
forall S,
diverging_state S ->
exists S',
plus transition S S' /\
diverging_state S'.
Proof.
Theorem compile_diverges:
forall a,
evalinf nil a ->
mach_diverges (
compile a nil).
Proof.
Adding tail call optimization
Module WithTailCalls.
The revised compilation scheme.
-
compile a k prepends the code for term a to the code k.
The code for a evaluates a and deposits its value on top of the stack.
-
tailcompile a produces code that evaluates a and returns the value of a
to the caller.
Note mutual recursion:
tailcompile uses
compile for sub-terms that are
not in tail position;
compile uses
tailcompile for the bodies of
Fun
abstractions.
Fixpoint compile (
a:
term) (
k:
code) :
code :=
match a with
|
Var n =>
IAccess n ::
k
|
Const i =>
IConst i ::
k
|
Fun b =>
IClosure (
tailcompile b) ::
k
|
Let b c =>
compile b (
ILet ::
compile c (
IEndLet ::
k))
|
App b c =>
compile b (
compile c (
IApp ::
k))
end
with tailcompile (
a:
term) :
code :=
match a with
|
Var n =>
IAccess n ::
IReturn ::
nil
|
Const i =>
IConst i ::
IReturn ::
nil
|
Fun b =>
IClosure (
tailcompile b) ::
IReturn ::
nil
|
Let b c =>
compile b (
ILet ::
tailcompile c)
|
App b c =>
compile b (
compile c (
ITailApp ::
nil))
end.
Extension to values and environments.
Fixpoint compile_value (
v:
value) :
mvalue :=
match v with
|
Int n =>
MInt n
|
Clos a e =>
let fix compenv (
e:
env) :
menv :=
match e with
|
nil =>
nil
|
v ::
e' =>
compile_value v ::
compenv e'
end in
MClos (
tailcompile a) (
compenv e)
end.
Fixpoint compile_env (
e:
env) :
menv :=
match e with
|
nil =>
nil
|
v ::
e' =>
compile_value v ::
compile_env e'
end.
Lemma lookup_compile:
forall e n v,
lookup n e =
Some v ->
lookup n (
compile_env e) =
Some (
compile_value v).
Proof.
induction e; destruct n; simpl; intros; auto; congruence.
Qed.
Compiler correctness for terminating programs.
Lemma compile_eval_gen:
forall e a v,
eval e a v ->
(
forall k s,
plus transition (
compile a k,
compile_env e,
s)
(
k,
compile_env e,
Value (
compile_value v) ::
s))
/\
(
forall s k e',
plus transition (
tailcompile a,
compile_env e,
Return k e' ::
s)
(
k,
e',
Value (
compile_value v) ::
s)).
Proof.
induction 1;
split;
simpl;
intros.
- (* Var, non-tail *)
apply plus_one.
constructor.
apply lookup_compile;
auto.
- (* Var, tail *)
eapply plus_left.
constructor.
apply lookup_compile;
eauto.
apply star_one.
constructor.
- (* Const, non-tail *)
apply plus_one.
constructor.
- (* Const, tail *)
eapply plus_left.
constructor.
apply star_one.
constructor.
- (* Closure, non-tail *)
apply plus_one.
constructor.
- (* Const, tail *)
eapply plus_left.
constructor.
apply star_one.
constructor.
- (* Let, non-tail *)
eapply plus_trans.
eapply IHeval1.
eapply plus_left.
constructor.
eapply star_trans.
apply plus_star.
eapply IHeval2.
apply star_one.
constructor.
- (* Let, tail *)
eapply plus_trans.
eapply IHeval1.
eapply plus_left.
constructor.
apply plus_star.
eapply IHeval2.
- (* App, non-tail *)
eapply plus_trans.
eapply IHeval1.
eapply plus_trans.
eapply IHeval2.
eapply plus_left.
change (
compile_value (
Clos ca ea))
with (
MClos (
tailcompile ca) (
compile_env ea)).
constructor.
apply plus_star.
apply IHeval3.
- (* App, tail *)
eapply plus_trans.
eapply IHeval1.
eapply plus_trans.
eapply IHeval2.
eapply plus_left.
change (
compile_value (
Clos ca ea))
with (
MClos (
tailcompile ca) (
compile_env ea)).
constructor.
apply plus_star.
apply IHeval3.
Qed.
Lemma compile_eval:
forall e a v k s,
eval e a v ->
plus transition (
compile a k,
compile_env e,
s)
(
k,
compile_env e,
Value (
compile_value v) ::
s).
Proof.
Lemma tailcompile_eval:
forall e a v s k e',
eval e a v ->
plus transition (
tailcompile a,
compile_env e,
Return k e' ::
s)
(
k,
e',
Value (
compile_value v) ::
s).
Proof.
Theorem compile_terminates:
forall a v,
eval nil a v ->
mach_terminates (
compile a nil) (
compile_value v).
Proof.
Compiler correctness for diverging programs.
Inductive diverging_state :
state ->
Prop :=
|
diverging_state_nontail:
forall e a k s,
evalinf e a ->
diverging_state (
compile a k,
compile_env e,
s)
|
diverging_state_tail:
forall e a s,
evalinf e a ->
diverging_state (
tailcompile a,
compile_env e,
s).
Lemma diverging_states_productive:
forall S,
diverging_state S ->
exists S',
plus transition S S' /\
diverging_state S'.
Proof.
Theorem compile_diverges:
forall a,
evalinf nil a ->
mach_diverges (
compile a nil).
Proof.
End WithTailCalls.