Roberto M Amadio

Yann Régis-Gianas

Certifying and reasoning about cost annotations of functional programs *

We present a so-called labelling method to insert cost annotations in a higher-order functional program, to certify their correctness with respect to a standard, typable compilation chain to assembly code including safe memory management, and to reason about them in a higher-order Hoare logic.

Introduction

In previous work [START_REF] Amadio | Certifying cost annotations in compilers[END_REF][START_REF] Ayache | Certifying and reasoning on cost annotations in C programs[END_REF], we have discussed the problem of building a C compiler which can lift in a provably correct way pieces of information on the execution cost of the object code to cost annotations on the source code. To this end, we have introduced a so called labelling approach and presented its application to a prototype compiler written in OCaml from a large fragment of the C language to the assembly languages of Mips and 8051, a 32 bits and 8 bits processor, respectively.

In the following, we are interested in extending the approach to (higher-order) functional languages. On this issue, a common belief is well summarized by the following epigram by A. Perlis [START_REF] Perlis | Epigrams on programming[END_REF]: A Lisp programmer knows the value of everything, but the cost of nothing. However, we shall show that, with some ingenuity, the methodology developed for the C language can be lifted to functional languages.

A standard compilation chain

Specifically, we shall focus on a rather standard compilation chain from a call-by-value λcalculus to a register transfer level (RTL) language. Similar compilation chains have been explored from a formal viewpoint by Morrisett et al. [START_REF] Morrisett | From system F to typed assembly language[END_REF] (with an emphasis on typing but with no simulation proofs) and by Chlipala [START_REF] Chlipala | A verified compiler for an impure functional language[END_REF] (for type-free languages but with machine certified simulation proofs).

λ M λ ℓ I o o Ccps G G er Ù Ù λ ℓ cps Cvn E E er λ ℓ cps,vn R k k Ccc G G er λ ℓ cc,vn C h G G er λ ℓ h,vn er λ L s s Ccps G G Ccps G G λ cps Cvn E E λ cps,vn R k k Ccc G G λ cc,vn C h G G λ h,vn
Table 1: The compilation chain with its labelling and instrumentation.

The compilation chain is described in the lower part of Table 1. Starting from a standard call-by-value λ-calculus with pairs, one performs first a CPS translation, then a transformation that gives names to values, followed by a closure conversion, and a hoisting transformation. All languages considered are subsets of the initial one though their evaluation mechanism is refined along the way. In particular, one moves from an ordinary substitution to a specialized one where variables can only be replaced by other variables. One advantage of this approach, as already noted for instance by Fradet and Le Métayer [START_REF] Fradet | Compilation of functional languages by program transformation[END_REF], is to have a homogeneous notation that makes correctness proofs simpler.

Notable differences with respect to Chlipala's compilation chain [START_REF] Chlipala | A verified compiler for an impure functional language[END_REF] is a different choice of the intermediate languages and the fact that we rely on a small-step operational semantics. We also diverge from Chlipala [START_REF] Chlipala | A verified compiler for an impure functional language[END_REF] in that our proofs, following the usual mathematical tradition, are written to explain to a human why a certain formula is valid rather than to provide a machine with a compact witness of the validity of the formula.

The final language of this compilation chain can be directly mapped to a RTL language: functions correspond to assembly level routines and the functions' bodies correspond to sequences of assignments on pseudo-registers ended by a tail recursive call.

The labelling approach to cost certification

While the extensional properties of the compilation chain have been well studied, we are not aware of previous work focusing on more intensional properties relating to the way the compilation preserves the complexity of the programs. Specifically, in the following we will apply to this compilation chain the 'labelling approach' to building certified cost annotations. In a nutshell the approach consists in identifying, by means of labels, points in the source program whose cost is constant and then determining the value of the constants by propagating the labels along the compilation chain and analysing small pieces of object code with respect to a target architecture.

Technically the approach is decomposed in several steps. First, for each language considered in the compilation chain, we define an extended labelled language and an extended operational semantics (upper part of Table 1). The labels are used to mark certain points of the control. The semantics makes sure that, whenever we cross a labelled control point, a labelled and observable transition is produced.

Second, for each labelled language there is an obvious function er erasing all labels and producing a program in the corresponding unlabelled language. The compilation functions are extended from the unlabelled to the labelled language so that they commute with the respective erasure functions. Moreover, the simulation properties of the compilation functions are lifted from the unlabelled to the labelled languages and transition systems.

Third, assume a labelling L of the source language is a right inverse of the respective erasure function. The evaluation of a labelled source program produces both a value and a sequence of labels, written Λ, which intuitively stands for the sequence of labels crossed during the program's execution. The central question we are interested in is whether there is a way of labelling the source programs so that the sequence Λ is a sound and possibly precise representation of the execution cost of the program.

To answer this question, we observe that the object code is some kind of RTL code and that its control flow can be easily represented as a control flow graph. The fact that we have to prove the soundness of the compilation function means that we have plenty of information on the way the control flows in the compiled code, in particular as far as procedure calls and returns are concerned. These pieces of information allow to build a rather accurate representation of the control flow of the compiled code at run time.

The idea is then to perform some simple checks on the control flow graph. The main check consists in verifying that all 'loops' go through a labelled node. If this is the case then we can associate a 'cost' with every label which over-approximates the actual cost of running a sequence of instructions. An optional check amounts to verify that all paths starting from a label have the same abstract cost. If this check is successful then we can conclude that the cost annotations are 'precise' in an abstract sense (and possibly concrete too, depending on the processor considered).

In our previous work [START_REF] Amadio | Certifying cost annotations in compilers[END_REF][START_REF] Ayache | Certifying and reasoning on cost annotations in C programs[END_REF], we have showed that it is possible to produce a sound and precise (in an abstract sense) labelling for a large class of C programs with respect to a moderately optimising compiler. In the following we show that a similar result can be obtained for a higher-order functional language with respect to the standard compilation chain described above. Specifically we show that there is a simple labelling of the source program that guarantees that the labelling of the generated object code is sound and precise. The labelling of the source program can be informally described as follows: it associates a distinct label with every abstraction and with every application which is not 'immediately surrounded' by an abstraction.

In this paper our analysis will stop at the level of an abstract RTL language, however our previously quoted work [START_REF] Amadio | Certifying cost annotations in compilers[END_REF][START_REF] Ayache | Certifying and reasoning on cost annotations in C programs[END_REF] shows that the approach extends to the back-end of a typical moderately optimising compiler including, e.g., dead-code elimination and register allocation. Concerning the source language, preliminary experiments suggest that the approach scales to a larger functional language such as the one considered in Chlipala's Coq development [START_REF] Chlipala | A verified compiler for an impure functional language[END_REF] including fixpoints, sums, exceptions, and side effects. Let us also mention that our approach has been implemented for a simpler compilation chain that bypasses the CPS translation. In this case, the function calls are not necessarily tail-recursive and the compiler generates a Cminor program which, roughly speaking, is a type-free, stack aware fragment of C defined in the Compcert project [START_REF] Leroy | Formal verification of a realistic compiler[END_REF].

Reasoning about the certified cost annotations

If the check described above succeeds every label has a cost which in general can be taken as an element of a 'cost' monoid. Then an instrumentation of the source labelled language is a monadic transformation I (left upper part of Table 1) in the sense of Gurr's PhD thesis [START_REF] Gurr | Semantic frameworks for complexity[END_REF] that replaces labels with the associated elements of the cost monoid. Following this monadic transformation we are back into the source language (possibly enriched with a 'cost monoid' such as integers with addition). As a result, the source program is instrumented so as to monitor its execution cost with respect to the associated object code. In the end, general logics developed to reason about functional programs such as the higher-order Hoare logic codeveloped by one of the authors [START_REF] Régis-Gianas | A Hoare logic for call-by-value functional programs[END_REF] can be employed to reason about the concrete complexity of source programs by proving properties on their instrumented versions (see Table 11 for an example of a source program with complexity assertions).

Accounting for the cost of memory management

In a realistic implementation of a functional programming language, the runtime environment usually includes a garbage collector. In spite of considerable progress in real-time garbage collectors (see, e.g., the work of Bacon et al. [START_REF] Bacon | A real-time garbage collector with low overhead and consistent utilization[END_REF]), it seems to us that this approach does not offer yet a viable path to a certified and usable WCET prediction of the running time of functional programs. Instead, the approach we shall adopt, following the seminal work of Tofte et al. [START_REF] Tofte | Region-based memory management[END_REF], is to enrich the last calculus of the compilation chain described in Table 1, [START_REF] Aiken | Better static memory management: improving region-based analysis of higherorder languages[END_REF] with a notion of memory region, (2) with operations to allocate and dispose memory regions, and (3) with a type and effect system that guarantees the safety of the dispose operation. This allows to further extend to the right with one more commuting square the compilation chain mentioned above and then to include the cost of safe memory management in our analysis. Actually, because effects are intertwined with types, what we shall actually do, following the work of Morrisett et al. [START_REF] Morrisett | From system F to typed assembly language[END_REF], is to extend a typed version of the compilation chain.

Related work

There is a long tradition starting from the work of Wegbreit [START_REF] Wegbreit | Mechanical Program Analysis[END_REF] which reduces the complexity analysis of first-order functional programs to the solution of finite difference equations. Much less is known about higher-order functional programs. Most previous work on building cost annotations for higher-order functional programs we are aware of does not take formally into account the compilation process. For instance, in an early work D. Sands [START_REF] Sands | Complexity analysis for a lazy higher-order language[END_REF] proposes an instrumentation of call-by-value λ-calculus in order to describe its execution cost. However the notion of cost adopted is essentially the number of function calls in the source code. In a standard implementation such as the one considered in this work, different function calls may have different costs and moreover there are 'hidden' function calls which are not immediately apparent in the source code.

A more recent work by Bonenfant et al. [START_REF] Bonenfant | Worst-case execution times for a purely functional language[END_REF] addresses the problem of determining the worst case execution time of a specialised functional language called Hume. The compilation chain considered consists in first compiling Hume to the code of an intermediate abstract machine, then to C, and finally to generate the assembly code of the Resenas M32C/85 processor using standard C compilers. Then for each instruction of the abstract machine, one computes an upper bound on the worst-case execution time (WCET) of the instruction relying on a wellknown aiT tool [START_REF][END_REF] that uses abstract interpretation to determine the WCET of sequences of binary instructions.

While we share common motivations with this work, we differ significantly in the technical approach. First, the Hume approach follows a tradition of compiling functional programs to the instructions of an abstract machine which is then implemented in a C like language. In contrast, we have considered a compilation chain that brings a functional program directly to RTL form. Then the back-end of a C like compiler is used to generate binary instructions. Second, the cited work [START_REF] Bonenfant | Worst-case execution times for a purely functional language[END_REF] does not address at all the proof of correctness of the cost annotations; this is left for future work. Third, the problem of producing synthetic cost statements starting from the cost estimations of the abstract instructions of the Hume machine is not considered. Fourth, the cost of dynamic memory management, which is crucial in higher-order functional programs, is not addressed at all. Fifth, the granularity of the cost annotations is fixed in Hume [START_REF] Bonenfant | Worst-case execution times for a purely functional language[END_REF] (the instructions of the Hume abstract machine) while it can vary in our approach.

We also share with the Hume approach one limitation. The precision of our analyses depends on the possibility of having accurate predictions of the execution time of relatively short sequences of binary code on a given processor. Unfortunately, as of today, user interfaces for WCET systems such as the aiT tool mentioned above or Chronos [START_REF] Li | Chronos: A timing analyzer for embedded software[END_REF] do not support modular reasoning on execution times and therefore experimental work focuses on processors with simple and predictable architectures. In a related direction, another potential loss of precision comes from the introduction of aggressive optimisations in the back-end of the compiler such as loop transformations. An ongoing work by Tranquilli [START_REF] Tranquilli | Indexed labelling for loop iteration dependent costs[END_REF] addresses this issue by introducing a refinement of the labelling approach.

Paper organisation

In the following, section 2 describes the certification of the cost-annotations, section 3 a method to reason about the cost annotations, section 4 the typing of the compilation chain, and section 5 an extension of the compilation chain to account for safe memory deallocation. Proofs are available in the appendix A.

The compilation chain: commutation and simulation

We describe the intermediate languages and the compilation functions from an ordinary λcalculus to a hoisted, value named λ-calculus. For each step we check that: (i) the compilation function commutes with the function that erases labels and (ii) the object code simulates the source code.

Conventions

The reader is assumed to be acquainted with the type-free and typed λ-calculus, its evaluation strategies, and its continuation passing style translations [START_REF] Wand | Continuation-based program transformation strategies[END_REF]. In the following calculi, all terms are manipulated up to α-renaming of bound names. We denote with ≡ syntactic identity up to α-renaming. Whenever a reduction rule is applied, it is assumed that terms have been renamed so that all binders use distinct variables and these variables are distinct from the free ones. With this assumption, we can omit obvious side conditions on binders and free variables. Similar conventions are applied when reasoning about a substitution, say [T /x]T ′ , of a term T for a variable x in a term T ′ . We denote with fv(T ) the set of variables occurring free in a term T .

Let C, C 1 , C 2 , . . . be one hole contexts and T a term. Then C[T ] is the term resulting from the replacement in the context C of the hole by the term T and C 1 [C 2 ] is the one hole context resulting from the replacement in the context C 1 of the hole by the context C 2 .

For each calculus, we assume a syntactic category id of identifiers with generic elements x, y, . . . and a syntactic category ℓ of labels with generic elements ℓ, ℓ 1 , . . . For each calculus, we specify the syntactic categories and the reduction rules. For the sake of clarity, the metavariables of these syntactic categories are sometimes shared between several calculus: the context is always sufficiently precise to determine to which syntax definitions we refer. We let α range over labels and the empty word ǫ. We write M α -→ N if M rewrites to N with a transition labelled by α. We abbreviate M ǫ -→ N with M → N . We write * → for the reflexive and transitive closure of →. We also define

M α ⇒ N as M * → N if α = ǫ and as M * → α -→ * → N otherwise.
Given a term M in one of the labelled languages we write

M ⇓ Λ N if M α 1 -→ • • • αn --→ N , Λ = α 1 • • • α n ,
and N cannot reduce (in general this does not imply that N is a value). We write M ⇓ Λ for ∃ N M ⇓ Λ N . Also, if the term M is unlabelled, Λ is always the empty word and we abbreviate M ⇓ ǫ N with M ⇓ N .

We shall write X + (resp. X * ) for a non-empty (possibly empty) finite sequence X 1 , . . . , X n of symbols. By extension, λx + .M stands for λx 1 . . .

x n .M , [V + /x + ]M stands for [V 1 /x 1 , . . . , V n /x n ]M , and let (x = V ) + in M stands for let x 1 = V 1 in • • • let x n = V n in M .

The source language

Table 2 introduces a type-free, left-to-right call-by-value λ-calculus. The calculus includes letdefinitions and polyadic abstraction and tupling with the related application and projection operators. Any term M can be pre-labelled by writing ℓ > M or post-labelled by writing M > ℓ. In the pre-labelling, the label ℓ is emitted immediately while in the post-labelling it is emitted after M has reduced to a value. It is tempting to reduce the post-labelling to the pre-labelling by writing M > ℓ as @(λx.ℓ > x, M ), however the second notation introduces an additional abstraction and a related reduction step which is not actually present in the original code. Roughly speaking, every λ-abstraction is a potential starting point for a loop in the control-flow graph. Thus, we will need the body of every λ-abstraction to be pre-labelled so as to maintain the invariant that all loops go through a labelled node in the control-flow graph. As the CPS translation introduces new λ-abstractions that are not present in the source code but correspond to the image of some applications, we will also need to post-label these particular applications so that the freshly introduced λ-abstraction can be assigned a label.

Table 2 also introduces an erasure function er from the λ ℓ -calculus to the λ-calculus. This function simply traverses the term and erases all pre and post labellings. Similar definitions arise in the following calculi of the compilation chain and are omitted.

Compilation to CPS form

Table 3 introduces a fragment of the λ ℓ -calculus described in Table 2 and a related CPS translation. To avoid all ambiguity, let us assume that (V 1 , . . . , V n ) | K is translated according to the case for values, but note that if we follow the general case for tuples we obtain the same result. We recall that in a CPS translation each function takes its evaluation context as a fresh additional parameter (see, e.g., the work of Wand [START_REF] Wand | Continuation-based program transformation strategies[END_REF], for an elaboration of this idea). The results of the evaluation of subterms (of tuples and of applications) are also named using fresh parameters x 0 , . . . , x n . The initial evaluation context is defined relatively to a fresh variable named 'halt '. Then the evaluation context is always trivial. Notice that the reduction rules are essentially those of the λ ℓ -calculus modulo the fact that we drop the rule to reduce V > ℓ since post-labelling does not occur in CPS terms and the fact that we optimize the rule for the projection to guarantee that CPS terms are closed under reduction. For instance, the term

let x = π 1 (V 1 , V 2 ) in M reduces directly to [V 1 /x]M rather than going Syntax V ::= id | | λid + .M | | (V * ) (values) M ::= V | | @(M, M + ) | | let id = M in M | | (M * ) | | πi(M ) | | ℓ > M | | M > ℓ (terms) E ::= [ ] | | @(V * , E, M * ) | | let id = E in M | | (V * , E, M * ) | | πi(E) | | E > ℓ (eval. cxts.)
Reduction Rules

E[@(λx1 . . . xn.M, V1, . . . , Vn)] → E[[V1/x1, . . . , Vn/xn]M ] E[let x = V in M ] → E[[V /x]M ] E[πi(V1, . . . , Vn)] → E[Vi] 1 ≤ i ≤ n E[ℓ > M ] ℓ -→ E[M ] E[V > ℓ] ℓ -→ E[V ]
Label erasure (selected equations) We study next the properties enjoyed by the CPS translation. In general, the commutation of the compilation function with the erasure function only holds up to call-by-value η-conversion, namely λx.@(V, x) = η V if x / ∈ fv(V ). This is due to the fact that post-labelling introduces an η-expansion of the continuation if and only if the continuation is a variable. To cope with this problem, we introduce next the notion of well-labelled term. We will see later (section 3.1) that terms generated by the initial labelling are well-labelled.

er (ℓ > M ) = er (M > ℓ) = er (M )
Definition 1 (well-labelling) We define two predicates W i , i = 0, 1 on the terms of the λ ℓ -calculus as the least sets such that W 1 is contained in W 0 and the following conditions hold:

x ∈ W1 M ∈ W0 M > ℓ ∈ W0 M ∈ W1 λx + .M ∈ W1 M ∈ Wi i ∈ {0, 1} ℓ > M ∈ Wi N ∈ W0, M ∈ Wi i ∈ {0, 1} let x = N in M ∈ Wi Mi ∈ W0 i = 1, . . . , n @(M1, . . . , Mn) ∈ W1 Mi ∈ W0 i = 1, . . . , n (M1, . . . , Mn) ∈ W1 M ∈ W0 πi(M ) ∈ W1 .
The intuition is that we want to avoid the situation where a post-labelling receives as continuation the continuation variable generated by the translation of a λ-abstraction. To that end, we make sure that post-labelling is only applied to terms M ∈ W 0 , that is, terms that are not the immediate body of a λ-abstraction (which are in W 1 ).

Example 2 (labelling and commutation) Let M ≡ λx.(@(x, x) > ℓ). Then M / ∈ W 0 because the rule for abstraction requires @(x, x) > ℓ ∈ W 1 while we can only show @(x, x) > ℓ ∈ W 0 . Notice that we have: er(C cps (M )) ≡ @(halt , λx, k.@(x, x, λx.@(k, x))) C cps (er (M )) ≡ @(halt , λx, k.@(x, x, k)) .

So, for M , the commutation of the CPS translation and the erasure function only holds up to η. Proposition 3 (CPS commutation) Let M ∈ W 0 be a term of the λ ℓ -calculus (Table 2). Then: er (C cps (M )) ≡ C cps (er (M )).

The proof of the CPS simulation is non-trivial but rather standard since Plotkin's seminal work [START_REF] Plotkin | Call-by-name, call-by-value and the lambda-calculus[END_REF]. The general idea is that the CPS translation pre-computes many 'administrative' reductions so that the translation of a term, say E[@(λx.M, V )] is a term of the shape @(ψ(λx.M ), ψ(V ), K E ) for a suitable continuation K E depending on the evaluation context E.

Proposition 4 (CPS simulation) Let M be a term of the λ ℓ -calculus. If M α -→ N then C cps (M ) α ⇒ C cps (N ).
We illustrate this result on the following example.

Example 5 (CPS) Let M ≡ @(λx.@(x, @(x, x)), I), where I ≡ λx.x. Then C cps (M ) ≡ @(λx, k.@(x, x, λy.@(x, y, k)), I ′ , H) where: I ′ ≡ λx, k.@(k, x) and H ≡ λx.@(halt , x). The term M is simulated by C cps (M ) as follows:

M → @(I, @(I, I)) → @(I, I) → I C cps (M ) → @(I ′ , I ′ , λy.@(I ′ , y, H)) → + @(I ′ , I ′ , H) → + @(halt , I ′ ) .

Transformation in value named CPS form

Table 4 introduces a value named λ-calculus in CPS form: λ ℓ cps,vn . In the ordinary λ-calculus, the application of a λ-abstraction to an argument (which is a value) may duplicate the argument as in: @(λx.M, V ) → [V /x]M . In the value named λ-calculus, all values are named and when we apply the name of a λ-abstraction to the name of a value we create a new copy of the body of the function and replace its formal parameter name with the name of the argument as in:

let y = V in let f = λx.M in @(f, y) → let y = V in let f = λx.M in [y/x]M .
We also remark that in the value named λ-calculus the evaluation contexts are a sequence of let definitions associating values to names. Thus, apart for the fact that the values are not necessarily closed, the evaluation contexts are similar to the environments of abstract machines for functional languages (see, e.g., [START_REF] Curien | An abstract framework for environment machines[END_REF]).

Table 5 defines the compilation into value named form along with a readback translation. (Only the case for the local binding of values is interesting.) The latter is useful to state the simulation property. Indeed, it is not true that if

M → M ′ in λ ℓ cps then C vn (M ) * → C vn (M ′ ) in λ ℓ
cps,vn . For instance, consider M ≡ (λx.xx)I where I ≡ (λy.y). Then M → II but C vn (M ) does not reduce to C vn (II) but rather to a term where the 'sharing' of the duplicated value I is explicitly represented. N ≡ @(λx, k.@(x, x, λy.@(x, y, k)), I ′ , H))

Syntax CPS terms

V ::= id | | λid + .M | | (V * ) (values) M ::= @(V, V + ) | | let id = πi(V ) in M | | ℓ > M (CPS terms) K ::= id | | λid.M (
M → [Vi/x]M 1 ≤ i ≤ n ℓ > M ℓ -→ M CPS translation ψ(x) = x ψ(λx + .M ) = λx + , k.(M | k) ψ((V1, . . . , Vn)) = (ψ(V1), . . . , ψ(Vn)) V | k = @(k, ψ(V )) V | (λx.M ) = [ψ(V )/x]M @(M0, . . . , Mn) | K = M0 | λx0. . . . (Mn | λxn.@(x0, . . . , xn, K)) let x = M1 in M2 | K = M1 | λx.(M2 | K) (M1, . . . , Mn) | K = M1 | λx1. . . . (Mn | λxn.(x1, . . . , xn) | K ) πi(M ) | K = M | λx.let y = πi(x) in y | K (ℓ > M ) | K = ℓ > (M | K) (M > ℓ) | K = M | (λx.ℓ > (x | K)) Ccps (M ) = M | λx.@(halt, x), halt fresh variable
where: I ′ ≡ λx, k.@(k, x) and H ≡ λx.@(halt , x). (This is the term resulting from the CPS translation in example 5.) The corresponding term in value named form is:

let z 1 = λx, k.(let z 11 = λy.@(x, y, k) in @(x, x, z 11 )) in let z 2 = I ′ in let z 3 = H in @(z 1 , z 2 , z 3 ) .
Proposition 7 (VN commutation) Let M be a λ-term in CPS form. Then:

(1) R(C vn (M )) ≡ M .

(2) er (C vn (M )) ≡ C vn (er (M )).

Proposition 8 (VN simulation) Let N be a λ-term in CPS value named form. If R(N ) ≡ M and M α -→ M ′ then there exists N ′ such that N α -→ N ′ and R(N ′ ) ≡ M ′ . Syntax V ::= λid + .M | | (id * ) (values) C ::= V | | πi(id) (let-bindable terms) M ::= @(id, id + ) | | let id = C in M | | ℓ > M (CPS terms) E ::= [ ] | | let id = V in E (evaluation contexts)
Reduction Rules

E[@(x, z1, . . . , zn)] → E[[z1/y1, . . . , zn/yn]M ] if E(x) = λy1 . . . yn.M E[let z = πi(x) in M ] → E[[yi/z]M ]] if E(x) = (y1, . . . , yn), 1 ≤ i ≤ n E[ℓ > M ] ℓ -→ E[M ]
where:

E(x) =    V if E = E ′ [let x = V in [ ]] E ′ (x) if E = E ′ [let y = V in [ ]], x = y undefined otherwise

Closure conversion

The next step is called closure conversion. It consists in providing each functional value with an additional parameter that accounts for the names free in the body of the function and in representing functions using closures. Our closure conversion implements a closure using a pair whose first component is the code of the translated function and whose second component is a tuple of the values of the free variables.

It will be convenient to write "let (y 1 , . . . , y n ) = x in M " for "let

y 1 = π 1 (x) in • • • let y n = π n (x) in M " and "let x 1 = C 1 . . . x n = C n in M " for "let x 1 = C 1 in . . . let x n = C n in M ".
The transformation is described in Table 6. The output of the transformation is such that all functional values are closed. In our opinion, this is the only compilation step where the proofs are rather straightforward.

Example 9 (closure conversion) Let M ≡ C vn (C cps (λx.y)), namely M ≡ let z 1 = λx, k.@(k, y) in @(halt , z 1 ) .

Then C cc (M ) is the following term:

α -→ M ′ then C cc (M ) α ⇒ C cc (M ′ ).
Transformation in value named form (from λ ℓ cps to λ ℓ cps,vn )

Cvn (@(x0, . . . , xn)) = @(x0, . . . , xn)

Cvn (@(x * , V, V * )) = Evn (V, y)[Cvn (@(x * , y, V * ))] V = id, y fresh Cvn (let x = πi(y) in M ) = let x = πi(y) in Cvn (M ) Cvn (let x = πi(V ) in M ) = Evn (V, y)[let x = πi(y) in Cvn (M )] V = id, y fresh Cvn (ℓ > M ) = ℓ > Cvn (M ) Evn (λx + .M, y) = let y = λx + .Cvn (M ) in [ ] Evn ((x * ), y) = let y = (x * ) in [ ] Evn ((x * , V, V * ), y) = Evn (V, z)[Evn ((x * , z, V * ), y)] V = id, z fresh Readback translation (from λ ℓ cps,vn to λ ℓ cps ) R(λx + .M ) = λx + .R(M ) R(x * ) = (x * ) R(@(x, x1, . . . , xn)) = @(x, x1, . . . , xn) R(let x = πi(y) in M ) = let x = πi(y) in R(M ) R(let x = V in M ) = [R(V )/x]R(M ) R(ℓ > M ) = ℓ > R(M )

Hoisting

The last compilation step consists in moving all functions definitions at top level. In Table 7, we formalise this compilation step as the iteration of a set of program transformations that commute with the erasure function and the reduction relation. Denote with λz + .T a function that does not contain function definitions. The transformations consist in hoisting (moving up) the definition of a function λz + .T with respect to either a definition of a pair or a projection, or another including function, or a labelling. Note that the hoisting transformations do not preserve the property that all functions are closed. Therefore the hoisting transformations are defined on the terms of the λ ℓ cps,vn -calculus. As a first step, we analyse the hoisting transformations.

Proposition 12 (on hoisting transformations) The iteration of the hoisting transformation on a term in λ ℓ cc,vn (all function are closed) terminates and produces a term satisfying the syntactic restrictions specified in Table 7.

Next we check that the hoisting transformations commute with the erasure function.

Proposition 13 (hoisting commutation) Let M be a term of the λ ℓ cps,vn -calculus.

(1) If M ; N then er(M ) ; er (N ) or er (M ) ≡ er (N ). (2) If M ; • then er (M ) ; •. (3) er (C h (M )) ≡ C h (er (M )).
The proof of the simulation property requires some work because to close the diagram we need to collapse repeated definitions, which may occur, as illustrated in the example below.

Example 14 (hoisting transformations and transitions) Let

M ≡ let x 1 = λy 1 .N in @(x 1 , z)
Syntactic restrictions on λ ℓ cps,vn after closure conversion All functional values are closed.

Closure Conversion

Ccc(@(x, y + ))

= let (c, e) = x in @(c, e, y + )

Ccc(let x = C in M ) = let c = λe, x + .let (z1, . . . , z k ) = e in Ccc(N ) in let e = (z1, . . . , z k ) in let x = (c, e) in Ccc(M ) (if C = λx + .N, fv(C) = {z1, . . . , z k }) Ccc(let x = C in M ) = let x = C in Ccc(M ) (if C not a function) Ccc(ℓ > M ) = ℓ > Ccc(M )
Table 6: Closure conversion on value named CPS terms where N ≡ let x 2 = λy 2 .T 2 in T 1 and y 1 / ∈ fv(λy 2 .T 2 ). Then we either reduce and then hoist:

M → let x 1 = λy 1 .N in [z/y 1 ]N ≡ let x 1 = λy 1 .N in let x 2 = λy 2 .T 2 in [z/y 1 ]T 1 ; let x 2 = λy 2 .T 2 in let x 1 = λy 1 .T 1 in let x 2 = λy 2 .T 2 in [z/y 1 ]T 1 ;
or hoist and then reduce:

M ; let x 2 = λy 2 .T 2 in let x 1 = λy 1 .T 1 in @(x 1 , z) → let x 2 = λy 2 .T 2 in let x 1 = λy 1 .T 1 in [z/y 1 ]T 1 ;
In the first case, we end up duplicating the definition of x 2 .

We proceed as follows. First we introduce a relation S h that collapses repeated definitions and show that it is a simulation. Second, we show that the hoisting transformations induce a 'simulation up to S h '. Namely if M ℓ -→ M ′ and M ; N then there is a N ′ such that N ℓ -→ N ′ and M ′ (; * •S h ) N ′ . Third, we iterate the previous property to derive the following one.

Proposition 15 (hoisting simulation)

There is a simulation relation T h on the terms of the λ ℓ cps,vn -calculus such that for all terms M of the λ ℓ cc,vn -calculus we have M T h C h (M ).

Composed commutation and simulation properties

Let C be the composition of the compilation steps we have considered:

C = C h • C cc • C vn • C cps .
We also define a relation R C between terms in λ ℓ and terms in λ ℓ h as:

M R C P if ∃ N C cps (M ) ≡ R(N ) and C cc (N ) T h P . Notice that for all M , M R C C(M ).
Theorem 16 (commutation and simulation) Let M ∈ W 0 be a term of the λ ℓ -calculus. Then:

(1) er (C(M )) ≡ C(er (M )). (2) If M R C N and M α -→ M ′ then N α ⇒ N ′ and M ′ R C N ′ .

Syntax for λ ℓ h

Syntactic restrictions on λ ℓ cps,vn after hoisting All function definitions are at top level.

C ::= (id * ) | | πi(id ) (restricted let-bindable terms) T ::= @(id, id + ) | | let id = C in T | | ℓ > T (restricted terms) P ::= T | | let id = λid + .T in P (programs)
Specification of the hoisting transformation

C h (M ) = N if M ; • • • ; N ;
, where:

D ::= [ ] | | let id = C in D | | let id = λid + .D in M | | ℓ > D (hoisting contexts) (h1) D[let x = C in let y = λz + .T in M ] ; D[let y = λz + .T in let x = C in M ] if x / ∈ fv(λz + .T ) (h2) D[let x = (λw + .let y = λz + .T in M ) in N ] ; D[let y = λz + .T in let x = λw + .M in N ] if {w + } ∩ fv(λz + .T ) = ∅ (h3) D[ℓ > let y = λz + .T in M ] ; D[let y = λz + .T in ℓ > M ]
Table 7: Hoisting transformation

Reasoning about the cost annotations

We describe an initial labelling of the source code leading to a sound and precise labelling of the object code and an instrumentation of the labelled source program which produces a source program monitoring its own execution cost. Then, we explain how to obtain static guarantees on this execution cost by means of a Hoare logic for purely functional programs.

Initial labelling

We define a labelling function L of the source code (terms of the λ-calculus) which guarantees that the associated RTL code satisfies the conditions necessary for associating a cost with each label. We set L(M ) = L 0 (M ), where the functions L i are specified in Table 8. When the index i in L i is equal to 1, it attests that M is an immediate body of a λ-abstraction. In that case, even if M is an application, it is not post-labelled. Otherwise, when i is equal to 0, the term M is not an immediate body of a λ-abstraction, and, thus is post-labelled if it is an application.

Example 17 (labelling application) Let M ≡ λx.@(x, @(x, x)). Then L(M ) ≡ λx.ℓ 0 > @(x, @(x, x) > ℓ 1 ). Notice that only the inner application is post-labelled.

Proposition 18 (labelling properties) Let M be a term of the λ-calculus.

(1) The function L is a labelling and produces well-labelled terms, namely:

er (L i (M )) ≡ M and L i (M ) ∈ W i for i = 0, 1.
(2) We have: C(M ) ≡ er(C(L(M ))).

L(M )

= L0(M ) where:

Li(x) = x Li(λx + .M ) = λx + .ℓ > L1(M ) ℓ fresh Li((M1, . . . , Mn)) = (L0(M1), . . . , L0(Mn)) Li(πj(M )) = πj(L0(M )) Li(@(M, N + )) = @(L0(M ), (L0(N )) + ) > ℓ i = 0, ℓ fresh @(L0(M ), (L0(N )) + ) i = 1 Li(let x = M in N ) = let x = L0(M ) in Li(N )
Table 8: A sound and precise labelling of the source code (3) Labels occur exactly once in the body of each function definition and nowhere else, namely, C(L(M )) is a term P specified by the following grammar:

P ::= T | | let id = λid + .Tlab in P Tlab ::= ℓ > T | | let id = C in Tlab T ::= @(id , id + ) | | let id = C in T C ::= (id * ) | | π i (id )
Point (2) of the proposition above depends on the commutation property of the compilation function (theorem 16 [START_REF] Aiken | Better static memory management: improving region-based analysis of higherorder languages[END_REF]). The point (3) entails that a RTL program generated by the compilation function is composed of a set of routines and that each routine is composed of a sequence of assignments on pseudo-registers and a terminal call to another routine. Points ( 2) and ( 3) entail that the only difference between the compiled code and the compiled labelled code is that in the latter, upon entering a routine, a label uniquely associated with the routine is emitted. Now suppose we can compute the cost of running once each routine, where the cost is an element of a suitable commutative monoid M with binary operation ⊕ and identity 0 (the reader may just think of the natural numbers). Then we can define a function costof which associates with every label the cost of running once the related routine; the function costof is extended to words of labels in the standard way. A run of a terminating program M corresponds to a finite sequence of routine calls which in turn correspond to the finite sequence of labels that we can observe when running the labelled program. We summarise this argument in the following proviso (a modelling hypothesis rather than a mathematical proposition).

Proviso 19 For any term M of the source language, if C(L(M )) ⇓ Λ then costof(Λ) is the cost of running M .
We stress that the model at the level of the RTL programs is not precise enough to obtain useful predictions on the execution cost in terms, say, of CPU cycles. However, the compilation chain of this paper can be composed with the back-end of a moderately optimising C compiler described in our previous work [START_REF] Amadio | Certifying cost annotations in compilers[END_REF][START_REF] Ayache | Certifying and reasoning on cost annotations in C programs[END_REF]. For RTL programs such as those characterized by the grammar above, the back end produces binary code which satisfies the checks for soundness and precision that we outlined in the introduction. This remains true even if the source language is enriched with other constructions such as branching and loops as long as the labelling function is extended to handle these cases.

ψ(x) = x ψ(λx + .M ) = λx + .I(M ) ψ(V1, . . . , Vn)
= (ψ(V1), . . . , ψ(Vn))

I(V ) = (0, ψ(V )) I(@(M0, . . . , Mn)) = let (m0, x0) = I(M0) • • • (mn, xn) = I(Mn), (mn+1, xn+1) = @(x0, . . . , xn) in (m0 ⊕ m1 ⊕ • • • ⊕ mn+1, xn+1) I((M1, . . . , Mn)) = let (m1, x1) = I(M1) • • • (mn, xn) = I(Mn) in (m1 ⊕ • • • ⊕ mn, (x1, . . . , xn)) ((M1, . . . , Mn) not a value) I(πi(M )) = let (m, x) = I(M ) in (m, πi(x)) I(let x = M1 in M2) = let (m1, x) = I(M1) in (m2, x2) = I(M2) in (m1 ⊕ m2, x2) I(ℓ > M ) = let (m, x) = I(M ) in (m ℓ ⊕ m, x) I(M > ℓ) = let (m, x) = I(M ) in (m ⊕ m ℓ , x)
Table 9: Instrumentation of labelled λ-calculus.

Instrumentation

As already mentioned, given a cost monoid M, we assume the analysis of the RTL code associates with each label ℓ in the term an element m ℓ = costof(ℓ) of the cost monoid. Table 9 describes a monadic transformation, extensively analysed in Gurr's PhD thesis [START_REF] Gurr | Semantic frameworks for complexity[END_REF], which instruments a program (in our case λ ℓ ) with the cost of executing its instructions. We are then back to a standard λ-calculus (without labels) which includes a basic data type to represent the cost monoid. We assume that the reduction rules of the source language (λ) are extended to account for a call-by-value evaluation of the monoidal expressions, where each element of the monoid is regarded as a value. Then instrumentation and labelling are connected as follows.

Proposition 20 (instrumentation vs. labelling) Let M be a term of the source labelled language λ ℓ . If I(M ) ⇓ (m, V ) where V is a value then M ⇓ Λ U , costof(Λ) = m, and

I(U ) = (0, V ).
The following result summarizes the labelling approach to certified cost annotations.

Theorem 21 (certified cost) Let M be a term of the source language λ. If π 1 (I(L(M ))) ⇓ m then the cost of running C(M ) is m.

Proof. We take the following steps:

π 1 (I(L(M ))) ⇓ m implies L(M ) ⇓ Λ and costof(Λ) = m
(by proposition 20 above) implies C(L(M )) ⇓ Λ and costof(Λ) = m (by the simulation theorem 16(2)).

By proposition 18 and the following proviso 19, we conclude that m is the cost of running the compiled code C(M ). 2

Higher-order Hoare Logic

Many proof systems can be used to obtain static guarantees on the evaluation of a purely functional program. In our setting, such systems can also be used to obtain static guarantees on the execution cost of a functional program by reasoning about its instrumentation.

Syntax F ::= True | | False | | x | | F ∧ F | | F = F | | (F, F ) (formulae) | | π1 | | π2 | | λ(x : θ).F | | F F | | F ⇒ F | | ∀(x : θ).F θ ::= prop | | ι | | θ × θ | | θ → θ (types) V ::= id | | λ(id : A) + /F : (id : A)/F.M | | (V * ) (values) M ::= V | | @(M, M + ) | | let id : A/F = M in M | | (M * ) | | πi(M ) (terms)
Logical reflection of types

⌈ι⌉ = ι ⌈A1 × . . . × An⌉ = ⌈A1⌉ × . . . ⌈An⌉ ⌈A1 → A2⌉ = (⌈A1⌉ → prop) × (⌈A1⌉ × ⌈A2⌉ → prop)
Logical reflection of values We illustrate this point using a Hoare logic dedicated to call-by-value purely functional programs [START_REF] Régis-Gianas | A Hoare logic for call-by-value functional programs[END_REF]. Given a well-typed program annotated by logic assertions, this system computes a set of proof obligations, whose validity ensures the correctness of the logic assertions with respect to the evaluation of the functional program.

⌈id⌉ = id ⌈(V1, . . . , Vn)⌉ = (⌈V1⌉, . . . , ⌈Vn⌉) ⌈λ(x1 : A1)/F1 : (x2 : A2)/F2. M ⌉ = (F1, F2)
Logic assertions are written in a typed higher-order logic whose syntax is given in Table 10. From now on, we assume that our source language is also typed. The metavariable A ranges over simple types, whose syntax is

A ::= ι | | A × A | | A → A
where ι are the basic types including a data type cm for the values of the cost monoid. The metavariable θ ranges over logical types. prop is the type of propositions. Notice that the inhabitants of arrow types on the logical side are purely logical (and terminating) functions, while on the programming language's side they are computational (and potentially non-terminating) functions. Types are lifted to the logical level through a logical reflection ⌈•⌉ defined in Table 10.

We write "let x : A/F = M in M " to annotate a let definition by a postcondition F of type ⌈A⌉ → prop. We write "λ(x 1 : A 1 )/F 1 : (x 2 : A 2 )/F 2 . M " to ascribe to a λ-abstraction a precondition F 1 of type ⌈A 1 ⌉ → prop and a postcondition F 2 of type ⌈A 1 ⌉ × ⌈A 2 ⌉ → prop. Computational values are lifted to the logical level using the reflection function defined in Table 10. The key idea of this definition is to reflect a computational function as a pair of predicates consisting of its precondition and its postcondition. Given a computational function f , a formula can refer to the precondition (resp. the postcondition) of f using the predicate pre f (resp. post f ). Thus, pre (resp. post) is a synonymous for π 1 (resp. π 2 ).

To improve the usability of our tool, we define in Table 10 a surface language by extending λ with several practical facilities. First, terms are explicitly typed. Therefore, the labelling L must be extended to convey type annotations in an explicitly typed version of λ ℓ (the typing system of λ ℓ is quite standard and will be presented formally in the following section 4). The instrumentation I defined in Table 9 is extended to types by replacing each type annotation A by its monadic interpretation I(A) defined by

I(A) = cm × A, ι = ι, A 1 × A 2 = (A 1 × A 2 ) and A 1 → A 2 = A 1 → I(A 2 ).
Second, since the instrumented version of a source program would be cumbersome to reason about because of the explicit threading of the cost value, we keep the program in its initial form while allowing logic assertions to implicitly refer to the instrumented version of the program. Thus, in the surface language, in the term "let x : A/F = M in M ", F has type ⌈I(A)⌉ → prop, that is to say a predicate over pairs of which the first component is the execution cost. Third, we allow labels to be written in source terms as a practical way of giving names to the labels introduced by the labelling L. By these means, the constant cost assigned to a label ℓ can be symbolically used in specifications by writing costof(ℓ).

Finally, as a convenience, we write "x : A/F " for "x : A/λ(cost : cm, x : ⌈I(A)⌉).F ". This improves the conciseness of specifications by automatically allowing reference to the cost variable in logic assertions without having to introduce it explicitly.

Prototype implementation

We implemented a prototype compiler [START_REF] Régis-Gianas | An annotating compiler for MiniML[END_REF] in OCaml (∼ 3.5Kloc). In addition to the distributed source code, a web application enables basic experiments without any installation process.

This compiler accepts a program P written in the surface language extended with fixpoints and algebraic datatypes. We found no technical difficulty in handling these extensions and this is the reason why they are excluded from the core language in the presented formal development. Specifications are written in the Coq proof assistant [START_REF]The Coq proof assistant[END_REF]. A logic keyword is used to include logical definitions written in Coq to the source program.

Type checking is performed on P and, upon success, it produces a type annotated program P t . Then, the labelled program P ℓ = L(P t ) is generated. Following the same treatment of branching as in our previous work on imperative programs [START_REF] Amadio | Certifying cost annotations in compilers[END_REF][START_REF] Ayache | Certifying and reasoning on cost annotations in C programs[END_REF], the labelling introduces a label at the beginning of each pattern matching branch.

By erasure of specifications and type annotations, we obtain a program P λ of λ (Table 2). Using the compilation chain presented earlier, P λ is compiled into a program P h of λ h,vn (Table 7) . The annotating compiler uses the cost model that counts for each label ℓ the number of primitive operations that belong to execution paths starting from ℓ (and ending in another label or in an instruction without successor).

Finally, the instrumented version of P ℓ as well as the actual cost of each label is given as input to a verification condition generator to produce a set of proof obligations implying the validity of the user-written specifications. These proof obligations are either proved automatically using first-order theorem provers or manually in Coq.

Examples

In this section, we present two examples that are idiomatic of functional programming: an inductive function and a higher-order function. These examples were checked using our prototype implementation. More involved examples are distributed with the software. These examples include several standard functions on lists (fold, map, . . . ), combinators written in continuation-passing style, and functions over binary search trees.

An inductive function Table 11 contains an example of a simple inductive function: the standard concatenation of two lists. In the code, one can distinguish three kinds of toplevel definitions: the type definitions prefixed by the type keyword, the definitions at the logical level surrounded by logic { . . . }, and the program definitions introduced by the let keyword.

On lines 1 and 2, the type definitions introduce a type list for lists of natural numbers as well as a type bool for booleans. Between lines 3 and 9, at the logical level, a Coq inductive function defines the length of lists so that we can use this measure in the cost annotation of concat. Notice that the type definitions are automatically lifted at the Coq level, provided that they respect the strict positivity criterion imposed by Coq to ensure well-foundedness of inductive definitions.

The concatenation function takes two lists l1 and l2 as input, and it is defined, as usual, by induction on l1. In order to write a precise cost annotation, each part of the function body is labelled so that every piece of code is dominated by a label: ℓ match dominates "match l1 with Nil ⇒ • | Cons(x, xs) ⇒ •", ℓ nil dominates "Nil", ℓ cons dominates "Cons(x, •)", and ℓ rec dominates "concat(xs, l2)". Looking at the compiled code in Table 12, it is easy to check that the covering of the code by the labels is preserved through the compilation process. One can also check that the computed costs are correct with respect to a cost model that simply counts the number of instructions, i.e., costof(ℓ nil ) = 2, costof(ℓ rec ) = 6, costof(ℓ cons ) = 5 and costof(ℓ match ) = 1. Here we are simply assuming one unit of time per low-level instruction, but a more refined analysis is possible by propagating the binary instructions till the binary code (cf. [START_REF] Amadio | Certifying cost annotations in compilers[END_REF][START_REF] Ayache | Certifying and reasoning on cost annotations in C programs[END_REF]).

Finally, the specification says that the cost of executing concat (l1, l2) is proportional to the size of l1. Recall that the 'cost' and 'result' variables are implicitly bound in the post-condition. Notice that the specification is very specific on the concrete time constants that are involved in that linear function. Following the proof system of the higher-order Hoare logic [START_REF] Régis-Gianas | A Hoare logic for call-by-value functional programs[END_REF], the verification condition generator produces 37 proof obligations out of this annotated code. All of them are automatically discharged by Coq (using, in particular, the linear arithmetic decision procedure omega).

A higher-order function Let us consider a higher-order function pexists that looks for an integer x in a list l such that x validates a predicate p. In addition to the functional specification, we want to prove that the cost of this function is linear in the length n of the list l. The corresponding program written in the surface language can be found in Table 13.

A prelude declares the type and logical definitions used by the specifications. On lines 1 and 2, two type definitions introduce data constructors for lists and booleans. Between lines 4 and 5, a Coq definition introduces a predicate bound over the reflection of computational functions from nat to nat × bool that ensures that the cost of a computational function p is uniformly bounded by a constant k.

On line 9, the precondition of function pexists requires the function p to be total. Between lines 10 and 11, the postcondition first states a functional specification for pexists: the boolean result witnesses the existence of an element x of the input list l that is related to BT rue by the postcondition of p. The second part of the postcondition characterizes the cost of pexists in case of a negative result: assuming that the cost of p is bounded by a constant k, the cost of pexists is proportional to k • n. Notice that there is no need to add a label in front of BT rue in the first branch of the inner pattern-matching since the specification only characterizes the cost of an unsuccessful search.

The verification condition generator produces 53 proof obligations out of this annotated program; 46 of these proof obligations are automatically discharged and 7 of them are man- Table 13: A higher-order function and its specification.

type list = Nil | Cons (nat, list) type bool = BTrue | BFalse logic { Fixpoint length (l : list) : nat : = match l with | Nil ⇒ 0 | Cons (x, xs) ⇒ 1 + length (xs) end. } let rec concat (l1 : list, l2 : list) : list { cost = costof(ℓ match ) + costof(ℓ nil ) + (costof(ℓrec) + costof(ℓ match ) + costof(ℓcons)) × length (l1) } = ℓ match > match l1 with | Nil → ℓ nil > l2 | Cons (x, xs) → ℓcons > Cons (x, concat (xs, l2) > ℓrec) end with        costof(ℓ nil ) = 2 costof(ℓrec) = 6 costof(ℓcons) = 5 costof(ℓ match ) = 1
ually proved in Coq.

Typing the compilation chain

We describe a (simple) typing of the compilation chain. Specifically, each λ-calculus of the compilation chain is equipped with a type system which enjoys subject reduction: if a term has a type then all terms to which it reduces have the same type. Then the compilation functions are extended to types and are shown to be type preserving: if a term has a type then its compilation has the corresponding compiled type.

Besides providing insight into the compilation chain, typing is used in two ways. First, the tool for reasoning about cost annotations presented in section 3 takes as input a typed λ-term and second, and more importantly, in section 5, we rely on an enrichment of a type system which is expressive enough to type a compiled code with explicit memory deallocations.

The two main steps in typing the compilation chain are well studied, see, e.g., the work of Morrisett et al. [START_REF] Morrisett | From system F to typed assembly language[END_REF], and concern the CPS and the closure conversion steps. In the former, the basic idea is to type the continuation/the evaluation context of a term of type A with its negated type (A → R), where R is traditionally taken as the type of 'results'. In the latter, one relies on existential types to hide the details of the representation of the 'environment' of a function, i.e. the tuple of variables occurring free in its body.

Typing conventions

We shall denote with tid the syntactic category of type variables with generic elements t, s, . . . and with A the syntactic category of types with generic elements A, B, . . . A type context is denoted with Γ, Γ ′ , . . ., and it stands for a finite domain partial function from variables to types. To explicit a type context, we write x 1 : A 1 , . . . , x n : A n where the variables x 1 , . . . , x n must be all distinct and the order is irrelevant. Also we write x * : A * for a possibly empty sequence x 1 : A 1 , . . . , x n : A n , and Γ, x * : A * for the context resulting from Γ by adding

Γ ⊢ M : A Γ ⊢ ℓ > M : A Γ ⊢ M : A Γ ⊢ M > ℓ : A Restricted syntax CPS types, R type of results A ::= tid | | A + → R | | ×(A * ) (CPS types)

CPS type compilation

Ccps (t) = t Ccps (×(A * )) = ×(Ccps(A) * ) Ccps (A + → B) = (Ccps (A)) + , ¬Ccps (B) → R where: ¬A ≡ (A → R)
Table 14: Type system for λ ℓ and λ ℓ cps the sequence x * : A * . Hence the variables in x * must not be in the domain of Γ. If A is a type, we write ftv(A) for the set of type variables occurring free in it and, by extension, if Γ is a type context ftv(Γ) is the union of the sets ftv(A) where A is a type in the codomain of Γ. A typing judgement is typically written as Γ ⊢ M : A where M is some term. We shall write Γ ⊢ M * : A * for Γ ⊢ M 1 : A 1 , . . . ,Γ ⊢ M n : A n . Similar conventions apply if we replace the symbol ' * ′ with the symbol '+ ′ except that in this case the sequence is assumed not-empty. A type transformation, say T , is lifted to type contexts by defining

T (x 1 : A 1 , . . . x n : A n ) = x 1 : T (A 1 ), . . . , x n : T (A n ). Whenever we write: if Γ ⊢ S 1 M : A then T (Γ) ⊢ S 2 T (M ) : T (A)
what we actually mean is that if the judgement in the hypothesis is derivable in a certain 'type system S 1 ' then the transformed judgement in derivable in the 'type system S 2 '.

The type system of the source language

Table 14 describes the typing rules for the source language defined in Table 2. These rules are standard except those for the labellings, and, as announced, they are preserved by reduction.

Proposition 22 (subject reduction) If M is a term of the λ ℓ calculus, Γ ⊢ M : A and M → N then Γ ⊢ N : A.

The typing rules described in Table 14 apply to the CPS λ-calculus too. Table 14 describes the restricted syntax of the CPS types and the CPS type translation. Then the CPS term translation defined in Table 3 preserves typing in the following sense.

Proposition 23 (type CPS) If Γ ⊢ M : A then C cps (Γ), halt : ¬C cps (A) ⊢ C cps (M ) : R.

Type system for the value named calculi

Table 15 describes the typing rules for the value named calculi. For the sake of brevity, we shall omit the type of a term since this type is always the type of results R and write Γ ⊢ vn M rather than Γ ⊢ vn M : R. The first 6 typing rules are just a specialization of the corresponding rules in Table 14. The last two rules allow for the introduction and elimination of existential types; we shall see shortly how they are utilised in typing closure conversion.

In the proposed formalisation, we rely on the tuple constructor to introduce an existential type and the first projection to eliminate it. This has the advantage of leaving unchanged the syntax and the operational semantics of the value named λ-calculus. An alternative presentation consists in introducing specific operators to introduce and eliminate existential types which are often denoted with pack and unpack, respectively. The reader who is familiar with this notation may just read (x) as pack(x) and π 1 (x) as unpack(x) when x has an existential type. With this convention, the rewriting rule which allows to unpack a packed value is just a special case of the rule for projection.

As in the previous system, typing is preserved by reduction.

Proposition 24 (subject reduction, value named) If M is a term of the λ ℓ cps,vn -calculus, Γ ⊢ vn M and M → N then Γ ⊢ vn N .

The transformation into value named CPS form specified in Table 5 affects the terms but not the types.

Proposition 25 (type value named) If M is a term of the λ ℓ cps -calculus and Γ ⊢ M : R then Γ ⊢ vn C vn (M ).

On the other hand, to type the closure conversion we rely on existential types to abstract/hide the type of the environment as specified in Table 15. Then the term translation of the function definition and application given in Table 6 has to be slightly modified to account for the introduction and elimination of existential types. The revised definition is as follows:

Ccc(@(x, y + )) = let x = π1(x) in (← existential elimination) let (c, e) = x in @(c, e, y + ) Ccc(let x = C in M ) = let c = λe, x + .let (z1, . . . , z k ) = e in Ccc(N ) in let e = (z1, . . . , z k ) in let x = (c, e) in let x = (x) in (← existential introduction) Ccc(M ) (if C = λx + .N, fv(C) = {z1, . . . , z k })
This modified closure conversion does not affect the commutation and simulation properties stated in propositions 10 and 11 and moreover it preserves typing as follows. Similarly to the transformation in value named form, the hoisting transformations affect the terms but not the types.

Syntax types

A ::= tid | | (A + → R) | | ×(A * ) | | ∃tid .A Typing rules Γ, x + : A + ⊢ vn M Γ ⊢ vn λx + .M : A + → R x : A + → R, y + : A + ∈ Γ Γ ⊢ vn @(x, y + ) x * : A * ∈ Γ Γ ⊢ vn (x * ) : ×(A * ) y : ×(A1, . . . , An) ∈ Γ 1 ≤ i ≤ n Γ, x : Ai ⊢ vn M Γ ⊢ vn let x = πi(y) in M Γ ⊢ vn V : A Γ, x : A ⊢ vn M Γ ⊢ vn let x = V in M Γ ⊢ vn M Γ ⊢ vn ℓ > M x : [B/t]A ∈ Γ Γ ⊢ vn (x) : ∃t.A y : ∃t.A ∈ Γ Γ, x : A ⊢ vn M t / ∈ ftv(Γ) Γ ⊢ vn let x = π1(y) in M Closure conversion type compilation Ccc(t) = t Ccc(×(A * )) = ×(Ccc(A) * ) Ccc(A + → R) = ∃t. × ((t, Ccc(A) + → R), t) Ccc(∃t.A) = ∃t.Ccc(A)
Proposition 27 (type hoisting) If M is a term in λ ℓ cps,vn , Γ ⊢ vn M , and M ; N then Γ ⊢ vn N .

Typing the compiled code

We can now extend the compilation function to types by defining:

C(A) = C cc (C cps (A))
and by composing the previous results we derive the following type preservation property of the compilation function.

Theorem 28 (type preserving compilation) If M is a term of the λ ℓ -calculus and Γ ⊢ M : A then C(Γ), halt : ∃t. × (t, C(A) → R, t) ⊢ vn C(M ) .

Remark 29

The 'halt' variable introduced by the CPS translation can occur only in a subterm of the shape @(halt , x) in the intermediate code prior to closure conversion. Then in the closure conversion translation, it suffices that C cc (@(halt , x)) = @(halt , x) and give to 'halt ′ a functional rather than an existential type. With this proviso, theorem 28 above can be restated as follows:

If M is a term of the λ ℓ -calculus and Γ ⊢ M : A then C(Γ), halt : ¬C(A) ⊢ vn C(M ).

Example 30 (typing the compiled code) We consider again the compilation of the term λx.y (cf. example 9) which can be typed, e.g., as follows:

y : t 1 ⊢ λx.y : (t 2 → t 1 ) .
Its CPS translation is then typed as: y : t 1 , halt : ¬C cps (t 2 → t 1 ) ⊢ @(halt , λx, k.@(k, y)) : R .

The value named translation does not affect the types:

y : t 1 , halt : ¬C cps (t 2 → t 1 ) ⊢ vn let z 1 = λx, k.@(k, y) in @(halt , z 1 ) .
After closure conversion we obtain the following term M :

let c = λe, x, k.let y = π 1 (e), k = π 1 (k), c = π 1 (k), e = π 2 (k) in @(c, e, y) in let e = (y), z 1 = (c, e), z 1 = (z 1 ), halt = π 1 (halt ), c = π 1 (halt ), e = π 2 (halt ) in @(c, e, z 1 )
which is typed as follows:

y : t 1 , halt : ∃t. × (t, C(t 2 → t 1 ) → R, t) ⊢ vn M .
In this case no further hoisting transformation applies. If we adopt the optimised compilation strategy sketched in remark 29 then after closure conversion we obtain the following term M ′ : let c = λe, x, k.let y = π 1 (e), k = π 1 (k), c = π 1 (k), e = π 2 (k) in @(c, e, y) in let e = (y), z 1 = (c, e), z 1 = (z 1 ), in @(halt , z 1 )

which is typed as follows:

y : t 1 , halt : C(t 2 → t 1 ) → R ⊢ vn M ′ .

Memory management

We describe an enrichment of the λ ℓ h,vn -calculus called λ ℓ,r h,vn -calculus which explicitly handles allocation and deallocation of memory regions. At our level of abstraction, the memory regions are just names r, r ′ , . . . of a countable set. Intuitively, the 'live' locations of a memory are partitioned into regions. The three new operations the enriched calculus may perform are: (1) allocate a new region, (2) allocate a value (in our case a non-empty tuple) in a region, and (3) dispose a region. The additional operation of reading a value from a region is implicit in the projection operation which is already available in the non-enriched calculus λ ℓ h,vn . In order to gain some expressivity we shall also allow a function to be parametric in a collection of region names which are provided as arguments at the moment of the function call.

From our point of view, the important property of this approach to memory management is both its cost predictability and the possibility of formalising and certifying it using techniques similar to those presented in section 2. Indeed the operations (1-3) inject short sequences of instructions in the compiled code that can be executed in constant time as stressed by Tofte and Talpin [START_REF] Tofte | Region-based memory management[END_REF] (more on this at the end of section 5.2).

Because of the operation (3) described above (dispose), the following memory errors may arise at run-time: (i) write a value in a disposed region, (ii) access (project) a value in a disposed region, and (iii) dispose an already disposed region. To avoid these errors, we formulate a type and effect system in the sense of Lucassen and Gifford [START_REF] Lucassen | Polymorphic effect systems[END_REF] that over-approximates the visible set of regions and guarantees safe memory disposal, following Tofte and Talpin [START_REF] Tofte | Region-based memory management[END_REF]. This allows to further extend to the right with one more commuting square a typed version of the compilation chain described in Table 1 and then to include the cost of safe memory management in our analysis.

Region conventions

We introduce a syntactic category of regions rid with generic elements r, r ′ , . . . and a syntactic category of effects e with generic elements e, e ′ , . . . An effect is a finite set of region variables. We keep denoting types with A, B, . . . However types may depend on both regions and effects. Regions can be bound when occurring either in types or in terms. In the first case, the binder is a universal quantifier ∀r.A, while in the second it is either a new region allocation operator let all(r) in T or a region λ-abstraction. On the other hand, we stress that the disposal operator dis(r) in T is not a binder. Because of the universal quantification and the λ-abstraction both the untyped and the typed region enriched calculi include a notion of region substitution. Note that such a substitution operates on the effects contained in the types too and that, as a result, it may reduce the cardinality of the set of regions which composes an effect. The change of cardinality however, can only arise in the untyped calculus. In the typed calculus, all the region substitutions are guaranteed to be injective. We denote with frv(A) the set of regions occurring free in the type A, and frv(Γ) denotes the obvious extension to type contexts.

A region enriched calculus

A formalisation of the operations and the related memory errors is given through the region enriched calculus presented in Tables 16 and17. Notice that an empty tuple is stored in a local variable rather than in a region and that a similar stategy would be adopted for basic data values such as booleans or integers. The usual formalisation of the operational semantics relies on a rather concrete representation of a heap as a (finite domain) function mapping regions to stores which are (finite domain) functions from locations to values satisfying some coherence conditions (see, e.g. [START_REF] Tofte | Region-based memory management[END_REF][START_REF] Aiken | Better static memory management: improving region-based analysis of higherorder languages[END_REF][START_REF] Boudol | Typing safe deallocation[END_REF]). In the following, we will take a slightly more abstract approach by representing the heap implicitly as a heap context H. The latter is simply a list of region allocations, value allocations at a region, and region disposals.

It turns out that it is possible to formulate the coherence conditions on the memory directly on this list so that we do not have to commit to a concrete representation of the heap as the one sketched above. A first consequence of this design choice, is that we can dispense with the introduction of additional syntactic entities like that of 'location' or 'address' and consequently avoid the non-deterministic rules that choose fresh regions or locations (as in, say, the π-calculus, α-renaming will take care of that). A second consequence is that the proof of the simulation property of the standard calculus by the region-enriched calculus is rather direct.

Table 16: Coherence predicate on heap contexts

Continuing the comparison with formalisations found in the literature, we notice that the fact that region disposal is decoupled from allocation avoids the introduction of a special 'disposed' or 'free' region which is sometimes used in the operational semantics to represent the situation where a region becomes inaccessible (see, e.g., [START_REF] Tofte | Region-based memory management[END_REF][START_REF] Aiken | Better static memory management: improving region-based analysis of higherorder languages[END_REF]). What we do instead is to keep track of the disposal operation in the heap context.

Finally, let us notice that we certainly take advantage of the fact that our formalisation of region management targets an intermediate RTL language where the execution order and the operations of writing and reading a value from memory are completely explicit. The formalisation of region management at the level of the source language, e.g., the λ-calculus, appears a bit more involved because one has to enrich the language with operations that really refer to the way the language is compiled. For instance, one has to distinguish between the act of storing a value in memory and the act of referring to it without exploring its internal structure.

Table 16 specifies the coherence predicate Coh(H, L) of the heap context H relatively to a set of 'live' regions L. Briefly, a heap context is coherent if whenever the context contains an allocation for a tuple in a region, or a disposal of a region, the region in question is alive. This is defined by induction on the structure of the heap context H.

The reduction rules in Table 17 are a refinement of those of the value named λ-calculus described in Table 4. The main novelties are that a transition can be fired only if the heap context is coherent relatively to an empty set of regions in the sense described above and moreover that a tuple can be projected only if it is allocated in a region which has not been disposed. To formalise this last property we have refined the definition of the function E(x) which looks for the value bound to a variable in an evaluation context. The refined function, upon success, returns both the value and the part of the heap context, say H, which has been explored. Then the predicate NDis(r, H) defined in Table 16 checks that the region r where the tuple is allocated is not disposed by H. Again, this predicate is defined by induction on the structure of the heap context H.

We remark the following decomposition property of region-enriched programs.

Proposition 31 (decomposition) A program P in the region enriched λ-calculus can be uniquely decomposed as

F [H[∆]]
where F is a function context, H a heap context, and ∆ is either an application of the shape @(x, r * , y + ) or a projection of the shape let x = π i (y) in T ,

Syntax rid ::= r | | r ′ | | • • • (region identifiers) C ::= () | | (id + )at(rid ) | | πi(id) (restricted let-bindable terms) T ::= @(id, rid * , id + ) | | let id = C in T | | ℓ > T | | let all(rid ) in T | | dis(rid) in T (restricted terms) P ::= T | | let id = λrid * , id + .T in P (programs) F ::= [ ] | | let id = λrid * , id + .T in F (function contexts) H ::= [ ] | | let id = () in H | | let id = (id + )at(rid ) in H | | let all(rid ) in H | | dis(rid) in H (heap contexts) E ::= F [H] (evaluation contexts)
Reduction rules

E[@(x, r ′ 1 , . . . , r ′ m , z1, . . . , zn)] → E[[r ′ 1 /r1, . . . , r ′ m /rm, z1/y1, . . . , zn/yn]T ] if π1(E(x)) ≡ λr1, . . . , rm, y1, . . . , yn.T, E ≡ F [H], Coh(H, ∅) E[let z = πi(x) in T ] → E[[yi/z]T ]] if E(x) = ((y1, . . . , yn)at(r), H ′ ), 1 ≤ i ≤ n, E ≡ F [H], Coh(H, ∅), NDis(r, H ′ ) E[ℓ > T ] ℓ -→ E[T ] if E ≡ F [H], Coh(H, ∅)
where: or a labelling of the shape ℓ > T .

        E(x) =    (V, [ ]) if E = E ′ [let x = V in [ ]] (V, E ′′ [El ]) otherwise if E = E ′ [El], E ′ (x) = (V, E ′′ ) undefined otherwise V ::= () | | (id * )at(rid) | | λrid * , id + .T El ::= let id = V in [ ] | | let all(rid) in [ ] | | dis(rid ) in [ ]        
We define an obvious erasure function on the region-enriched types, values, and terms that just erases all the region related pieces of information (please refer to the formal definition in Table 19 of the appendix for details).

Because of the possible memory errors described above, a region enriched program does not necessarily simulate its region erasure.

Example 32 (memory errors) Consider the following program P in λ ℓ h,vn (not necessarily the result of a compilation):

P ≡ F [@(pair , v 1 , v 2 )] F ≡ let prj1 = λx.let y = π 1 (x) in @(halt , y) in let pair = λx 1 , x 2 .let y = (x 1 , x 2 ) in @(prj1 , y) in [ ] .
One strategy to manage memory regions in P is to allocate a region upon entering the pair function and to dispose it just before calling the prj1 function as in the following program P 1 in λ ℓ,r h,vn .

P 1 ≡ F 1 [@(pair , v 1 , v 2 )] F 1 ≡ let prj1 = λx.let z = π 1 (x) in @(halt , z) in let pair = λx 1 , x 2 .let all(r) in let y = (x 1 , x 2 )at(r) in dis(r) in @(prj1 , y) in [ ] .
Unfortunately this strategy leads to a memory error as:

P 1 * → F 1 [H 1 [let z = π 1 (y) in @(halt , z)]] H 1 ≡ let all(r) in let y = (v 1 , v 2 )at(r) in dis(r) in [ ] Formally, F 1 [H 1 ](y) = ((v 1 , v 2 )at(r), H 2 ), H 2 = dis(r) in [ ],
and the predicate NDis(r, H 2 ) does not hold. In plain words, the problem with this strategy is that it disposes the region r before the value (v 1 , v 2 ) allocated into it is projected. A better strategy is to pass the region created in the function pair to the function prj1 and let this function dispose the region once the value (v 1 , v 2 ) has been projected. This strategy is described by the following program P 2 in λ ℓ,r h,vn .

P 2 ≡ F 2 [@(pair , v 1 , v 2 )] F 2 ≡ let prj1 = λr, x.let z = π 1 (x) in dis(r) in @(halt , z) in let pair = λx 1 , x 2 .let all(r) in let y = (x 1 , x 2 )at(r) in @(prj1 , r, y) in [ ] .
This time the reduction leads to a normal termination:

P 2 * → F 2 [H 2 [@(halt , v 1 )]] H 2 ≡ let all(r) in let y = (v 1 , v 2 )at(r) in dis(r) in [ ] .
We conclude this section with an overview of a rather standard implementation scheme of region based memory management (see, e.g., [START_REF] Makholm | A language-independent framework for region inference[END_REF] for more details). Initially, the available memory is partitioned in pages which constitute a free list. A region is a pointer to a 'region descriptor' that contains a pointer to the beginning and the end of a list of pages and a counter which gives the amount of memory available in the last page of the list. A value (a non-empty tuple in our case) is just a pointer to a memory address and an access to a value is direct. Storing a value in a region means storing the value in the last page of the list related to the region and updating the region descriptor. If the space available is not sufficient, then one or more pages are taken from the free list and appended to the end of the region list and again the region descriptor is updated. This operation can be executed in constant time as long as the size of the values to be allocated can be determined at compile time (which is obviously true in our case). Deallocating a region means concatenating the list related to the region to the free list. We refrain from going into the details of the implementation scheme mentioned above which really belong to the backend of the compiler. Indeed, the scheme is rather independent from the source language (for instance, Christiansen et al. [START_REF] Christiansen | Safe region-based memory management for objects[END_REF] rely on it to implement an object-oriented language) while depending for its efficiency on the memory organisation of the processor and possibly the operating system.

A type and effect system

In order to have the simulation property, we require that the region-enriched program is typable with respect to an enhanced type and effect system described in Table 18 whose purpose is precisely to avoid memory errors at run time. The system defines judgment (i) Γ ⊢ rg T : e read "restricted term T has effect e under Γ" ; (ii) Γ ⊢ rg C : A read "restricted let-bindable term C has type A under Γ" and (iii) Γ ⊢ rg P : e read "program P has effect e under Γ". The formalisation follows the work of Aiken et al. [START_REF] Aiken | Better static memory management: improving region-based analysis of higherorder languages[END_REF] in that allocation and disposal of a region are decoupled (see also Henglein et al. [16] for a survey and Boudol [START_REF] Boudol | Typing safe deallocation[END_REF] for a discussion). Then a region can be disposed only if the following computation neither accesses nor disposes it. Note that in typing values we omit the effect (which is always empty) and in typing terms we omit the type (which is always the type of results R). The typing rules are designed to maintain several invariants. First, if a program P has effect e then the set of regions e over-approximates the visible regions that the program P may dispose or access for allocating or reading a value. Second, all the region names have been allocated (and possibly disposed afterwards). Third, distinct region names in the program correspond at run time to different regions, i.e., all region substitutions are injective. With respect to the system described in Table 15, we notice that we distinguish the rules for typing an empty and a non-empty tuple as the former has no effect on the heap. For similar reasons, we split the rule for typing a value definition in three depending on whether the value is an empty tuple, a function, or a non-empty tuple (possibly of existential type). Only in the last case an effect on the heap is recorded. As already mentioned, empty tuples do not affect the heap and function definitions eventually become sequences of assembly language instructions which are stored in a statically allocated and read-only zone of memory separated by the data memory.

Example 33 (types and effects) Going back to example 32, let us assume the types t i : v i , i = 1, 2 and halt : t 1 ∅ -→ R. Then the reader may check that the program P 2 is typable (has an effect) assuming the following types for the functions:

pair : t 1 , t 2 ∅ -→ R prj1 : ∀r. × (t 1 , t 2 )at(r) {r} --→ R
On the other hand, any attempt at typing P 1 fails trivially because the type of the function prj1 must be of the shape ×(t 1 , . . .)at(r) Example 34 (injective region substitutions) The soundness and relative simplicity of the type and effect system bear on the fact that region substitutions are injective. Technically this property is enforced by the rules for typing an application and an abstraction. In an application, say @(x, r * , y + ), the region variables r * are distinct and the type of x is region closed. In an abstraction, say λr * , x + .T , the function is region closed. It is instructive to see what can go wrong if we drop these conditions. Consider a function x of the following shape with its possible (region closed) type:

x = λr 1 , r 2 , y.dis(r 1 ) in let z = (y)at(r 2 ) in T : ∀r 1 , r 2 . × () {r 1 ,r 2 } ----→ R .
An application @(x, r, r, y) where we pass twice the same region name r will produce a memory error since we dispose r before writing into it. A similar phenomenon arises with a function of the following shape and related (region open!) type:

x = λr 1 , y.dis(r 1 ) in let z = (y)at(r 2 ) in T : ∀r 1 . × ()

{r 1 ,r 2 } ----→ R .
Then an application @(x, r 2 , y) where we pass a region name which is free in the type of the function will also produce a memory error. As a final example, consider a function

x = λr 1 , y.let z = () in @(y, r 1 , r 2 , z)
with a free region variable r 2 . Note that r 1 , r 2 may not appear in the type of the function x because, e.g., y makes no use of them. Then if we apply x as in @(x, r 2 , y) we end up with an application @(y, r 2 , r 2 , z) which is not typable because it violates the condition that all the region variables passed as arguments are distinct.

Properties of the type and effect system

We notice that the region erasing function preserves typing.

Proposition 35 (region erasure) If Γ ⊢ rg P : e then rer (Γ) ⊢ vn rer (P ).

In the other direction, it is always possible to insert region annotations in a typable program of λ ℓ h,vn so as to produce a typable region-enriched program. A simple but not very interesting way to do this is to allocate one region at the very beginning of the computation which is never disposed and which is shared by all functions.

Proposition 36 (region enrichment) Let Γ 0 be a type context such that if x : A ∈ Γ 0 then A is not a type of the shape ×(B + ) or ∃t.B. If Γ 0 ⊢ vn P then it is always possible to find a region enriched typable program P ′ such rer (P ′ ) ≡ P .

Fortunately, more interesting strategies are available; we refer to Aiken et al. [START_REF] Aiken | Better static memory management: improving region-based analysis of higherorder languages[END_REF] for their description and for an encouraging experimental evaluation and to Henglein et al. [START_REF] Henglein | Effect types and region-based memory management[END_REF] for a survey of region inference techniques. For our purposes, it is enough to know that it is always possible to define a compilation function C rg from the typed λ ℓ h,vn -calculus to the typed λ ℓ,r h,vn -calculus which is a right inverse of the region erasing function, i.e., rer (C rg (P )) ≡ P , and which commutes with the label erasure functions, i.e., er(C rg (P )) ≡ C rg (er (P )). Also, following remark 29, we notice that the typing context of the compiled code satisfies the conditions in proposition 36 provided that if Γ is the typing context of the source code and x : A ∈ Γ then the type A is not of the shape ×(B + ).

Next we remark that the type system entails the coherence of the heap context of a program; this leads to the following progress property.

Proposition 37 (progress) Let P be a typable program in the region enriched calculus such that frv(P ) = ∅. Then P decomposes as F [H[∆]] (proposition 31) and either (i) P reduces or (ii) ∆ has the shape @(x, r * , y + ) or let y = π i (x) in T , where x ∈ fv(P ).

Of course, we must also prove that the region enriched types are preserved by reduction.

Proposition 38 (subject reduction, types and effects) If Γ ⊢ rg P : e and P → P ′ then Γ ⊢ rg P ′ : e.

Finally, we can show that a well-typed region enriched program does indeed simulate its region erasure.

Theorem 39 (region simulation) If Γ ⊢ rg P : e, frv(P ) = ∅, and rer (P ) α -→ Q then P α -→ P ′ and rer (P ′ ) ≡ Q.

Conclusion

We have shown that our approach, that we call the 'labelling' approach, can be used to obtain certified execution costs on functional programs following a standard compilation chain which composes well with the back-end of a moderately optimising C compiler. The technique allows to compute the cost of the compiled code while reasoning abstractly at the level of the source language and it accounts precisely for the cost of memory management for a particular memory management strategy that uses regions. To provide technical evidence for this claim has required to have an in-depth and sometimes novel look at the formal properties of the compilation chain; notable examples are the commutation property of the CPS transformation and the simulation property for the hoisting and the region aware transformations.

Proof of proposition 3 [CPS commutation]

The proof takes the following steps:

1. We remark that if V is a value in λ ℓ and K a continuation in λ ℓ cps then so are er (V ) and er (K). The proof is a direct induction on the structure of V and K, respectively.

2. For all values V and terms M of the λ ℓ -calculus, we check that:

er ([V /x]M ) ≡ [er (V )/x]er (M ) .
The proof proceeds by induction on the structure of M .

3. We notice that λx.(x | K) ≡ K holds, for all continuations K such that K is an abstraction.

4. For all terms M and continuations K such that either M ∈ W 0 and K is an abstraction or M ∈ W 1 the following holds:

er (M | K) ≡ er (M ) | er (K) .
We proceed by induction on M .

x We expand the definition of x | K depending on whether K is a variable or a function and we rely on step 2.

λx + .M We have λx + .M ∈ W 1 and M ∈ W 1 . We analyse λx + .M | K depending on whether K is a variable or a function and we apply the inductive hypothesis on M and step 2. Notice that it is essential that M ∈ W 1 to apply the inductive hypothesis.

@(M 0 , . . . , M n ) We know M 0 , . . . , M n ∈ W 0 . We apply the inductive hypothesis on M n , . . . , M 0 to conclude that:

er (@(M 0 , . . . , M n )) | er (K) ≡ er (M 0 ) | λx 0 . . . . er(M n ) | λx n .@(x 0 , . . . , x n , er (K)) ≡ er (M 0 ) | λx 0 . . . . er(M n | λx n .@(x 0 , . . . , x n , K)) ≡ • • • ≡ er (M 0 | λx 0 . . . . M n | λx n .@(x 0 , . . . , x n , K)) ≡ er (@(M 0 , . . . , M N ) | K) .
ℓ > M We know that if ℓ > M ∈ W i then M ∈ W i and we apply the inductive hypothesis on M .

M > ℓ By definition, we must have M > ℓ ∈ W 0 . Hence K is a function and M ∈ W 0 . Then we apply the inductive hypothesis on M and step 3.

(M 1 , . . . , M n ) We know that M i ∈ W 0 for i = 1, . . . , n. First we notice that:

er (λx n .(x 1 , . . . , x n ) | K) ≡ λx n .(x 1 , . . . , x n ) | er (K) .
Then we apply the inductive hypothesis on M n , . . . , M 0 to conclude that:

er ((M 1 , . . . , M n )) | er (K) ≡ er (M 1 ) | λx 1 . . . er(M n ) | λx n .(x 1 , . . . , x n ) | er (K) ≡ er (M 1 ) | λx 1 . . . er(M n ) | er (λx n .(x 1 , . . . , x n ) | K) ≡ er (M 1 ) | λx 1 . . . er(M n | λx n .(x 1 , . . . , x n ) | K) ≡ • • • ≡ er (M 1 | λx 1 . . . M n | λx n .(x 1 , . . . , x n ) | K) ≡ er ((M 1 , . . . , M n ) | K) .
π i (M ) We know M ∈ W 0 . We observe that er (y | K) ≡ y | er (K). Then we apply the inductive hypothesis on M to conclude that:

er (π i (M )) | er (K) ≡ π i (er (M )) | er (K) ≡ er (M ) | λx.let y = π i (x) in y | er (K) ≡ er (M ) | er (λx.let y = π i (x) in y | K) ≡ er (M | λx.let y = π i (x) in y | K) ≡ er (π i (M ) | K) . let x = N in M If let x = N in M ∈ W i then we know N ∈ W 0 and M ∈ W i .
We apply the inductive hypothesis on N and M to conclude that:

er (let x = N in M | K) ≡ er (N | λx.(M | K)) ≡ er (N ) | λx.er (M | K) ≡ er (N ) | λx.er (M ) | er (K) ≡ er (let x = N in M ) | er (K) .
5. Then we prove the assertion for M ∈ W 0 as follows:

er (C cps (M )) ≡ er (M | λx.@(halt , x)) (by definition) ≡ er (M ) | λx.@(halt , x) (by point 4) ≡ C cps (er (M )) (by definition).

2

Proof of proposition 4 [CPS simulation]

The proof takes the following steps.

1. We show that for all values V , terms M , and continuations K = x:

[V /x]M | [ψ(V )/x]K ≡ [ψ(V )/x](M | K) .
We proceed by induction on M .

M is a variable. By case analysis: M ≡ x or M ≡ y = x.

λz + .M By case analysis on K which is either a variable or a function. We develop the second case with K ≡ λy.N . We observe:

[V /x](λz + .M ) | [ψ(V )/x]K ≡ [λz + , k.([V /x]M | k)/y][ψ(V )/x]N ≡ [λz + , k.[ψ(V )/x](M | k)/y][ψ(V )/x]N ≡ [ψ(V )/x][λz + , k.(M | k)/y]N ≡ [ψ(V )/x]((λz + .M ) | K) .
@(M 0 , . . . , M n ) We apply the inductive hypothesis on M 0 , . . . , M n as follows:

[ψ(V )/x](@(M 0 , . . . , M n ) | K) ≡ [ψ(V )/x](M 0 | λx 0 . . . M n | λx n .@(x 0 , . . . , x n , K)) • • • ≡ [V /x]M 0 | λx 0 . . . [ψ(V )/x](M n | λx n .@(x 0 , . . . , x n , K)) ≡ [V /x]M 0 | λx 0 . . . [V /x]M n | λx n .@(x 0 , . . . , x n , [ψ(V )/x]K) ≡ [V /x]@(M 0 , . . . , M n ) | [ψ(V )/x]K .
Note that in this case the substitution [ψ(V )/x] may operate on the continuation. The remaining cases (pairing, projection, let, pre and post labelling) follow a similar pattern and are omitted.

2. The evaluation contexts for the λ ℓ -calculus described in Table 2 can also be specified 'bottom up' as follows:

E ::= [ ] | | E[@(V * , [ ], M * )] | | E[let id = [ ] in M ] | | E[(V * , [ ], M * )] | | E[π i ([ ])] | | E[[ ] > ℓ] .
Following this specification, we associate a continuation K E with an evaluation context as follows:

K [ ] = λx.@(halt , x) K E[@(V * ,[ ],M * )] = λx.M * | λy * .@(ψ(V ) * , x, y * , K E ) K E[let x=[ ] in N ] = λx.N | K E K E[(V * ,[ ],M * )] = λx.M * | λy * .(ψ(V ) * , x, y * ) | K E K E[π i ([ ]) = λx.let y = π i (x) in y | K E K E[[ ]>ℓ] = λx.ℓ > x | K E where M * | λx * .N stands for M 0 | λx 0 . . . M n | λx n .N with n ≥ 0.
3. For all terms M and evaluation contexts E, E ′ we prove by induction on the evaluation context E that the following holds:

E[M ] | K E ′ ≡ M | K E ′ [E] .
For instance, we detail the case where the context has the shape

E[@(V * , [ ], M * )]. E[@(V * , [M ], M * )] | K E ′ ≡ @(V * , [M ], M * ) | K E ′ [E] (by inductive hypothesis) ≡ M | λx.M * | λx * .@(ψ(V ) * , x, x * , K E ′ [E] ) ≡ M | K E ′ [E[@(V * ,[ ],M * )]] .
4. For all terms M , continuations K, K ′ , and variable x / ∈ fv(M ) we prove by induction on M and case analysis that the following holds:

[K/x](M | K ′ ) → M | K ′ if K abstraction, M value, K ′ = x ≡ (M | [K/x]K ′ ) otherwise.
5. Finally, we prove the assertion by proceeding by case analysis on the reduction rule.

• E[@(λx

+ .M, V + )] → E[[V + /x + ]M ]. We have: E[@(λx + .M, V + )] | K [ ] ≡ @(λx + .M, V + ) | K E ≡ @(λx + , k.M | k, ψ(V ) + , K E ) → [K E /k, ψ(V ) + /x + ](M | k) ≡ [K E /k]([V + /x + ]M | k) * → [V + /x + ]M | K E ≡ E[[V + /x + ]M ] | K [ ] . • E[let x = V in M ] → E[[V /x]M ]. We have: E[let x = V in M ] | K [ ] ≡ let x = V in M | K E ≡ V | λx.(M | K E ) ≡ [ψ(V )/x](M | K E ) ≡ [V /x]M | K E ≡ E[[V /x]M ] | K [ ] . • E[π i (V )] → E[V i ]
, where V ≡ (V 1 , . . . , V n ) and 1 ≤ i ≤ n. We have:

E[π i (V )] | K [ ] ≡ π i (V ) | K E ≡ V | λx.let y = π i (x) in y | K E ≡ let y = π i (ψ(V 1 ), . . . , ψ(V n )) in y | K E → [ψ(V i )/y](y | K E ) ≡ V i | K E ≡ E[V i ] | K [ ] . • E[ℓ > M ] ℓ -→ E[M ]. We have: E[ℓ > M ] | K [ ] ≡ ℓ > M | K E ≡ ℓ > (M | K E ) ℓ -→ (M | K E ) ≡ E[M ] | K [ ] . • E[V > ℓ] ℓ -→ E[V ]. We have: E[V > ℓ] | K [ ] ≡ V > ℓ | K E ≡ V | λx.ℓ > x | K E ≡ ℓ > (V | K E ) ℓ -→ V | K E ≡ E[V ] | K [ ] .
2

Proof of proposition 7 [VN commutation]

(1) We show that for every P which is either a term or a value of the λ ℓ cps -calculus the following properties hold:

A If P is a term then R(C vn (P )) ≡ P .
B If P is a value then for any term N , R(E vn (P, x)[N ]) ≡ [P/x]R(N ).

We prove the two properties at once by induction on the structure of P . @(x, x + ) We are in case A and by definition we have: R(C vn (@(x, x + ))) ≡ R(@(x, x + )) ≡ @(x, x + ) . @(x * , V, V * ), V = id Again in case A. We have: R(C vn (@(x * , V, V * ))) ≡ R(E vn (V, y)[C vn (@(x * , y, V * ))]) ≡ [V /y]R(C vn (@(x * , y, V * ))) (by ind. hyp. on B) ≡ [V /y]@(x * , y, V * ) (by ind. hyp. on A) ≡ @(x * , V, V * ) . let x = π i (z) in M Again in case A. We have:

R(C vn (let x = π i (z) in M )) ≡ R(let x = π i (z) in C vn (M )) ≡ let x = π i (z) in R(C vn (M )) ≡ let x = π i (z) in M
(by ind. hyp. on A) . let x = π i (V ) in M, V = id Again in case A. We have: (y * , V, V * ), V = id Last case for B. We have: (2) The proof is similar to the previous one. We show that for every P which is either a term or a value of the λ ℓ cps -calculus the following properties hold:

R(C vn (let x = π i (V ) in M )) ≡ R(E vn (V, y)[let x = π i (y) in C vn (M )]) ≡ [V /y]R(let x = π i (y) in C vn (M )) (by ind. hyp. on B) ≡ [V /y]let x = π i (y) in R(C vn (M )) ≡ [V /y]let x = π i (y) in M (by ind. hyp. on A) ≡ let x = π i (V ) in M .
R(E vn ((y * , V, V * ), x)[N ]) ≡ R(E vn (V, z)[E vn ((y * , z, V * ), x)[N ]]) ≡ [V /z]
A If P is a term then er (C vn (P )) ≡ C vn (er (P )).

B If P is a value then for any term N , er (E vn (P, x)[N ]) ≡ E vn (er (P ), x)[er (N )].

We prove the two properties at once by induction on the structure of P . @(x, x + ) We are in case A and by definition we have:

er (C vn (@(x, x + ))) ≡ er (@(x, x + )) ≡ @(x, x + ) ≡ C vn (er (@(x, x + ))) .

@(x * , V, V * ), V = id Again in case A. We have:

er (C vn (@(x * , V, V * ))) ≡ er (E vn (V, y)[C vn (@(x * , y, V * ))]) ≡ E vn (er (V ), y)[er (C vn (@(x * , y, V * )))] (by ind. hyp. on B) ≡ E vn (er (V ), y)[C vn (er (@(x * , y, V * )))] (by ind. hyp. on A) ≡ C vn (er (@(x * , V, V * ))) .

let x = π i (z) in M Again in case A. We have: (y * , V, V * ), V = id Last case for B. We have:

er (C vn (let x = π i (z) in M )) ≡ er (let x = π i (z) in C vn (M )) ≡ let x = π i (z) in er(C vn (M )) ≡ let x = π i (z) in C vn (er (M )) (by ind. hyp. on A) ≡ C vn (er (let x = π i (z) in M )) . let x = π i (V ) in M, V = id Again in case A. We have: er(C vn (let x = π i (V ) in M )) ≡ er (E vn (V, z)[let x = π i (z) in C vn (M )]) ≡ E vn (er (V ), z)[let x = π i (z)
er (E vn ((y * , V, V * ), x)[N ]) ≡ er (E vn (V, z)[E vn ((y * , z, V * ), x)[N ]]) ≡ E vn (er (V ), x)[er (E vn ((y * , z, V * ), x)[N ])]
(by ind. hyp. on B) ≡ E vn (er (V ), x)[E vn (er ((y * , z, V * )), x)[er (N )]] (by ind. hyp. on B) ≡ E vn (er ((y * , V, V * )), x)[er (N )] .

2

Proof of proposition 8 [VN simulation]

First we fix some notation. We associate a substitution σ E with an evaluation context E of the λ ℓ cps,vn -calculus as follows:

σ [ ] = Id σ let x=V in E = [R(V )/x] • σ E .
Then we prove the property by case analysis.

• If R(N ) ≡ @(λy

+ .M, V + ) → [V + /y + ]M then N ≡ E[@(x, x + )], σ E (x) ≡ λy + .M , and σ E (x + ) ≡ V + . Moreover, E ≡ E 1 [let x = λy + .M ′ in E 2 ] and σ E 1 (λy + .M ′ ) ≡ λy + .M . Therefore, N → E[[x + /y + ]M ′ ] ≡ N ′ and we check that R(N ′ ) ≡ σ E ([x + /y + ]M ′ ) ≡ [V + /y + ]M . • If R(N ) ≡ let x = π i ((V 1 , . . . , V n )) in M → [V i /x]M then N ≡ E[let x = π i (y) in N ′′ ], σ E (y) ≡ (V 1 , . . . , V n ), and σ E (N ′′ ) ≡ M . Moreover, E ≡ E 1 [let y = (z 1 , . . . , z n ) in E 2 ] and σ E 1 (z 1 , . . . , z n ) ≡ (V 1 , . . . , V n ). Therefore, N → E[[z i /x]N ′′ ] ≡ N ′ and we check that R(N ′ ) ≡ σ E ([z i /x]N ′′ ) ≡ [V i /x]M . • If R(N ) ≡ ℓ > M ℓ -→ M then N ≡ E[ℓ > N ′′ ] and σ E (N ′′ ) ≡ M . We conclude by observing that N ℓ -→ E[N ′′ ]. 2

Proof of proposition 10 [CC commutation]

This is a simple induction on the structure of the term M . @(x, y + ) We have: er (C cc (@(x, y + ))) ≡ er (let (c, e) = x in @(c, e, y + )) ≡ let (c, e) = x in @(c, e, y + ) ≡ C cc (@(x, y + )) ≡ er (C cc (@(x, y + ))) .

let x = C in M , C not a function We have: er(C cc (let x = C in M )) ≡ er (let x = C in C cc (M )) ≡ let x = C in er (C cc (M )) ≡ let x = C in C cc (

Proof of proposition 11 [CC simulation]

As a first step we check that the closure conversion function commutes with name substitution:

C cc ([x/y]M ) ≡ [x/y]C cc (M ) .
This is a direct induction on the structure of the term M . Then we extend the closure conversion function to contexts as follows: We note that for any evaluation context E, C cc (E) is again an evaluation context, and moreover for any term M we have:

C cc ([ ]) = [ ] C cc (let x = (y * ) in E) = let x = (y * ) in C cc (E) C cc (let x = λx + .M in E) =
C cc (E[M ]) ≡ C cc (E)[C cc (M )] .
Finally we prove the simulation property by case analysis of the reduction rule being applied.

• Suppose M ≡ E[@(x, y + )] → E[[y + /x + ]M ]
C cc (E)[let (c ′ , e ′ ) = x in @(c ′ , e ′ , y + )] * → C cc (E)[let (z 1 , . . . , z k ) = e in [y + /x + ]C cc (M )] * → C cc (E)[[y + /x + ]C cc (M )] ≡ C cc (E)[C cc ([y + /x + ]M )] (by substitution commutation) ≡ C cc (E[[y + /x + ]M ]) . • Suppose M ≡ E[let x = π i (y) in M ] → E[[z i /x]M ] where E(y) = (z 1 , . . . , z k ), 1 ≤ i ≤ k.
Then:

C cc (E[let x = π i (y) in M ]) ≡ C cc (E)[let x = π i (y) in C cc (M )]
with C cc (E)(y) = (z 1 , . . . , z k ). Therefore:

C cc (E)[let x = π i (y) in C cc (M )] → C cc (E)[[z i /x]C cc (M )] ≡ C cc (E)[C cc ([z i /x]M )] (by substitution commutation) ≡ C cc (E[[z i /x]M ]) . • Suppose M ≡ E[ℓ > M ] ℓ -→ E[M ]. Then: C cc (E[ℓ > M ]) ≡ C cc (E)[C cc (ℓ > M )] ≡ C cc (E)[ℓ > C cc (M )] ℓ -→ C cc (E)[C cc (M )] ≡ C cc (E[M ]) . 2

Proof of proposition 12 [on hoisting transformations]

As a preliminary remark, note that the hoisting contexts D can be defined in an equivalent way as follows:

D ::= [ ] | | D[let x = C in [ ]] | | D[let x = λy + .[ ] in M ] | | D[ℓ > [ ]]
If D is a hoisting context and x is a variable we define D(x) as follows:

D(x) =        λz + .T if D = D ′ [let x = λz + .T in [ ]] D ′ (x) o.w. if D = D ′ [let y = C in [ ]], x = y D ′ (x) o.w. if D = D ′ [let y = λy + .[ ] in M ], x / ∈ {y + } undefined o.w.
The intuition is that D(x) checks whether D binds x to a simple function λz + .T . If this is the case it returns the simple function as a result, otherwise the result is undefined.

Let I be the set of terms of the λ ℓ cps,vn such that if M ≡ D[let x = λy + .T in N ] and z ∈ fv(λy + .T ) then D(z) = λz + .T ′ . Thus a name free in a simple function must be bound to another simple function. We prove the following properties:

1. The hoisting transformations terminate. 5. The set I is an invariant of the hoisting transformations, i.e., if M ∈ I and M ; N then N ∈ I.

6. If a term satisfying the invariant above is not a program then a hoisting transformation applies.

(1) To prove the termination of the hoisting transformations we introduce a size function from terms to positive natural numbers as follows:

|@(x, x + )| = 1 |let x = λy + .M in N | = 2 • |M | + |N | |let x = C in N | = 2 • |N | |ℓ > N | = 2 • |N | .
Then we check that if M ; N then |M | > |N |. Note that the hoisting context D induces a function which is strictly monotone on natural numbers. Thus it is enough to check that the size of the redex term is larger than the size of the reduced term.

(h 1 ) |let x = C in let y = λz + .T in M | = 2 • (2 • |T | + |M |) > 2 • |T | + 2 • |M | = |let y = λz + .T in let x = C in M | . (h 2 ) |let x = λw + .let y = λz + .T in M in N | = 2 • (2 • |T | + |M |) + |N | > 2 • |T | + 2 • |M | + |N | = |let y = λz + .T in let x = λw + .M in N | . (h 3 ) |ℓ > let y = λz + .T in M | = 2 • (2 • |T | + |M |) > 2 • |T | + 2 • |M | = |let y = λz + .T in ℓ > M | .
(2) Since the hoisting transformation is terminating, by Newman's lemma it is enough to prove local confluence. There are 9 = 3 • 3 cases to consider. In each case one checks that the two redexes cannot superpose. Moreover, since the hoisting transformations neither duplicate nor erase terms, one can close the diagrams in one step. For instance, suppose the term D[let x = λw + .let y = λz + .T in M in N ] contains a distinct redex ∆ of the same type (a function definition containing a simple function definition). Then the root of this redex can be in the subterms M or N or in the context D. Moreover if it is in D, then either it is disjoint from the first redex or it contains it strictly. Indeed, the second let of the second redex cannot be the first let of the first redex since the latter is not defining a simple function.

(3) By induction on M . Let F be an abbreviation for let x = λy + .T in N . @(x, x + ) The property holds trivially. (4) In the terms of the λ ℓ cc,vn calculus all functions are closed and therefore the condition is vacuously satisfied.

(5) We proceed by case analysis on the hoisting transformations. [START_REF] Bacon | A real-time garbage collector with low overhead and consistent utilization[END_REF] We proceed by induction on the structure of the term M . @(x, y + ) This is a program. let x = C in M ′ There are two cases:

• If M ′ is not a program then by inductive hypothesis a hoisting transformation applies and the same transformation can be applied to M .

• If M ′ is a program then it has a function definition on top (otherwise M is a program). Because M belongs to I the side condition of (h 1 ) is satisfied. let x = λy + .M ′ in M ′′ Again there are two cases:

• If M ′ or M ′′ are not programs then by inductive hypothesis a hoisting transformation applies and the same transformation can be applied to M .

• Otherwise, M ′ is a program with a function definition on top (otherwise M is a program). Because M belongs to I the side condition of (h 2 ) is satisfied.

ℓ > M ′ Again there are two cases:

• If M ′ is not a program then by inductive hypothesis a hoisting transformation applies and the same transformation can be applied to M .

• If M ′ is a program then it has a function definition on top (otherwise M is a program) and (h 3 ) applies to M . 2

Proof of proposition 13 [hoisting commutation]

As a preliminary step, extend the erasure function to the hoisting contexts in the obvious way and notice that (i) if D is a hoisting context then er (D) is a hoisting context too, and (ii) er(D[M ]) ≡ er (D)[er (M )].

• The reduction rule is

E[@(x, y + )] → E[[y + /z + ]M ]
where E(x) = λz + .M . We suppose that π is the path which corresponds to the letdefinition of the variable x and π ′ is that path that determines the redex of the hoisting transformation.

(h 1 ) There are two critical cases.

1. The let-definition that defines a function of the hoisting transformation coincides with the let-definition of x. In this case M is actually a restricted term T . The diagram is closed in one step. 2. The path π ′ determines a subterm of M . If we reduce first then we have to apply the hoisting transformation twice to close the diagram using the fact that these transformations are preserved by name substitution.

(h 2 ) Again there are two critical situations.

1. The top level let-definition of the hoisting transformation coincides with the let-definition of the variable x in the reduction. This is the case illustrated by the example 14. If we reduce first then we have to apply the hoisting transformation twice (again using preservation under name substitution). After this we have to commute the let-definitions and finally collapse two identical ones. 2. The path π ′ determines a subterm of M . If we reduce first then we have to apply the hoisting transformation twice to close the diagram using the fact that these transformations are preserved by name substitution.

(h 3 ) There are two critical cases.

1. The function let-definition in the hoisting transformation coincides with the let-definition of the variable x in the reduction. We close the diagram in one step. 2. The path π ′ determines a subterm of M . If we reduce first then we have to apply the hoisting transformation twice to close the diagram using the fact that these transformations are preserved by name substitution.

• The reduction rule is

E[let x = π i (y) in M ] → E[[z i /x]M ]
where E(y) = (z 1 , . . . z n ) and 1 ≤ i ≤ n.

(h 1 ) There are two critical cases.

1. The first let-definition in the hoisting transformation coincides with the letdefinition of the tuple in the reduction. We close the diagram in one step. 2. The first let-definition in the hoisting transformation coincides with the projection in the reduction. If we reduce first then there is no need to apply a hoisting transformation to close the diagram because the projection disappears.

(h 2 ) The only critical case arises when the redex for the hoisting transformation is contained in M . We close the diagram in one step using the fact that the transformations are preserved by name substitution.

(h 3 ) Same argument as in the previous case.

• The reduction rule is

E[ℓ > M ] ℓ -→ E[M ]
The hoisting transformations can be either in E or in M . In both cases we close the diagram in one step.

We conclude by proving by diagram chasing the following proposition. We rely on the previous lemma and the fact that S h is a simulation.

Proposition 46 The relation T h = ((; * ) • S h ) * is a simulation and for all terms of the λ ℓ cc,vn -calculus, M T h C h (M ).

Proof of theorem 16 [commutation and simulation]

By composition of the commutation and simulation properties of the four compilation steps.

Proof of proposition 18 [labelling properties]

(1) Both properties are proven by induction on M . The first is immediate. We spell out the second.

x

Then L i (x) = x ∈ W 1 ⊆ W 0 . λx + .M Then L i (λx + .M ) = λx + .ℓ > L 1 (M ) and by inductive hypothesis L 1 (M ) ∈ W 1 .
Hence, ℓ > L 1 (M ) ∈ W 1 and λx + .ℓ > L 1 (M ) ∈ W 1 .

(M 1 , . . . , M n ) Then L i ((M 1 , . . . , M n )) = (L 0 (M 1 ), . . . , L 0 (M n )) and by inductive hypothesis L 0 (M j ) ∈ W 0 for j = 1, . . . , n.

Hence, (L 0 (M 1 ), . . . , L 0 (M n )) ∈ W 1 ⊆ W 0 .

π j (M ) Same argument as for the pairing.

let x = M in N Then L i (let x = M in N ) = let x = L 0 (M ) in L i (N ) and by inductive hypothesis L 0 (M ) ∈ W 0 and L i (N ) ∈ W i . Hence let x = L 0 (M ) in L i (N ) ∈ W i .
@(M 1 , . . . , M n ) and i = 0 Then L 0 (@(M 1 , . . . , M n )) = @(L 0 (M 1 ), . . . , L 0 (M n )) > ℓ and by inductive hypothesis L 0 (M j ) ∈ W 0 for j = 1, . . . , n. Hence @(L 0 (M 1 ), . . . , L 0 (M n )) > ℓ ∈ W 0 . @(M 1 , . . . , M n ) and i = 1 Same argument as in the previous case to conclude that @(L 0 (M 1 ), . . . , L 0 (M n )) ∈ W 1 .

( (3) The main point is to show that the CPS compilation of a labelled term is a term where a pre-labelling appears exactly after each λ-abstraction. The following compilation steps (value named, closure conversion, hoisting) neither destroy nor introduce new λ-abstractions while maintaining the invariant that the body of each function definition contains exactly one pre-labelling. As a preliminary step, we define a restricted syntax for the λ ℓ cps -calculus where labels occur exactly after each λ-abstraction. Let us call this language λ ℓ cps,r (r for restricted). First we remark that if V is a restricted value and M is a restricted CPS term then [V /x]M is again a restricted CPS term. Then we show the following property.

For all terms M of the λ-calculus and all continuations K of the λ ℓ cps,r -calculus the term L i (M ) | K is again a term of the λ ℓ cps,r -calculus provided that i = 0 if K is a function and i = 1 if K is a variable.

Notice that the initial continuation K 0 = λx.@(halt , x) is a functional continuation in the restricted calculus and recall that by definition C cps (L(M )) = L 0 (M ) | K 0 . We proceed by induction on M and case analysis assuming that if i = 0 then K = λy.N .

L 1 (λx + .M ) | k = λx + .ℓ > L 1 (M ) | k = @(k, λx + , k.ℓ > L 1 (M ) | k)
and we apply the inductive hypothesis on L 1 (M ) | k. @(M 1 , . . . , M n ), i = 0 We have: L i (@(M 1 , . . . , M n )) | K ≡ @(L 0 (M 1 ), . . . , L 0 (M n )) > ℓ | K ≡ @(L 0 (M 1 ), . . . , L 0 (M n )) | K ′ ≡ L 0 (M 1 ) | λx 1 . . . L 0 (M n ) | λx n .@(x 1 , . . . , x n , K ′ ) where K ′ = λy.ℓ > N . Then we apply the inductive hypothesis on M n , . . . , M 1 with the suitable functional continuations.

We consider the case for application which explains the need for the generalisation. Suppose ψ(S)I(@(M 0 , M 1 , . . . , M n )) ⇓ (m, V ). By the shape of I(@(M 0 , M 1 , . . . , M n )) this entails that ψ(S)I(M i ) ⇓ (m i , V i ) for i = 0, . . . , n. By induction hypothesis, I(SM i ) ⇓ Λ i V ′ i , costof(Λ i ) = m i , and ψ(V ′ i ) = V i , for i = 0, . . . , n. We also have @(V 0 , V 1 , . . . , V n ) ⇓ (m n+1 , V ) and m = m 0 ⊕ • • • ⊕ m n+1 . Since ψ(V ′ 0 ) = V 0 , this requires V ′ 0 = λx 1 • • • x n .M ′ and V 0 = λx 1 • • • x n .I(M ′ ). So we must have [V 1 /x 1 , . . . , V n /x n ]I(M ′ ) ⇓ (m n+1 , V ). Again by inductive hypothesis, this entails that I([V ′ 1 /x 1 , . . . , V ′ n /x n ]M ′ ) ⇓ Λ n+1 V ′ , costof(Λ n+1 ) = m n+1 , and ψ(V ′ ) = V . We conclude that I(S(@(M 0 , M 1 , . . . , M n )))

⇓ Λ V ′ with Λ = Λ 0 • • • Λ n+1 . 2

Proof of proposition 22 [subject reduction]

The proof of this result is standard, so we just recall the main steps.

1. Prove a weakening lemma: Γ ⊢ M : A implies Γ, x : B ⊢ M : A for x fresh. 14 are driven by the syntax of the term. Then the property is checked by case analysis while appealing, for the first two rewriting rules, to the substitution properties mentioned above.

Proof of proposition 23 [type CPS]

First, we prove the following properties at once by induction on the structure of the term (possibly a value).

1. If Γ ⊢ V : A then C cps (Γ) ⊢ ψ(V ) : C cps (A). We illustrate the analysis for the cases of abstraction and application.

Abstraction Suppose Γ ⊢ λx + .M : A + → B is derived from Γ, x + : A + ⊢ M : B. We prove the 3 properties above. Then by weakening and substitution we derive that:

C cps (Γ), Γ ′ ⊢ [ψ(λx + .M )/y]N : R .

Application Suppose Γ ⊢ @(M 0 , . . . , M n ) : B is derived from Γ ⊢ M 0 : A, A ≡ A 1 , . . . , A n → B, and Γ ⊢ M i : A i for i = 1, . . . , n. In this case, we just look at the last two properties since an application cannot be a value.

(2) Clearly: C cps (Γ), Γ ′ , x n : C cps (A n ) ⊢ @(x 0 , x 1 , . . . , x n , k) : R , where Γ ′ ≡ x 0 : C cps (A), . . . , x n-1 : C cps (A n-1 ), k : ¬C cps (B). By induction hypothesis (property 3) on M n , we derive:

C cps (Γ), Γ ′ ⊢ (M n : λx n .@(x 0 , x 1 , . . . , x n , k)) : R .

Then by applying the inductive hypothesis (property 3) on M n-1 , . . . , M 0 we obtain:

C cps (Γ), k : ¬C cps (B) ⊢ (M 0 | λx 0 . • • • M n | λx n .@(x 0 , x 1 , . . . , x n , k)) : R .

(3) Suppose additionally that C cps (Γ), Γ ′′ , y : C cps (B) ⊢ N : R. Then we have:

C cps (Γ), Γ ′ , Γ ′′ , x n : C cps (A n ) ⊢ @(x 0 , x 1 , . . . , x n , λy.N ) : R , where Γ ′ ≡ x 0 : C cps (A), . . . , x n-1 : C cps (A n-1 ). Then proceed as in the previous case by applying the inductive hypothesis (property 3) on M n , . . . , M 0 .

The proof of the omitted cases follows a similar pattern. Now to derive proposition 23, recall that C cps (M ) ≡ M | λx.@(halt , x). Then we obtain the desired statement from the property 3 above observing that if Γ ⊢ M : A and C cps (Γ), halt : ¬C cps (A), x : C cps (A) ⊢ @(halt , x) : R then C cps (Γ), halt : ¬C cps (A) ⊢ C cps (M ) : R.

V ≡ (x * , V ′ , V * ), V ′ = id . Suppose Γ, y : A ⊢ vn N and Γ ⊢ (x * , V ′ , V * ) : A. This entails Γ ⊢ V ′ : B for some type B and Γ, z : B ⊢ (x * , z, V * ) : A. By weakening and inductive hypothesis (property 2) on (x * , z, V * ) we derive Γ, z : B ⊢ vn E vn ((x * , z, V * ), y)[N ]. Then by inductive hypothesis on V ′ (again by property 2) we derive:

Γ ⊢ vn E vn (V ′ , z)[E vn ((x * , z, V * ), y)[N ]] ,
and this last term equals E vn ((x * , V ′ , V * ), y)[N ].

Proof of proposition 26 [type closure conversion]

By induction on the typing of Γ ⊢ vn M according to the rules specified in Table 15. We detail the cases of abstraction and application.

Abstraction Suppose Γ ⊢ vn let x = λy + .M in N is derived from Γ, y + : A + ⊢ vn M and Γ, x : A + → R ⊢ vn N . Let us pose {z * } = fv(λy + .M ). Then for some C * we have z * : C * ∈ Γ. We have to show that: Application Suppose Γ ⊢ vn @(x, y + ) is derived from x : A + → R, y + : A + ∈ Γ. We have to show: C cc (Γ) ⊢ vn let x ′ = π 1 (x) in let (c, e) = x ′ in @(c, e, y + ) .

Since C cc (A + → R) ≡ ∃t. × ((t, C cc (A) + → R), t), the judgement above is derived from:

C cc (Γ), x ′ : ×((t, C cc (A) + → R), t), c : (t, C cc (A) + → R), e : t ⊢ vn @(c, e, y + ) .

Proof of proposition 27 [type hoisting]

First, we show the following property.

Strengthening If Γ, x : A ⊢ vn P and x / ∈ fv(P ) then Γ ⊢ vn P .

Then we proceed by case analysis (3 cases) on the hoisting transformations specified in Table 7. They all have the shape D[∆] ; D[∆ ′ ], so it suffices to show that if Γ ⊢ vn ∆ then Γ ⊢ vn ∆ ′ . We detail the analysis for the transformation (h 2 ). Suppose: Γ ⊢ vn let x = λw + .let y = λz + .T in M in N,

  let c = λe, x, k.(let (y) = e, (c, e) = k in @(c, e, y)) in let e = (y), z 1 = (c, e), (c, e) = halt in @(c, e, z 1 ) . Proposition 10 (CC commutation) Let M be a CPS term in value named form. Then er (C cc (M )) ≡ C cc (er (M )). Proposition 11 (CC simulation) Let M be a CPS term in value named form. If M

01

  type list = Nil | Cons (nat, list) 02 type bool = BTrue | BFalse 03 logic { 04 Definition bound (p : nat -→ (nat * bool)) (k : nat) : Prop : = 05 ∀ x m : nat, ∀ r : bool, post p x (m, r) ⇒ m ≤ k. 06 Definition k0 : = costof(ℓm) + costof(ℓ nil ). 07 Definition k1 : = costof(ℓm) + costof(ℓp) + costof(ℓc) + costof(ℓ f ) + costof(ℓr). 08 } 09 let rec pexists (p : nat → bool, l : list) { ∀ x, pre p x } : bool { 10 ((result = BTrue) ⇔ (∃ x c : nat, mem x l ∧ post p x (c, BTrue))) ∧ 11 (∀ k : nat, bound p k ∧ (result = BFalse) ⇒ cost ≤ k0 + (k + k1) × length (l)) 12 } = ℓm> match l with 13 | Nil → ℓ nil > BFalse 14 | Cons (x, xs) → ℓc> match p (x) > ℓp with 15 | BTrue → BTrue 16 | BFalse → ℓ f > (pexists (p, xs) > ℓr)

  {r} --→ R and it cannot match the type of the pair allocated by the function pair in a new region. If we fix this problem by abstracting the function prj1 w.r.t. a region so that it has the type ∀r. × (t 1 , . . .)at(r) {r} --→ R we stumble on the main Types and effects syntax e ::= {rid , . . . , rid } (effects) A ::= tid | | ∀rid * .A + e -→ R | | ×() | | ×(A + )at(rid ) | | (∃tid .A)at(rid ) (types) Typing rules Γ, y + : A + ⊢ rg T : e {r * } ∩ frv(Γ) = ∅ frv(λr * , y + .T ) = ∅ Γ ⊢ rg λr * , y + .T : ∀r * .A + e -→ R B ≡ ∀r * 1 .A + e -→ R x : B ∈ Γ y + : [r * /r *problem, namely the function pair disposes a region which is used in the continuation; this is forbidden by the typing rule for region disposal.

  ℓ > M Last case for A. We have:R(C vn (ℓ > M )) ≡ R(ℓ > C vn (M )) ≡ ℓ > R(C vn (M )) ≡ ℓ > M(by ind. hyp. on A) .λy + .M We now turn to case B. We have:R(E vn (λy+ .M, x)[N ]) ≡ R(let x = λy + .C vn (M ) in N ) ≡ [R(λy + .C vn (M ))/x]R(N ) ≡ [λy + .R(C vn (M ))/x]R(N ) ≡ [λy + .M/x]R(N ) (by ind. hyp. on A) . (y * ) Again in case B. We have: R(E vn ((y * ), x)[N ]) ≡ R(let x = (y * ) in N ) ≡ [(y * )/x]R(N ) .

  R(E vn ((y * , z, V * ), x)[N ]) (by ind. hyp. on B) ≡ [V /z]([(y * , z, V * )/x]R(N )) (by ind. hyp. on B) ≡ [(y * , V, V * )/x]R(N ) .

  in er (C vn (M ))] (by ind. hyp. on B)≡ E vn (er (V ), z)[let x = π i (z) in C vn (er (M ))] (by ind. hyp. on A) ≡ C vn (er (let x = π i (V ) in M )) .ℓ > M Last case for A. We have:er (C vn (ℓ > M )) ≡ er (ℓ > C vn (M )) ≡ er (C vn (M )) ≡ C vn (er (M )) (by ind. hyp. on A) ≡ C vn (er (ℓ > M )) .λy + .M We now turn to case B. We have: er (E vn (λy + .M, x)[N ]) ≡ er (let x = λy + .C vn (M ) in N ) ≡ let x = λy + .er (C vn (M )) in er (N ) ≡ let x = λy + .C vn (er (M )) in er (N ) (by ind. hyp. on A) ≡ E vn (er (λy + .M ), x)[er (N )] . (y * ) Again in case B. We have: er (E vn ((y * ), x)[N ]) ≡ er (let x = (y * ) in N ) ≡ let x = (y * ) in er (N ) ≡ E vn (er ((y * )), x)[er (N )] .

  er (M )) (by ind. hyp.) ≡ C cc (er (let x = C in M )) .let x = λx + .N in M, fv(λx + .N ) = {z 1 , . . . , z k } We have: er (C cc (let x = λx + .N in M )) ≡ er ( let c = λe, x + .let (z 1 , . . . , z k ) = e in C cc (N ) in let e = (z 1 , . . . , z k ), x = (c, e) in C cc (M ) ) ≡ let c = λe, x + .let (z 1 , . . . , z k ) = e in er(C cc (N )) in let e = (z 1 , . . . , z k ), x = (c, e) in er (C cc (M )) ≡ let c = λe, x + .let (z 1 , . . . , z k ) = e in C cc (er (N )) in let e = (z 1 , . . . , z k ), x = (c, e) in C cc (er (M )) (by ind. hyp.) ≡ C cc (er (let x = λx + .N in M )) . ℓ > M We have: er(C cc (ℓ > M )) ≡ er(ℓ > C cc (M )) ≡ er(C cc (M ))≡ C cc (er (M )) (by ind. hyp.) ≡ C cc (er (ℓ > M )) .2

  let c = λe, x + .let (z 1 , . . . , z k ) = e in C cc (M ) in let e = (z 1 , . . . , z k ), x = (c, e) in C cc (E) where: fv(λx + .M ) = {z 1 , . . . , z k } .

  where E(x) = λx + .M and fv(λx + .M ) = {z 1 , . . . , z k }. Then:C cc (E[@(x, y + )]) ≡ C cc (E)[let (c, e) = x in @(c, e, y + )] with C cc (E)(x) = (c, e), C cc (E)(c) = λe, x + .let (z 1 , . . . , z k ) = e in C cc(M ) and C cc (E)(e) = (z 1 , . . . , z k ). Therefore:

2 . 3 .

 23 The hoisting transformations are confluent (hence the result of the hoisting transformations is unique). If a term M of the λ ℓ cps,vn -calculus contains a function definition then M ≡ D[let x = λy + .T in N ] for some D, T, N . 4. All terms in λ ℓ cc,vn belong to the set I (trivially).

  let y = C in M Then M must contain a function definition. Then by inductive hypothesis, M ≡ D ′ [F ]. We conclude by taking D ≡ let y = C in D ′ . let y = λx + .M ′ in M If M is a restricted term then we take D ≡ [ ]. Otherwise, M ′ must contain a function definition and by inductive hypothesis, M ′ ≡ D ′ [F ]. Then we take D ≡ let y = λx + .D ′ in M . ℓ > M Then M contains a function definition and by inductive hypothesis M ≡ D ′ [F ]. We conclude by taking D ≡ ℓ > D ′ .

  ) By[START_REF] Aiken | Better static memory management: improving region-based analysis of higherorder languages[END_REF] we know that er (L(M )) ≡ M and L(M ) ∈ W 0 . Then:P ≡ C(M ) ≡ C(er (L(M )))≡ er (C(L(M ))) (by theorem 16(1)) .

V

  ::= id | | λid + .ℓ > M | | (V * ) (restricted values) M ::= @(V, V + ) | | let id = π i (V ) in M (restricted CPS terms) K ::= id | | λid .M (restricted continuations)

x, i = 0

 0 We have:L 0 (x) | K = x | K = [x/y]N .x, i = 1 We have:L i (x) | k = x | k = @(k, x). λx + .M , i = 0 We have: L 0 (λx + .M ) | K = λx + .ℓ > L 1 (M ) | K = [λx + , k.ℓ > L 1 (M ) | k/y]Nand we apply the inductive hypothesis on L 1 (M ) | k and closure under value substitution. λx + .M , i = 1 We have:

2 .

 2 Prove a substitution lemma: Γ, x : A ⊢ M : B and Γ ⊢ N : A implies Γ ⊢ [N/x]M : B; by induction on the proof of M . 3. Derive by iteration the following substitution lemma: Γ, x 1 : A 1 , . . . , x n : A n ⊢ M : B and Γ ⊢ N i : A i for i = 1, . . . , n implies Γ ⊢ [N 1 /x 1 , . . . , N n /x n ]M : B.

4 . 5 .

 45 With reference to Table2, notice that in an evaluation context one does not cross any binder. Then we have thatE[M ] ≡ [M/x]E[x] for x fresh variable. Moreover, if Γ ⊢ E[M ] :A then for some B, Γ ⊢ M : B. Now examine the 5 possibilities for reduction specified in Table2. They all have the shape E[∆] → E[∆ ′ ]. By the previous remark, it suffices to show that if Γ ⊢ ∆ : B then Γ ⊢ ∆ ′ : B. Note that the typing rules in Table

2 .

 2 If Γ ⊢ M : A then C cps (Γ), k : ¬C cps (A) ⊢ (M | k) : R. 3. If Γ ⊢ M : A and C cps (Γ), Γ ′ , x : C cps (A) ⊢ N : R then C cps (Γ), Γ ′ ⊢ (M | (λx.N )) : R.

( 1 )

 1 By induction hypothesis (property 2), we know:C cps (Γ), x + : C cps (A) + , k : ¬C cps (B) ⊢ (M | k) : R .Then, recalling that:ψ(λx + .M ) ≡ λx + , k.(M | k) and C cps (A + → B) = C cps (A) + , ¬C cps (B) → R ,we derive:C cps (Γ) ⊢ ψ(λx + .M ) : C cps (A + → B) .

( 2 )( 3 )

 23 Recall that (λx + .M ) | k ≡ @(k, ψ(λx + .M )). By property 1, we derive:C cps (Γ) ⊢ ψ(λx + .M ) : C cps (A + → B)Then by weakening and substitution we deriveC cps (Γ), k : ¬C cps (A + → B) ⊢ ψ(λx + .M ) : C cps (A + → B)Finally the application rule gives C cps (Γ), k : ¬C cps (A + → B) ⊢ ((λx + .M ) : k) : R . Suppose additionally that C cps (Γ), Γ ′ , y : C cps (A + → B) ⊢ N : R. Recall that (λx + .M | λy.N ) ≡ [ψ(λx + .M )/y]N . By property 1, we know that: C cps (Γ) ⊢ ψ(λx + .M ) : C cps (A + → B) .

C

  cc (Γ) ⊢ vn let c = λe, y + .let (z * ) = e in C cc (M ) in let e = (z * ) in let x ′ = (c, e) in let x = (x ′ ) in C cc (N ) .By inductive hypothesis on M , variable substitution, and weakening we derive:C cc (Γ), Γ ′ ⊢ vn C cc (M ), with Γ ′ ≡ c : ×(C * ), C cc (A) + → R, e : ×(C * ).Also, by inductive hypothesis on N and weakening we derive:C cc (Γ), Γ ′ , Γ ′′ ⊢ vn C cc (N ) ,with Γ ′′ ≡ x ′ : [×(C * )/t]B, x : ∃t.B and B ≡ ×((t, C cc (A) + → R), t).

Table 2 :

 2 An ordinary call-by-value λ-calculus: λ ℓ through the intermediate term let x = V 1 in M which does not belong to the CPS terms.

Table 3

 3

	: CPS λ-calculus (λ ℓ cps ) and CPS translation
	Example 6 (value named form) Suppose

Table 4 :

 4 A value named CPS λ-calculus: λ ℓ

	cps,vn

Table 5 :

 5 Transformations in value named CPS form and readback

Table 10 :

 10 The surface language.

Table 11 :

 11 A function that concatenates two lists, and its cost annotation.

	routine x19 (c20, x7)
	ℓrec:
	k2 ← proj 1 c20 ;
	x ← proj 2 c20 ;
	x14 ← make int 1 ;
	x15 ← make tuple (x14, x, x7) ;
	x22 ← proj 0 k2 ;
	call x22 (k2, x15)
	routine x16 (c17, l1, l2, k2)
	ℓ match :
	switch l1
	0 : ℓ nil :
	x18 ← proj 0 k2 ;
	call x18 (k2, l2)
	1 : ℓcons :
	xs ← proj 2 l1 ;
	x ← proj 1 l1 ;
	x13 ← make tuple (x19, k2, x) ;
	x21 ← proj 0 c17 ;
	call x21 (c17, xs, l2, x13)

Table 12 :

 12 The compiled code of concat.

Table 15 :

 15

Type system for the value named calculi and closure conversion Proposition 26 (type closure conversion) If M is a term in λ ℓ cps,vn and Γ ⊢ vn M then C cc (Γ) ⊢ vn C cc (M ).

Table 17 :

 17 The region-enriched calculus: λ ℓ,r

	h,vn

Syntax typesA ::= tid | | A + → A | | ×(A * ) (types)Typing rulesx : A ∈ Γ Γ ⊢ x : A Γ, x : A ⊢ N : B Γ ⊢ M : A Γ ⊢ let x = M in N : B Γ, x + : A + ⊢ M : B Γ ⊢ λx + .M : A + → B Γ ⊢ M : A + → B Γ ⊢ N + : A + Γ ⊢ @(M, N + ) : B Γ ⊢ M * : A * Γ ⊢ (M * ) : ×(A * ) Γ ⊢ M : ×(A1, . . . , An) 1 ≤ i ≤ n Γ ⊢ πi(M ) : Ai

]A + ∈ Γ r * distinct frv(B) = ∅ Γ ⊢ rg @(x, r * , y + ) : [r * /r * 1 ]e x + : A + ∈ Γ Γ ⊢ rg (x + )at(r) : ×(A + )at(r) y : ×(A1, . . . , An)at(r) ∈ Γ 1 ≤ i ≤ n Γ, x : Ai ⊢ rg T : e Γ ⊢ rg let x = πi(y) in T : e ∪ {r} x : [B/t]A ∈ Γ Γ ⊢ rg (x)at(r) : (∃t.A)at(r) y : (∃t.A)at(r) ∈ Γ Γ, x : A ⊢ rg T : e t / ∈ ftv(Γ) Γ ⊢ rg let x = π1(y) in T : e ∪ {r} Γ ⊢ rg() : ×() Γ ⊢ rg () : A Γ, x : A ⊢ rg T : e Γ ⊢ rg let x = () in T : e Γ ⊢ rg λr * , y + .T : A Γ, x : A ⊢ rg P : e Γ ⊢ rg let x = λr * , y + .T in P : e Γ ⊢ rg (y + )at(r) : A Γ, x : A ⊢ rg T : e Γ ⊢ rg let x = (y + )at(r) in T : e ∪ {r} Γ ⊢ rg T : e ∪ {r} r / ∈ frv(Γ), e Γ ⊢ rg let all(r) in T : e Γ ⊢ rg T : e r / ∈ e

Acknowledgements The authors would like to thank the anonymous reviewers for their valuable comments and suggestions that significantly helped to improve this paper, as well as Anindya Banerjee and Olivier Danvy for their efficient work in the edition process.

The authors were supported by the Information and Communication Technologies (ICT) Programme as Project FP7-ICT-

(2) We show that er (M ) ; entails that M ;. Since er (M ) has no labels, either (h 1 ) or (h 2 ) apply. Then M is a term that is derived from er (M ) by inserting (possibly empty) sequences of pre-labelling before each subterm. We check that either the hoisting transformation applied to er (M ) can be applied to M too or (h 3 ) applies.

(3) If C h (M ) ≡ N then by definition we have M ; * N ;. By ( 1) er (M ) ; * er (N ), and by ( 2) er (N ) ;. Hence C h (er (M )) ≡ er (N ) ≡ er (C h (M )).

2

Proof of proposition 15 [hoisting simulation]

Definition 40 A (strong) simulation on the terms of the λ ℓ cps,vn -calculus is a binary relation

Definition 41 A (pre-)congruence on the terms of the λ ℓ cps,vn -calculus is an equivalence relation (a pre-order) which is preserved by the operators of the calculus.

Definition 42 Let ≃ be the smallest congruence on terms of the λ ℓ cps,vn -calculus which is induced by structural equivalence and the following commutation of let-definitions:

The relation ≃ is preserved by name substitution and it is a simulation.

Definition 43 Let the smallest pre-congruence on terms of the λ ℓ cps,vn -calculus which is induced by structural equivalence and the following collapse of let-definitions:

The relation is preserved by name substitution and it is a simulation.

Definition 44 Let S h be the relation ≃ • .

Note that S h is a simulation too. Then we can state the main lemma.

Lemma 45 Let M be a term of the λ ℓ cps,vn -calculus. If M α -→ M ′ and M ; N then there is

Proof. As a preliminary remark we notice that the hoisting transformations are preserved by name substitution. Namely if M ; N then [y + /x + ]M ; [y + /x + ]N .

There are three reduction rules and three hoisting transformations hence there are 9 cases to consider and for each case we have to analyse how the two redexes can superpose.

As usual a term can be regarded as a tree and an occurrence in the tree is identified by a path π which is a sequence of natural numbers. @(M 1 , . . . , M n ), i = 1 We have:

Again we apply the inductive hypothesis on M n , . . . , M 1 with the suitable functional continuations.

(M 1 , . . . , M n ) We have:

We apply the inductive hypothesis on M n , . . . , M 1 with the suitable functional continuations.

π j (M ) We have:

We apply the inductive hypothesis on M with a functional continuation.

We apply the inductive hypothesis on M and then on N with a functional continuation. 2

Proof of proposition 20 [instrumentation vs. labelling]

As a preliminary step, we show that for all terms M and values V, V ′ of the λ ℓ -calculus the following (mutually dependent) properties hold.

We prove the following generalisation of the proposition.

For all terms M and substitutions S, if ψ(S)I(M ) ⇓ (m, V ) then

We proceed by induction on the length of the derivation of the judgement ψ(S)I(M ) ⇓ (m, V ) and case analysis on M .

Proof of proposition 24 [subject reduction, value named]

First, we prove some standard properties for the type system described in Table 15.

Weakening If Γ ⊢ vn M then Γ, x : A ⊢ vn M with x fresh.

Variable substitution If Γ, x : A ⊢ vn M and y : A ∈ Γ then Γ ⊢ vn [y/x]M . This property generalizes to Γ, x + : A + ⊢ vn M and y + :

Next, suppose Γ ⊢ vn M and M → N according to the rules specified in Table 4. This means M ≡ E[∆] where for some Γ ′ , we have Γ, Γ ′ ⊢ vn ∆ and ∆ is either an application, or a projection or a labelling. We consider each case in turn. ∆ ≡ @(x, y + ). Then y + : ∆ ≡ let x = π i (y) in M ′ . This case splits in two sub-cases: the first for product types and the second for existential types.

, and for some z, B, we have z :

Proof of proposition 25 [type value named]

We prove at once the following two properties:

We proceed by induction on the structure of M and V along the pattern of the definition of the value named translation in Table 5. We spell out two typical cases.

M ≡ @(x * , V, V * ), V = id . Suppose Γ ⊢ @(x * , V, V * ) : R. This entails Γ ⊢ V : A for some type A. We also have Γ, y : A ⊢ @(x * , y, V * ) : R and by inductive hypothesis (property 1) Γ, y : A ⊢ vn C vn (@(x * , y, V * )). Then, we apply the inductive hypothesis on V (property 2) to derive that: Γ ⊢ vn E vn (V, y)[C vn (@(x * , y, V * ))], and this last term equals C vn (@(x * , V, V * )).

Then we derive:

as follows:

(1 ′ ) Γ, x : A + → R, y : B + → R ⊢ vn N (by (1) and weakening) (2 ′ ) Γ, z + : B + ⊢ vn T (by (2) and strengthening) (3 ′ ) Γ, w + : A + , y :

(by (3)) .

Proof theorem 28 [type preserving compilation]

Suppose M term of the λ ℓ -calculus and Γ ⊢ M : A. Then:

Next recall that the compiled term C(M ) is the result of iterating the hoisting transformations on the term C cc (C vn (C cps (M ))) a finite number of times. Hence, by proposition 27 we conclude:

Proof of proposition 31 [decomposition]

With reference to Table 17, we know that a program P is a list of function definitions, determining the function context F , followed by a term T . The latter is a list of value definitions and region allocations and disposals, determining the heap context H, and ending either in an application or a projection or a labelling. This last part of the program corresponds to the redex ∆.

Proof of proposition 35 [region erasure]

First, we notice that the region erasure function is invariant under region substitutions: rer ([r ′ /r]A) = rer (A). Then we prove at once the following two properties where it is intended that the judgements on the left are derivable in the type and effect system described in Table 18 and the ones on the right in the type system described in Table 15.

1. If Γ ⊢ rg P : e then rer (Γ) ⊢ vn rer (P ).

If Γ ⊢ rg

We detail the cases of abstraction and application.

Abstraction Suppose Γ ⊢ rg λr * , y + .T : ∀r * .A + e -→ R is derived from Γ, y + : A + ⊢ rg T : e. Then by inductive hypothesis, rer (Γ), y + : rer (A) + ⊢ vn rer (T ). And we conclude: rer (Γ) ⊢ vn λy + .T : rer (A) + → R as required.

Then, by the invariance property of the region erasure function noticed above, x : rer (A) + → R, y + : rer (A) + ∈ rer (Γ). So we conclude: rer (Γ) ⊢ vn @(x, y + ) as required.

Proof of proposition 36 [region enrichment]

We define a region enrichment function ren from the programs of the λ ℓ h,vn -calculus to those of the λ ℓ,r h,vn -calculus. With reference to Table 7, we recall that a program P of the λ ℓ h,vn is composed of a list of function definitions and a term. Thus P is decomposed uniquely as F [T ] where F is a functional context defined as follows

We fix one region variable r and define the region enrichment function relatively to it as follows:

The intuition is that a region r is created initially and never disposed, that all tuples are allocated in this region, and that at every function call we pass this region as a parameter. Then all functions when applied will produce (at most) an effect {r}.

Next we extend the region enrichment function to types as follows:

= ×(ren(A) + )at(r) ren((∃t.A))

= (∃t.ren(A))at(r)

We notice that function definitions are region closed and so are the functional types in the image of the function ren. Let us denote with Γ 0 a type context such that if x : A ∈ Γ 0 then A is not a type of the shape ×(B + ) or ∃t.B. It follows that frv(ren(Γ 0 )) = ∅.

We show the following enrichment property:

If Γ 0 , Γ ⊢ vn T then ren(Γ 0 , Γ) ⊢ rg ren(T ) : {r}.

We detail three cases.

Tuple construction Suppose Γ 0 , Γ ⊢ vn let x = (y + ) in T is derived from Γ 0 , Γ ⊢ vn (y + ) : A + and Γ 0 , Γ, x : ×(A + ) ⊢ vn T .

Then we derive:

ren(Γ 0 , Γ) ⊢ rg let x = (y + )at(r) in ren(T ) : {r} from:

ren(Γ 0 , Γ) ⊢ rg (y + )at(r) : ×(ren(A) + )at(r) and ren(Γ 0 , Γ), x : ×(ren(A) + )at(r) ⊢ rg ren(T ) : {r} (inductive hypothesis).

Projection Suppose Γ 0 , Γ ⊢ vn let x = π i (y) in T is derived from y : ×(A 1 , . . . , A n ) ∈ Γ, 1 ≤ i ≤ n, and Γ 0 , Γ, x : A i ⊢ vn T . Then: y : ×(ren(A 1 ), . . . , ren(A n ))at(r) ∈ ren(Γ) and ren(Γ 0 , Γ, x : A i ) ⊢ vn ren(T ) : {r} .

Hence: ren(Γ 0 , Γ) ⊢ vn ren(let x = π i (y) in T ) : {r} .

Application Suppose Γ 0 , Γ ⊢ vn @(x, y + ) is derived from x : A + → R, y + : A + ∈ Γ 0 , Γ. Then we derive ren(Γ 0 , Γ) ⊢ rg @(x, r, y + ) : {r} from:

x : ∀r.ren(A) + {r} --→ R, y + : ren(A) + ∈ ren(Γ 0 , Γ) .

Finally, we derive from the enrichment property above the following two properties which suffice to derive the statement:

• If Γ 0 ⊢ vn T then ren(Γ 0 ) ⊢ rg let all(r) in ren(T ) : ∅.

Proof of proposition 37 [progress]

First we prove by induction on the structure of a heap context the following monotonicity property of the coherence predicate: if Coh(H, L) and L ⊆ L ′ then Coh(H, L ′ ).

Let frv(H) denote the set of region variables free in a heap context. If the program P ≡ F [H[∆]] is typable then a judgement of the form Γ ⊢ rg H[∆] : e is derivable. We show by induction on the typing of such judgement the following two properties:

Because we assumed frv(P ) = ∅ we must have frv(H) = ∅ and by the first property we derive Coh(H, ∅). In other terms, in a typable program without free region variables the heap context is coherent relatively to the empty set. We look at the shape of ∆.

Labelling If ∆ is a labelling then the program may reduce.

Application If ∆ is an application @(x, r * , y + ) then either the variable x is not bound in the function context or it is bound to a function value. The fact that the number of parameters matches the number of arguments is forced (as usual) by typing. Then a reduction is possible.

Projection The last case is when the redex is a projection let x = π i (y) in T . Similarly to the previous case, either y is not bound or it is bound to a tuple allocated at a region r. Then we must be able to type a term of the shape:

The fact that the projection is in the right range is forced (as usual) by typing. To fire the transition we need to check that NDis(r, H) holds. In fact let us argue that if the predicate does not hold then the judgement (1) above cannot be typed. By inspecting the definition of NDis(r, H) we see that for the predicate to fail, H must have the shape

] for a heap context H 1 which contains neither allocations nor disposals on the region r. But then H 2 [let x = π i (y) in T ] must produce a visible effect on r. Indeed the typing system records an effect on r when projecting y and this effect cannot be hidden by an allocation because the region variable r is free in the context Γ, y : (A)at(r). Then the typing of dis(r) in [H 2 [let x = π i (y) in T ]] fails because the typing forbids disposing a region which is in the effect of the continuation.

Proof of proposition 38 [subject reduction, types and effects]

First we prove some standard properties (cf. proof of proposition 24) and a specific property on injective region substitutions.

Weakening If Γ ⊢ rg P : e then Γ, x : A ⊢ rg P : e with x fresh.

Variable substitution If Γ, x : A ⊢ rg P : e and y : A ∈ Γ then Γ ⊢ rg [y/x]P : e.

Type substitution If Γ ⊢ rg P : e then [B/t](Γ) ⊢ rg P : e.

Injective region substitution If Γ ⊢ rg T : e and σ is a (finite domain) region substitution which is injective on frv(T ) ∪ e then σΓ ⊢ rg σT : σe.

We detail the proof of the last property which proceeds by induction on the typing proof of Γ ⊢ rg T : e.

Application Suppose Γ ⊢ rg @(x, r * , y + ) : [r * /r * 1 ]e is derived from x : B, y

1 ]e = {r * }. So suppose σ is an injective substitution on r * so that r ′ * = (σr) * . We remark:

Then we can prove σΓ ⊢ rg σ@(x, r * , y + ) : σ([r * /r * 1 ]e) by the typing rule for application.

Unit Suppose Γ ⊢ rg let x = () in T : e is derived from Γ, x : ×() ⊢ rg T : e and σ is injective on frv(let x = () in T ) ∪ e. Then σ is injective on frv(T ) ∪ e, by inductive hypothesis σΓ, x : ×() ⊢ rg σT : σe, and we conclude σΓ ⊢ rg σ(let x = () in T ) : σe.

Product Suppose Γ ⊢ rg let x = (y + )at(r) in T : e ∪ {r} is derived from Γ, x : ×(A + )at(r) ⊢ rg T : e, y + : A + ∈ Γ and σ is injective on the set:

Then σ is injective on frv(T ) ∪ e. By inductive hypothesis: σΓ, x : (×(σA + ))at(σr) ⊢ rg σT : σe . We conclude: σΓ ⊢ rg σ(let x = π i (y) in T ) : σe ∪ {σr} .

The case where y has an existential type is similar.

Disposal Suppose Γ ⊢ rg dis(r) in T : e ∪ {r} is derived from Γ ⊢ rg T : e, r / ∈ e, and σ is injective on frv(dis(r) in T ) ∪ e ∪ {r}. Then σ is injective on frv(T ) ∪ e and by inductive hypothesis σΓ ⊢ rg σT : σe. Also σr / ∈ σe. We conclude:

σΓ ⊢ rg σ(dis(r) in T ) : σe ∪ {σr} .

Allocation Suppose Γ ⊢ rg let all(r) in T : e is derived from Γ ⊢ rg T : e ∪ {r}, r / ∈ e ∪ frv(Γ), and σ is injective on frv(let all(r) in T ) ∪ e. Up to renaming, we can choose r so that it is not in the domain or image of σ. Then σ is injective on frv(T ) ∪ e ∪ {r} and by inductive hypothesis σΓ ⊢ rg σT : σe ∪ {σr}. Also, by the choice above, σr / ∈ σe ∪ σΓ. We conclude σΓ ⊢ rg σ(let all(r) in T ) : σe.

Labelling Suppose Γ ⊢ rg ℓ > T : e is derived from Γ ⊢ rg T : e and σ is injective on frv(ℓ > T )∪{e}. By inductive hypothesis σΓ ⊢ rg σT : σe and we conclude σΓ ⊢ rg σ(ℓ > T ) : σe.

Subeffect Suppose Γ ⊢ rg P : e is derived from Γ ⊢ rg P : e ′ , e ′ ⊆ e, and σ is injective on frv(P ) ∪ e. By inductive hypothesis σΓ ⊢ rg σP : σe ′ and we conclude σΓ ⊢ rg σP : σe.

If Γ P ⊢ rg P : e P then we know that P ≡ F [H[∆]] and the reduced term has the shape

For some Γ we have Γ ⊢ rg ∆ : e ′ . We show that then Γ ⊢ rg ∆ ′ : e ′ and frv(∆ ′ ) ⊆ frv(∆). Then we claim that the typing proof for the surrounding context F

We proceed by case analysis on the reduction rule applied and its typing. Notice that the typing is syntax directed except for the subeffect rule. So for instance, if Γ ⊢ rg ∆ : e ′ and ∆ is an application then for some e ′′ ⊆ e ′ we can derive Γ ⊢ rg ∆ : e ′′ where the last rule being applied is the one for application. A similar argument holds for the cases where ∆ is a projection or a labelling.

Application Suppose Γ ⊢ rg @(x, r * , y + ) : e ′′ with e ′′ = [r * /r * 1 ]e is derived from x : B, y + : [r * /r * 1 ]A + ∈ Γ, B ≡ ∀r 1 .A + e -→ R, frv(B) = ∅, r * distinct variables. Since the program reduces, x must be bound to a region closed function λr * 1 , z + .T in the functional context F and Γ 1 , z + : A + ⊢ rg T : e where Γ 1 is a prefix of Γ and {r * 1 } ∩ frv(Γ 1 ) = ∅. We notice that the substitution σ = [r * /r * 1 ] is injective on frv(T ) ∪ e ⊆ {r * 1 }, hence by the injective substitution property we derive: Γ 1 , z + : (σA) + ⊢ rg σT : σe .

Notice that we must have y + : (σA) + ∈ Γ 1 hence by the variable substitution property and weakening we derive:

We conclude by noticing that:

frv([r * /r * 1 , y + /z + ]T ) ⊆ frv(@(x, r * , y + )) = {r * } .

Projection Suppose Γ ⊢ rg let x = π i (y) in T : e ∪ {r} is derived from y : ×(A 1 , . . . , A n ) ∈ Γ, 1 ≤ i ≤ n Γ, x : A i ⊢ rg T : e. Since the program reduces, y must be bound to a tuple (z 1 , . . . , z n )at(r) in the heap context H and z 1 : A 1 , . . . , z n : A n ∈ Γ. Then by variable substitution we derive Γ ⊢ rg [z i /x]T : e. Also notice that frv([z i /x]T ) = frv(let x = π i (y) in T ).

The case where y has an existential type is similar except that it relies on type substitution too (cf. proof of proposition 24).

Labelling Suppose Γ ⊢ rg ℓ > T : e is derived from Γ ⊢ rg T : e. Since ∆ ≡ ℓ > T ℓ -→ T , the conclusion is immediate.

Proof of theorem 39 [region simulation]

First, we observe that the region erasure function commutes with variable substitution: [x/y]rer (T ) ≡ rer ([x/y]T ) .

By proposition 31, P decomposes as F [H[∆]

] where ∆ is either an application, or a projection, or a labelling. The region erasure function commutes with this decomposition too, so that we can write rer (P ) as rer (F )[rer (H)[rer (∆)]], where rer (F )[rer (H)] is an evaluation context. If rer (P ) α -→ Q then we proceed by case analysis on the reduction rule being applied. We detail the case where ∆ is an application @(x, r * , y + ). Then we must have F ≡