verified compilers for a multi-language world

17
Verified Compilers for a Multi-Language World * Amal Ahmed Northeastern University [email protected] Abstract Though there has been remarkable progress on formally verified compilers in recent years, most of these compilers suffer from a serious limitation: they are proved correct under the assumption that they will only be used to compile whole programs. This is an unrealistic assumption since most software systems today are comprised of components written in different languages—both typed and untyped—compiled by different compilers to a common target, as well as low-level libraries that may be handwritten in the target language. We are pursuing a new methodology for building verified compilers for today’s world of multi-language software. The project has two central themes, both of which stem from a view of compiler correctness as a language interoperability problem. First, to specify correctness of component compilation, we require that if a source component s compiles to target component t, then t linked with some arbitrary target code t should behave the same as s interoperating with t . The latter demands a formal semantics of interoperability between the source and target languages. Second, to enable safe interoperability between components compiled from languages as different as ML, Rust, Python, and C, we plan to design a gradually type-safe target language based on LLVM that supports safe interoperability between more precisely typed, less precisely typed, and type-unsafe components. Our approach opens up a new avenue for exploring sensible language interoperability while also tackling compiler correctness. 1998 ACM Subject Classification F.3.2 Semantics of Programming Languages, D.3.1 Formal Definitions and Theory, D.3.4 Processors Keywords and phrases verified compilation; compositional compiler correctness; multi-language semantics; typed low-level languages; gradual typing Digital Object Identifier 10.4230/LIPIcs.xxx.yyy.p 1 Landscape of Verified Compilation The field of compiler verification has witnessed considerable progress in the last decade following the pioneering work on CompCert [32, 33]. The latter uses the Coq proof assistant to both implement and verify a multi-pass optimizing compiler from C to assembly, proving that the compiler preserves semantics of source programs. Several other compiler verification efforts have successfully followed CompCert’s lead and basic methodology to verify increasingly sophisticated compilers for increasingly realistic languages, for instance, focusing on just- in-time compilation [40], multithreaded Java [34], C with relaxed memory concurrency [49], LLVM passes [62], and imperative functional languages [18, 31]. Unfortunately, all of the above projects prove correctness assuming that the compiler will only be used to compile whole programs. But this assumption contradicts the reality of how we use these compilers. The whole programs that we actually run are almost never the output of a single compiler: they are composed by linking code from various places, including * This work is supported by the National Science Foundation and a Google Faculty Research Award. © Amal Ahmed; licensed under Creative Commons License CC-BY Conference title on which this volume is based on. Editors: Billy Editor and Bill Editors; pp. 1–17 Leibniz International Proceedings in Informatics Schloss Dagstuhl – Leibniz-Zentrum für Informatik, Dagstuhl Publishing, Germany

Upload: buitruc

Post on 02-Jan-2017

232 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Verified Compilers for a Multi-Language World

Verified Compilers for a Multi-Language World∗

Amal Ahmed

Northeastern [email protected]

AbstractThough there has been remarkable progress on formally verified compilers in recent years, mostof these compilers suffer from a serious limitation: they are proved correct under the assumptionthat they will only be used to compile whole programs. This is an unrealistic assumption sincemost software systems today are comprised of components written in different languages—bothtyped and untyped—compiled by different compilers to a common target, as well as low-levellibraries that may be handwritten in the target language.

We are pursuing a new methodology for building verified compilers for today’s world ofmulti-language software. The project has two central themes, both of which stem from a viewof compiler correctness as a language interoperability problem. First, to specify correctness ofcomponent compilation, we require that if a source component s compiles to target componentt, then t linked with some arbitrary target code t′ should behave the same as s interoperatingwith t′. The latter demands a formal semantics of interoperability between the source and targetlanguages. Second, to enable safe interoperability between components compiled from languagesas different as ML, Rust, Python, and C, we plan to design a gradually type-safe target languagebased on LLVM that supports safe interoperability between more precisely typed, less preciselytyped, and type-unsafe components. Our approach opens up a new avenue for exploring sensiblelanguage interoperability while also tackling compiler correctness.

1998 ACM Subject Classification F.3.2 Semantics of Programming Languages, D.3.1 FormalDefinitions and Theory, D.3.4 Processors

Keywords and phrases verified compilation; compositional compiler correctness; multi-languagesemantics; typed low-level languages; gradual typing

Digital Object Identifier 10.4230/LIPIcs.xxx.yyy.p

1 Landscape of Verified Compilation

The field of compiler verification has witnessed considerable progress in the last decadefollowing the pioneering work on CompCert [32, 33]. The latter uses the Coq proof assistantto both implement and verify a multi-pass optimizing compiler from C to assembly, provingthat the compiler preserves semantics of source programs. Several other compiler verificationefforts have successfully followed CompCert’s lead and basic methodology to verify increasinglysophisticated compilers for increasingly realistic languages, for instance, focusing on just-in-time compilation [40], multithreaded Java [34], C with relaxed memory concurrency [49],LLVM passes [62], and imperative functional languages [18, 31].

Unfortunately, all of the above projects prove correctness assuming that the compilerwill only be used to compile whole programs. But this assumption contradicts the reality ofhow we use these compilers. The whole programs that we actually run are almost never theoutput of a single compiler: they are composed by linking code from various places, including

∗ This work is supported by the National Science Foundation and a Google Faculty Research Award.

© Amal Ahmed;licensed under Creative Commons License CC-BY

Conference title on which this volume is based on.Editors: Billy Editor and Bill Editors; pp. 1–17

Leibniz International Proceedings in InformaticsSchloss Dagstuhl – Leibniz-Zentrum für Informatik, Dagstuhl Publishing, Germany

Page 2: Verified Compilers for a Multi-Language World

2 Verified Compilers for a Multi-Language World

the runtime system, libraries, and foreign functions, all potentially compiled using differentcompilers and written in different languages (e.g., Java, Python, C, and even handcraftedassembly). For today’s world of multi-language software, we need verified compilers thatguarantee correct compilation of components.

Formally verifying that components are compiled correctly—often referred to as com-positional compiler correctness—is a difficult problem. A key challenge is how to state thecompiler correctness theorem for this setting. CompCert’s compiler correctness theorem iseasy to state thanks to the whole program assumption: informally, it says that if a sourceprogram PS compiles to a target program PT , then running PS and PT results in the sametrace of observable events. The same sort of theorem does not make sense when we compilea component eS to a component eT : we cannot “run” a component since it is not a completeprogram.

Part of the challenge is that any correct-component-compilation theorem should satisfytwo important properties: (1) it should allow compiled components to be linked with targetcomponents of arbitrary provenance, including those compiled from other languages (dubbedhorizontal compositionality in the literature); and (2) it should support verification of multi-pass compilers by composing proofs of correctness for individual passes (dubbed verticalcompositionality). These are nontrivial requirements. There have been several notable effortsat compositional compiler verification in recent years but none offer a proof methodologythat fully addresses the dual challenges of horizontal and vertical compositionality. Theearliest work, by Benton and Hur [15, 16, 29], specifies compiler correctness in a way thatdoes not scale to multi-pass compilers. Both that work and the recent work on Pilsner [44]only supports linking with separately compiled components from the same source language.The recent work on Compositional CompCert [52] supports linking across the languages inCompCert’s compilation pipeline, but all of these share the same memory model; it is notclear how to extend the approach to support linking across languages with different memorymodels. (We discuss related work in detail in §3.2.)

Let us look at why compiler correctness becomes challenging in the context of multi-language software. The issue is that real software systems often link together componentsfrom multiple languages with different expressive power and different guarantees. Whencompiling source language S, what does it mean for the compiler to “preserve the semanticsof source code” when a compiled component eT may be linked with some e′

T from a languageS′ that is more expressive or provides fewer guarantees as compared to S?

Suppose S does not support first-class continuations (call/cc) but S′ does. For instance, e′T ,

compiled from language S′, might be a continuation-based web server that provides call/cc-based primitives that a web programmer can use for creating client-server interactionpoints [48, 30, 2, 1]. The programmer, meanwhile, might use language S to developher web application—making use of the afore-mentioned primitives which allow her towrite her program in a more direct style [48, 30]—and compile it to eT . When we linkthese components and run, e′

T disrupts the control flow of eT in a way that is usefuland desirable but does not mirror any whole-program behavior in the source language S.How, then, do we specify that the compiler “preserves semantics of source code”?Suppose S supports strong static typing (e.g., ML) while S′ is dynamically typed (e.g.,Scheme) or weakly typed (e.g., C). Again, linking e′

T with eT might result in code thatdoes not mirror any whole-program behavior in the source language S. For instance, (1)the C component may try to access memory it has already freed; (2) the C componentmay write past the end of a C array, but in doing so overwrite a memory location ownedby an ML module; or (3) the Scheme component may apply an ML function that expects

Page 3: Verified Compilers for a Multi-Language World

A. Ahmed 3

an argument of type ∀α.α → α to the boolean negation function, thus violating ML’sguarantee that a function of this type will behave parametrically with respect to itsinput. The first interaction (with C) seems entirely reasonable since the error is in Ccode and does not affect the behavior of the ML component. The second interaction(with C) is clearly unacceptable as it can violate ML’s type safety guarantee. Thethird interaction (with Scheme) may or may not be considered reasonable: it does notviolate ML’s type safety guarantee, but it does violate ML’s parametricity guarantee. Inwhat sense, then, can we say that the compiler “preserves semantics of source code”?

The above examples suggest that we need a novel way of understanding and specifyingwhat we mean by compiler correctness in the context of multi-language software.

2 Our Approach and Research Goals

My research group at Northeastern is working on techniques for building realistic, multi-pass verified compositional compilers that guarantee correct compilation of components andformally support linking with arbitrary target code, no matter its source. We follow the tenetthat compositional compiler correctness is a language interoperability problem.We are in the early stages of a project that will require years of investigation and evaluation.Our long-term vision, depicted in Figure 1, is to have verified compositional compilers fromlanguages as different as C, Python, ML, Rust, Coq’s Gallina, and Hoare Type Theory(HTT) [41, 43, 42] to a common low-level target language that supports safe interoperabilitybetween more precisely typed, less precisely typed, and type-unsafe components. Our currentfocus is on laying the groundwork for realizing this vision through two central contributions:1. Development of a proof methodology for verifying compositional compilers. The defining

feature of our approach is that the specification of compositional compiler correctnessrelies on a formal semantics of interoperability between source components and targetcode using multi-language semantics in the style of Matthews and Findler [36].(We havealready done extensive work on developing this methodology [47]; we give further detailsin §3.) To demonstrate the viability of this approach, we plan to verify compositionaltype-preserving compilers for subsets of ML and Rust.

2. Design of a gradually type-safe LLVM-like target language (dubbed GTVM, see Figure 1)that supports safe interoperability between components that are statically type-safe (e.g.,produced by our type-preserving ML and Rust compilers), dynamically type-safe (e.g.,compiled from Python or Scheme), or potentially unsafe (e.g., compiled from C). We planto build on Vellvm (verified LLVM) [3, 61, 62] by first developing TTVM (tightly typedLLVM), a statically type-safe version of the LLVM IR formalized in Vellvm, and thendesign GTVM as a gradually typed extension of TTVM. GTVM will make use of casts(think: coercions or contracts) to ensure safe interoperability between the more preciselytyped, less precisely typed, and type-unsafe parts of the language—the latter unsafepart is just standard LLVM IR which has types but is not type-safe. We will compileGTVM to the LLVM IR, inserting wrappers that perform dynamic checks to ensuresafe coercion. Thus, compilers targeting our gradually type-safe LLVM can continue toleverage the optimizations provided by the LLVM compiler infrastructure [55] or theverified optimizations provided by Vellvm [3, 62].

Page 4: Verified Compilers for a Multi-Language World

4 Verified Compilers for a Multi-Language World

GTVM

casts

LLVM backend (optimizations, code gen)

Link (add casts/coercions)Compile

(insert wrappers for safe

coercion)

compilerverifiedcompiler

verifiedcompiler

verifiedcompiler

LLVM IR

LLVM IR type-safe LLVM IR dependently typed

ML Coq / HTT / F*C/C++ Rust

casts

Project Focus Future workCurrently

Figure 1 Research planned as part of this project and potential future work

Specifying compositional compiler correctness for a multi-language world Informally, ifa component eS compiles to a component eT then compiler correctness should require thateS is “equivalent” to eT . But how can we formalize this notion of “equivalence” betweensource and target components? Observe that to use a compiled component eT , we willlink it with some other target-level component e′

T to obtain a whole program that canbe run. Intuitively, therefore, compiler correctness should guarantee that the operationalbehavior of this resulting target program is the same as the operational behavior of eSlinked with e′

T . Therefore, to formally state that “a component is compiled correctly,” weneed to formalize the semantics of interoperability between source and target code. For amulti-pass compiler we propose to do this in a modular fashion. For instance, if the compilerconsists of two passes, from source language S to intermediate language I to target languageT , we define a combined language SIT that embeds these three languages and formalizesthe semantics of interoperability between each pair of adjacent languages using boundariesin the style of Matthews and Findler’s multi-language semantics [36]. We can stack theseboundaries to allow interoperability between the source and target of the compiler, e.g.,SI(IT (eT )), which we abbreviate to SIT (eT ), allows a target component eT to be used fromwithin an S-language expression. Compiler correctness can now be stated as observationalequivalence in the combined language: if eS compiles to eT , then eS is observationallyequivalent to SIT (eT ). Direct proofs of observational equivalence—also known as contextualequivalence—are known to be intractable. Therefore, we define a logical relation for thecombined language that corresponds to contextual equivalence and use that to carry outthe proof of compiler correctness. Note that we do not use the multi-language semanticsfor running actual multi-language programs; its purpose is to serve as a specification of thedesired source-target relationship, allowing us to state and prove compiler correctness. Thisspecification also enables reasoning about the whole-program behavior of eT linked with e′

T

in terms of the whole-program behavior of eS linked with SIT (e′T ). Most importantly, note

that we have not imposed any restrictions on the provenance of e′T . We give further details

in §3.

Page 5: Verified Compilers for a Multi-Language World

A. Ahmed 5

Why ML and Rust? All of the existing work on compositional compiler correctness haseither (a) focused on unsafe C-like source languages [52, 59] or (b) assumed that code producedby a verified compiler from a type-safe language will only be linked with code produced byanother verified compiler from the same source language [15, 16, 29, 44]. We instead focuson compiling the statically typed languages ML and Rust while allowing linking with codeof arbitrary provenance. This is a real-world scenario with interesting semantic challengesfor interoperability—specifically, how to maximize interoperability with less precisely typedand type-unsafe components while ensuring that those interactions respect the ML or Rusttype system. We believe that these interoperability challenges make compositional compilercorrectness harder to establish for these languages. Languages that provide fewer guarantees(e.g., C) are less picky in terms of what they can interoperate with, which makes it lesssemantically challenging to ensure correct compositional compilation.

Rust is a systems programming language with type-system support for safe memorymanagement. In essence, it supports affine types which indicate that a resource—in thiscase, memory cells—can be used at most once. Since ML does not support affine types, wewill have to ensure that ML respects Rust’s affine typing guarantees.

GTVM: a gradually type-safe LLVM IR As the above discussion suggests, to prove compilercorrectness in the context of multi-language software, we must specify a formal semantics ofinteroperability (1) between source and target code, and (2) between more precisely typed,less precisely typed and type-unsafe code. For instance, for our ML compiler, we will have tospecify how an ML source component eS interoperates with a target component e′

T that mayhave been compiled from Scheme or C. We believe that safe interoperability between moreand less typed and type-unsafe should be investigated at the level of the target language sothat the benefits of this effort can be reaped by all compilers that target the language. Thatis the philosophy underlying our investigation of GTVM.

To understand the design of GTVM, it is useful to think of it in two layers: (1) a staticallytype-safe core, TTVM (tightly typed LLVM), and (2) a gradual typing extension, GTVM,that extends TTVM with dynamically type-safe code of type dyn and unsafe, standard LLVMIR code of type un. Our goal is a gradual type system that ensures that the typing (andparametricity) guarantees provided by more precise types cannot be violated by the lessprecisely typed parts of the language.

The statically type-safe core language, TTVM, should provide a rich enough type systemto adequately serve as a target for type-preserving compilers from different statically typedlanguages. Since we wish to compile ML and Rust, our TTVM will need to support at leasttype abstraction and affine types in addition to the standard LLVM types. Even within thisTTVM core, we will need a system of casts (contracts) to mediate between more precise andless precise types to support interoperability between code compiled from different sourcelanguages. For instance, we wish to allow code compiled from ML and Rust to interoperate,which means we will need to design casts (probably along the lines of Tov and Pucella [56])that protect an affinely typed resource (from Rust) from being used more than once, even ifit is passed to a function (from ML) that knows nothing about affine types and might freelyduplicate the resource.

GTVM will let compiler writers choose whether to target the statically type-safe, dynam-ically type-safe, or unsafe parts of the language (or some mix of the three) depending on thenature of their source language, and depending on how restrictive a form of interoperabilitythey want. The lever that provides control over interoperability is the compiler’stype translation. For instance, when compiling ML, picking the most informative type

Page 6: Verified Compilers for a Multi-Language World

6 Verified Compilers for a Multi-Language World

translation (relative to source types) will guarantee that interoperability with other languagesrespects the source type system—including parametricity guarantees, as long as the compilerdoesn’t monomorphize. At the other extreme, translating all ML code to the type un saysthat interoperability with other code need not respect ML’s type system guarantees.

Our longer-term goal is to enrich the statically typed core of GTVM with dependenttypes in the style of Hoare Type Theory (HTT) [41, 43]. HTT incorporates specifications—inthe style of Hoare logic or separation logic—into types and makes it possible to formallyspecify and reason about effects. A type system based on HTT would allow us to express richinvariants about memory layout, separation, and resource usage, which will be important forspecifying low-level conventions that affect interoperability at the LLVM level.

Enabling secure compilation via target-level types Compiler correctness is about preserva-tion of dynamic semantics, but we are interested in an architecture that can also support thedevelopment of secure (or fully abstract) compilers. Informally, secure compilation guaranteesthat compiled components will remain as secure as their source-level counterparts whenexecuted within target contexts of arbitrary origin. More formally, a fully abstract compilerguarantees that if two source components have the same observable behavior in all well-typedsource contexts then their compiled versions must have the same observable behavior in allappropriately-typed target contexts. In prior work [7], we studied how compilers can bemade fully abstract by changing the compiler’s type translation to ensure that compiledcode is never linked with target contexts whose behavior does not match that of some sourcecontext. By equipping GTVM with an expressive type system, we wish to offer compilerwriters the facility to pick different degrees of protection of compiled components from theirtarget contexts—ranging from no protection at all (when compiled code has type un and theverified compiler only guarantees preservation of dynamic semantics), all the way throughfully abstract compilation—via their choice of type translation.

The rest of this paper explains our approach to specifying compiler correctness (§3) andthen outlines our research plans and anticipated challenges (§4).

3 Proof Methodology for Verified Compilation of Components

In recent work [47], we have demonstrated the viability of our multi-language-semanticsapproach, using it to prove correctness of a two-pass compiler that performs closure conversionand heap allocation, translating a polymorphic source language with recursive types to atarget that also features dynamically allocated mutable memory. In particular, we supportlinking of compiled code with code that performs state effects that cannot be expressed inthe source. This work was the first multi-pass, compositional compiler-correctness result. Webelieve that it is, to date, the only approach that supports linking with code that cannot beexpressed in the verified compiler’s own source language. We plan to use this methodology tobuild verified compositional compilers for ML and Rust. Below, we explain our approach inmore detail, compare it to related work, and illustrate our methodology using typed closureconversion [37, 39, 6] as a case study.

3.1 Specifying compiler correctness using multi-language semanticsThe compiler correctness theorem should say that if a component eS compiles to eT , thensome desired relationship eS ' eT holds between eS and eT—intuitively, they should “behavethe same.” But how do we formally specify eS ' eT ? To answer this question, consider how

Page 7: Verified Compilers for a Multi-Language World

A. Ahmed 7

the compiled component is actually used: it needs to be linked with some e′T , creating a

whole program that can be run. This e′T may have been handwritten in the target language

or produced by another compiler, possibly from a different source language. Of course, itdoesn’t make sense to link with any e′

T : at the very least, e′T should adhere to the same

calling conventions that eT does. Moreover, since our target language is typed—with typesdyn and un in additional to the more standard types—that means that eT can only belinked with components of a certain type because we want the resulting whole program tobe well typed. Informally, then, the compiler correctness theorem should guarantee that ifwe link eT with an appropriately typed e′

T then the resulting target-level program shouldcorrespond to the source component eS linked with e′

T . Formally speaking, what does itmean to “link a source component with a target component” and what are the rules forrunning the resulting source-target hybrid? We have argued that these questions demand asemantics of interoperability between the source and target languages. Next, we explain howto specify such semantics.

Consider a two-pass compiler from a source language S (in blue) to intermediate languageI (in red) to target language T (in purple). The first pass translates S components eS of typeτ to I components eI of type τI , where τI denotes the type translation of τ . As is usualwith type-preserving compilation, the type τI provides a simple means of expressing anycompiler invariants about representation and/or layout of the transformed term eI that areuseful to keep track of as we compile. The second pass analogously translates I componentseI of type τ to T components eT of type τT , where τT is the type translation of τ .

To define the semantics of interoperability between these languages, we embed them allinto one language, SIT , and add syntactic boundary forms between each pair of adjacentlanguages in the style of Matthews and Findler [36] and our own prior work [7, 47]. Forinstance, the term ISτ (eS) allows an S component eS of type τ to be used as an I componentof type τI , while τSI(eI) allows an I component eI of translation type τI to be used asan S component of type τ . Similarly, we have boundary forms T I and IT for the nextlanguage pair. Non-adjacent languages can interact by stacking up boundaries: for example,SI(IT eT) (abbreviated SIT (eT)) allows a T component eT to be embedded in an S term.

Design principles for multi-language system Our goal is for the SIT interoperabilitysemantics to give us a useful specification of when a component in one of the embeddedlanguages should be considered equivalent to a component in another language. But howdo we know if that specification is correct? There are three essential properties that thecombined language must satisfy.

First, the operational semantics of SIT must be designed so that the original languages areembedded into SIT unchanged: running an SIT program that’s written solely in one of theembedded languages is identical to running it in that language alone. For instance, executionof the T program eT proceeds in exactly the same way whether we use the operationalsemantics of T or the augmented semantics for SIT . Second, the typing rules must besimilarly embedded: a component that contains syntax from only one underlying languageshould typecheck under that language’s individual type system if and only if it typechecksunder SIT ’s type system. The final property we need is boundary cancellation which says thatwrapping two opposite language boundaries around a component yields the same behavior asthe underlying component with no boundaries: for example, any eS : τ must be contextuallyequivalent to τSI(ISτeS), and any eI : τI must be equivalent to ISτ (τSIeI). Note thattwo components e1 and e2 are considered contextually equivalent in language SIT (writtene1 ≈ctxSIT e2) if there is no well-typed SIT program context that can tell them apart.

Page 8: Verified Compilers for a Multi-Language World

8 Verified Compilers for a Multi-Language World

Compiler correctness We state the correctness criterion for our compiler as a contextualequivalence: if eS : τ compiles to eI, then eS ' eI, where: eS ' eI

def= eS ≈ctxSITτSI(eI) : τ

and similarly for the next pass:if eI : τ compiles to eT, then eI ' eT, where: eI ' eT

def= eI ≈ctxSITτIT (eT) : τ .

Since contextual equivalence is transitive, our framework achieves vertical compositionalityimmediately: it is easy to combine the two correctness proofs for the individual compilerpasses, to get the correctness result for the entire compiler:

if eS compiles to eT, then eS ≈ctxSITτSIT (eT) : τ .

All of the above properties are stated as contextual equivalences but direct proofs of contextualequivalence are usually intractable. We use the standard technique of defining a logicalrelation for the combined language SIT that is sound and complete with respect to contextualequivalence. Defining the logical relation becomes more challenging as the demands ofinteroperability increase: e.g., when all the interoperating languages support type abstraction.(See the paper [47] for details.)

Reasoning about linking Our approach enjoys a strong horizontal compositionality prop-erty: we can link with any target component e′

T that has an appropriate type, with norequirement that e′

T was produced by any particular means or from any particular sourcelanguage. Specifically, if eS : τ ′ → τ expects to be linked with a component of type τ ′

and compiles to eT, then eT will expect to be linked with a component of type ((τ ′)I)T .If e′

T has this type, then using our compiler correctness theorem, we can conclude that:(eS

τ ′SIT (e′T)) ≈ctxSIT SIT (eT e′

T) : τ .

3.2 Comparison with related workThe literature on compiler verification spans almost five decades but is mostly limitedto whole-program compilation. We refer the reader to the bibliography by Dave [19] forcompilation of first-order languages, and to Chlipala [18] for compilation of higher-orderfunctional languages. Here we discuss only recent work on compositional compiler correctness.

The approach advocated by Benton and Hur [15, 16] involves formalizing correct compila-tion of components using a logical relation that specifies when a source term eS semanticallyapproximates target code eT and vice versa (written eS ' eT ). Using this approach, theyverifed a compiler from STLC with recursion (and later from System F) to an SECD machine,proving that if source component eS compiles to target code eT , then eS and eT are logicallyrelated. Later, Hur and Dreyer [29] used essentially the same strategy to verify a compilerfrom an idealized ML to assembly. However, this strategy of setting up a logical relationbetween source and target has two serious drawbacks. First, the approach does not scale tomulti-pass compilers because the source-target logical relations defined for each pass do notcompose. Second, if we compile an S component eS to eT and then wish to link eT withsome arbitrary e′

T , the only way to check if it’s okay to link with e′T (i.e., if the compiler

correctness theorem allows it) is to come up with a source-level component e′S and show that

e′S ' e′

T . It may be possible to come up with e′S when e′

T is a few lines long, but it would beinfeasible when e′

T consists of hundreds of lines of assembly. Worse, the question of whethersuch an e′

S exists in language S is undecidable: for instance, there is no e′S in the case of the

continuation-based web server example discussed in §1. By comparison, our approach simplyrequires type-checking e′

T to ensure that it can be sensibly linked with t.Neis et al. [44] recently developed Pilsner, a verified separate compiler from a typed,

higher-order imperative language to an untyped target. Pilsner also suffers from the second

Page 9: Verified Compilers for a Multi-Language World

A. Ahmed 9

drawback discussed above for the Benton-Hur and Hur-Dreyer work—put another way,Pilsner assumes that compiled code will only be linked with code compiled by a (possiblydifferent) verified compiler from the same source language. Instead of a logical relationbetween source and target code, Pilsner uses parametric inter-language simulations (PILS)between the source and target of each compiler pass. Unlike Kripke logical relations, PILSdo compose transitively and can be used to verify a multi-pass compiler.

Stewart et al. [52] recently reported on Compositional CompCert, a verified separatecompiler for C. (This generalizes their prior work [17] which allowed shared-memory systemcalls from C, but could not accommodate mutually recursive inter-module dependencies.)Whereas we define a multi-language semantics for interoperability between source andtarget, Stewart et al. have devised an interaction semantics, a protocol-oriented operationalsemantics that accommodates interoperation between different languages. They have shownthat CompCert’s intermediate languages—all of which share the same C-like memory model—can be “plugged into” this interaction semantics. It is not clear how to extend interactionsemantics to support interoperability between C and high-level, strongly typed languageslike ML, or more generally, to accommodate compilers whose source and target languagesuse different memory models.

3.3 Our proof methodology applied to closure conversionAs an example, we show how our methodology can be used to prove compositional correctnessof typed closure conversion [37, 39] for the simply-typed lambda calculus (STLC). (We elidemany details; see [47].)

Step 0: Specify the source language The source language S is call-by-value STLC withbooleans. The syntax of the language is as follows:

Types σ ::= bool | σ1→σ2

Values v ::= x | true | false | λx :σ. eExpressions e ::= v | if e then e1 else e2 | e1 e2

Step 1. Pick appropriate target language and define translation Closure conversion is acompiler transformation that collects a function’s free variables in a tuple called a closureenvironment that is passed as an additional argument to the function, thus turning thefunction into a closed term. The closed function is paired with its environment to create aclosure. The basic idea of typed closure conversion goes back to Minamide et al. [37], whomwe follow in using an existential type to abstract the type of the environment. This ensuresthat two functions with the same type, but different free variables still have the same typeafter closure conversion: the abstract type hides the fact that the closures’ environments havedifferent types. Thus our target language for closure conversion must support existentialtypes as well as tuples. It also supports multi-argument functions. We define the targetlanguage T as follows:

Types τ ::= bool | (τ)→τ′ | 〈τ1, . . . ,τn〉 | α | ∃α .τValues v ::= x | true | false | λ(x :τ). e | 〈v1, . . . ,vn〉 | pack(τ,v) as ∃α .τ′

Expressions e ::= v | if e then e1 else e2 | e1(e) | 〈e1, . . . ,en〉 | πi e |pack(τ, e) as ∃α .τ′ | unpack(α,x) = e1 in e2

The typing rules are standard (with judgments ∆; Γ ` e : τ), with one exception: sincelanguage T is the target of closure conversion, we ensure via the function typing rule that

Page 10: Verified Compilers for a Multi-Language World

10 Verified Compilers for a Multi-Language World

Types ϕ ::= σ | τ

Terms e ::= . . . | σST ee ::= . . . | TS σ ee ::= e | e

Values v ::= v | v

Type Environments ∆ ::= · | ∆,αValue Environments Γ ::= · | Γ, x : σ | Γ,x : τ

∆; Γ ` e : ϕ

. . .∆; Γ ` e : σ+

∆; Γ ` σST e : σ∆; Γ ` e : σ

∆; Γ ` TS σ e : σ+

e 7−→ e′

boolST true 7−→ trueboolST false 7−→ falseσ1→ σ2ST v 7−→ λx :σ1.

σ2ST (unpack(α,y) = v in π1 y (π2 y, TS σ1 x))

TS bool true 7−→ trueTS bool false 7−→ falseTS σ1→ σ2 v 7−→ pack(〈〉, 〈v, 〈〉〉) as ∃α .〈〈α, σ1

+〉→σ2+, α〉

where v = λ(z : 〈〉,x :σ1+).TS σ2 (v σ1ST x)

Figure 2 ST : Extensions to S and T syntax, static semantics, dynamic semantics

functions contain no free term variables. Thus, the typing rule for functions is as follows:

·; x : τ ` e : τ′

∆; Γ ` λ(x :τ). e : (τ)→τ′

Closure conversion maps source terms of type σ to target terms of type σ+. The definitionof the type translation (σ+) and environment translation (Γ+) is as follows:

(bool)+ = bool (·)+ = ·(σ1→σ2)+ = ∃α . 〈〈α, σ1

+〉→σ2+, α〉 (Γ, x : σ)+ = Γ+,x : σ+

We then define a type-directed term translation Γ ` e : σ ; e that translates a term e suchthat Γ ` e : σ into a target term e such that ·; Γ+ ` e : σ+. For instance, x translates tox and below we show how functions are translated into closures. (We elide the rest of thetranslation as it is standard, e.g., see [37, 39, 47].)

y1, . . . , ym = free-vars(λx :σ. e) Γ ` y1 : σ1 . . . Γ ` ym : σm τenv = 〈σ1+, . . . ,σm

+〉Γ, x : σ ` e : σ′

; e v = λ(z :τenv,x :σ+). e[π1 z/y1]. . .[πm z/ym]Γ ` λx :σ. e : σ→σ′

; pack(τenv, 〈v, 〈y1, . . . ,ym〉〉) as ∃α . 〈〈α, σ+〉→σ′+, α〉

Step 2: Define interoperability semantics The language ST embeds the languages S andT so that both languages have natural access to foreign values (i.e., values from the otherlanguage). They receive foreign boolean values as native values, and can call foreign functionsas native functions. We extend the original core languages with boundary terms σST e andTS σ e which mediate between the types σ on the source side and σ+ on the target side.Figure 2 presents the new syntax, typing rules and reduction rules. Typing judgments forST have the form ∆; Γ ` e : ϕ where the environment Γ now tracks both source variables oftype σ and target variables of type τ. The typing rules include all the S typing rules, butaugmented with the additional environment ∆; all the T typing rules, unchanged; and rulesfor the two boundary constructs, shown in Figure 2.

Page 11: Verified Compilers for a Multi-Language World

A. Ahmed 11

We evaluate under a boundary until we have a value. The reduction rules for boundariesannotated bool convert boolean values from one language to the other. To convert functionsacross languages, we use native proxy functions. We represent a target function v in thesource at type σ1→σ2 by a new function that takes an argument of type σ1 and firsttranslates this argument from source to target, then unpacks the closure v and applies thecode to its environment and the translated argument, and finally translates the result backto source at type σ2. Notice that the direction of the conversion (and the boundary used)reverses for function arguments.

Converting source functions to target functions is a bit more subtle. To represent a sourcefunction v in the target at type (σ1→σ2)+ we have to construct a closure. Since these arereduction rules, and since we only run closed programs, we know that v is closed. Hence, wesimply use an empty tuple type 〈〉 for the closure environment. The underlying function vfor this closure takes an environment of type 〈〉 and an argument of type σ1

+, translatesthe argument to source, applies v to the translated argument, and finally translates theresult back to target. The use of an empty environment in this reduction rule illustrates thedifference between the translation done by boundaries in the multi-language and that doneby the compiler itself.

Step 3: Define logical relation for combined language (≈logST ) and prove ≈log

ST ≡ ≈ctxST

Next, we define a logical relation for the combined language ST and prove it sound andcomplete with respect to contextual equivalence (≈ctx

ST ). In the process, we must prove theboundary cancellation property described in §3.1—that is, this is the stage at which weare required to prove that the combined language can sensibly serve as an interoperabilitysemantics.

Step 4: Prove translation is semantics preservingIf Γ ` e : σ ; e then ·; Γ ` e ≈log

STσST e′ : σ, where e′ is e with all x that are translations

of x : σ′ ∈ Γ replaced with TS σ′x. Since ≈log

ST exactly captures ≈ctxST , this lemma immediately

yields the compiler correctness theorem we want.

4 Research Plan and Central Challenges

We plan to carry out this work in three stages, with the target language GTVM growing infeatures and functionality across the three stages. Below we discuss the main tasks and thechallenges we anticipate, and describe some of the work done to date.

Verified compiler: ML to TTVM In the first phase, we plan to develop a verified compilerfrom an idealized ML to a statically type-safe LLVM IR. The LLVM IR is a platform-independent, static single assignment (SSA) language [55]. An LLVM compiler normallytranslates a high-level language into LLVM IR. This can then be optimized using a series ofIR to IR transformations. LLVM provides a collection of such transformations which performoptimizations and static analyses. The resulting LLVM IR can then be translated to a targetarchitecture using LLVM’s code generator or JIT-compiler.

LLVM programs consist of modules, which in turn contain function definitions anddeclarations. A function consists of a sequence of basic blocks, which as usual are a sequenceof commands ending with a branch or return (ret) instruction. Commands include load,store, malloc, and free instructions; alloca for stack allocation; and a function callinstruction. To be well formed, an LLVM program must be in valid SSA form. All components

Page 12: Verified Compilers for a Multi-Language World

12 Verified Compilers for a Multi-Language World

in the language are annotated with types, but the LLVM IR is not a type-safe language—likeC, it allows arbitrary casts, invalid memory access, and so on.

Vellvm [3] provides a mechanized formal semantics of LLVM’s IR, its static semantics,and SSA form, all formalized in Coq, as well as a proof of static safety (modulo reachingknown stuck states) via preservation and progress theorems. It says that if the programtakes a step then it continues to be well formed SSA; and if the program is well formed theneither it can take a step or it is in one of the defined set of stuck states. Vellvm also providesa set of tools to extract LLVM IR from Coq so it can be processed by the standard LLVMtools. Vellvm provides us with a useful starting point; without the Vellvm formalization ourresearch plans would not be feasible.

We will first identify a statically type-safe subset of the LLVM IR (TTVM) and modifyVellvm’s static safety theorems so that the progress lemma holds without the possibility of awell formed program configuration being stuck. This will require eliminating arbitrary casts,memory deallocation (free), and anything that leads to undefined behavior (undef) fromthe language. We will then extend the type system with polymorphism, existential types,and any other extensions needed to do type-preserving compilation from our idealized ML(language M ) to TTVM (language T ).

Tentatively, the compiler will consist of four languages and three passes: a closureconversion pass from M to C, an explicit allocation pass (where the data representationstrategy is made explicit) from C to A, and code generation pass from A to T. To state compilercorrectness, we will embed all four of the compiler’s languages into a combined languageMCAT by defining interoperability between the adjacent languages in the compilation pipeline.The design of the interoperability semantics between M and C (for the closure conversionpass) and between C and A (for the explicit allocation pass) is already well understood andwe can adapt the multi-language and logical relation from our recent work [47] which coversclosure conversion and explicit allocation for System F with recursive types. Moreover, inrecent work with Phillip Mates and James Perconti, we have already proved compositionalcorrectness of closure conversion in the presence of ML-style mutable references. The presenceof mutable references required a novel extension to our logical relation for the multi-languagesystem, which we plan to report on in the near future.

We anticipate that the design of an interoperability semantics between language A andTTVM (i.e., for the code generation pass) will be the most challenging. In the language A,code still has a compositional structure even though tuples and closures are allocated onthe heap—that is, a component is a simply a term eA. However, at the TTVM level, thatcompositional structure is lost. To define interoperability between A components and T(TTVM) components, we first have to identify what exactly constitutes a TTVM component—that is, since there are no “terms” in TTVM, what is the shape of an eT that we can putunder a boundary AT eT ?

Fortunately, in preliminary work with Perconti, we have already answered this questionin the context of an idealized typed assembly language (TAL), which is even lower level thatTTVM. For the purpose of the multi-language semantics, a TAL (or TTVM) componentis comprised of a number of basic blocks. Thus, eT denotes a pair (b0, b) of the currentlyexecuting basic block b0 and the rest of the blocks b that comprise that component (whichcorresponds now to a TTVM function body). The next question is how do we run theterm AT eT ? As in §3, we want to run eT until we have a value vT and then convert thatvalue to the language A. But running the eT in TTVM will ultimately end with a returninstruction. How do we distinguish between a normal return within TTVM from a returnto language A? The solution is to introduce a special ret-to-A pseudo-instruction as part

Page 13: Verified Compilers for a Multi-Language World

A. Ahmed 13

of the extensions we make when defining the multi-language semantics. When AT eT hasreduced to AT (ret-to-A vT ), we simply convert vT to an A value in the usual type-directedmanner. We are reasonably confident that we will be able to use ideas from our TAL workto design interoperability between A and TTVM. We do not yet know if LLVM’s SSA formwill complicate matters.

GTVM: a gradually type-safe LLVM IR In the second phase, we aim to extend TTVM withsupport for dynamically type-safe code, assigned type dyn, and type-unsafe code, assignedtype un. There is a significant body of work on contracts and gradual typing for high-levellanguages [22, 50, 51, 35, 9, 20, 26, 54, 46, 23, 28, 58, 27, 45, 14] that we can leveragewhen designing GTVM. The recent work on TS?, a gradual type system for JavaScript [53],deserves special mention as it also mixes static, dynamic, and unsafe types (though theirdynamic type is called any).

As is usual in gradual type systems, interactions between type safe code and unsafe codemust be protected by wrappers (dynamically checked contracts). However, note that unsafecode is just standard LLVM IR that may, for instance, be the output of a C program. Toprevent raw LLVM IR (or raw C) from breaking internal invariants of statically type safecode (say from ML or Scheme), we need to ensure that C code cannot trample memorycells that belong to the type-safe parts of the language. This is one of the most significantchallenges we face, but there are ideas that we can draw upon from the literature. Oneoption is to use some sort of software-based fault isolation (as in Google’s Native Client,NaCl [60]) to ensure that the unsafe code adheres to a strict sandbox policy, though weexpect this to be too coarse-grained. Another option is to investigate whether we can devisewrappers for GTVM similar to those used by TS? (adopted from Fournet et al. [25]), whichenforce a strict heap separation between unsafe and type-safe code. A third option is todevise contracts based on separation logic predicates similar to the approach of Agten etal. [4], who use these contracts to protect verified C modules from unverified C code, thoughthis approach comes with considerable performance overhead.

As mentioned earlier, we plan to develop a compiler from GTVM to LLVM IR that insertswrappers or safe coercions to ensure safe interoperability is preserved after the translation toLLVM IR. TS? similarly provides a compiler to JavaScript. To prove that the translationfrom TS? to JavaScript preserves properties such as memory isolation, the authors leveragea dependently typed version of JavaScript (called JS*) in which memory layout invariantscan be specified. We conjecture that we should be able to carry out such a proof using ourlogical relation for GTVM which will be based on much recent progress in scaling up logicalrelations to so that they can be used to tractably prove sophisticated equivalences in thepresence of state [5, 8, 21, 57].

Verified compiler: Rust to GTVM In the third phase, we plan to extend the GTVM/TTVMtype system so that it can support type-preserving compilation from Rust, in particular,adding features capable of expressing Rust’s region and ownership discipline. For the designof the TTVM type system and logical relation, we can leverage ideas from our prior workon linear and affine type systems for memory management [13, 12, 38, 10, 24, 11]. However,instead of trying to shoehorn an appropriate notion of affine types or capabilities into TTVM,it may be better to extend the TTVM type system with dependent types in the style ofHTT [41, 43]. This is a more challenging task, but a type system based on HTT might be abetter choice as it can be designed independent of Rust considerations and yet should beexpressive enough to serve as a target of typed compilation from Rust.

Page 14: Verified Compilers for a Multi-Language World

14 Verified Compilers for a Multi-Language World

5 Conclusion

Practically every software system, from safety-critical software to web browsers, uses com-ponents written in multiple programming languages and stands to benefit from verifiedcompositional compilers. We have proposed a proof architecture for verifying compositionalcompilers based on source-target interoperability and are working on demonstrating theviability of this approach. We also propose to extend LLVM—increasingly the backend ofchoice for modern compilers—to support compositional compilation from type-safe sourcelanguages and principled linking with less precisely typed languages. This ambitious researchprogram consists of numerous technical challenges and we look forward to collaborating withother groups in the community on various facets of this project.

Acknowledgements We thank the SNAPL anonymous reviewers, Andrew Appel, MatthiasBlume, Matthias Felleisen, Robby Findler, Bob Harper, Xavier Leroy, Guy McCusker, GregMorrisett, Aaron Turon, and members of IFIP WG 2.8 (Working Group on FunctionalProgramming) for valuable feedback on various aspects of this project. Jamie Perconti andPhillip Mates did the bulk of the work completed to date. Current project members includeWilliam Bowman, Max New, and Nick Rioux. This research is supported in part by the NSF(grants CCF-1453796, CCF-1422133, and CCF-1203008) and a Google Faculty ResearchAward.

References

1 Ocsigen. http://ocsigen.org.2 Seaside. http://seaside.st.3 Vellvm: Verifying the llvm. http://www.cis.upenn.edu/∼stevez/vellvm/.4 Pieter Agten, Bart Jacobs, and Frank Piessens. Sound modular verification of c code

executing in an unverified context. In ACM Symposium on Principles of ProgrammingLanguages (POPL), Mumbai, India, January 2015.

5 Amal Ahmed. Step-indexed syntactic logical relations for recursive and quantified types.In European Symposium on Programming (ESOP), pages 69–83, March 2006.

6 Amal Ahmed and Matthias Blume. Typed closure conversion preserves observational equiv-alence. In International Conference on Functional Programming (ICFP), Victoria, BritishColumbia, Canada, pages 157–168, September 2008.

7 Amal Ahmed and Matthias Blume. An equivalence-preserving CPS translation via multi-language semantics. In International Conference on Functional Programming (ICFP),Tokyo, Japan, pages 431–444, September 2011.

8 Amal Ahmed, Derek Dreyer, and Andreas Rossberg. State-dependent representation inde-pendence. In ACM Symposium on Principles of Programming Languages (POPL), Savan-nah, Georgia, January 2009.

9 Amal Ahmed, Robert Bruce Findler, Jeremy Siek, and Philip Wadler. Blame for all. InACM Symposium on Principles of Programming Languages (POPL), Austin, Texas, pages201–214, January 2011.

10 Amal Ahmed, Matthew Fluet, and Greg Morrisett. A step-indexed model of substructuralstate. In International Conference on Functional Programming (ICFP), Tallinn, Estonia,pages 78–91, September 2005.

11 Amal Ahmed, Matthew Fluet, and Greg Morrisett. L3 : A linear language with locations.Fundamenta Informaticae, 77(4):397–449, June 2007.

Page 15: Verified Compilers for a Multi-Language World

A. Ahmed 15

12 Amal Ahmed, Limin Jia, and David Walker. Reasoning about hierarchical storage. InIEEE Symposium on Logic in Computer Science (LICS), Ottawa, Canada, pages 33–44,June 2003.

13 Amal Ahmed and David Walker. The logical approach to stack typing. In ACM SIGPLANWorkshop on Types in Language Design and Implementation (TLDI), pages 74–85, January2003.

14 João Filipe Belo, Michael Greenberg, Atsushi Igarashi, and Benjamin C. Pierce. Polymor-phic contracts. In European Symposium on Programming (ESOP), March 2011.

15 Nick Benton and Chung-Kil Hur. Biorthogonality, step-indexing and compiler correct-ness. In International Conference on Functional Programming (ICFP), Edinburgh, Scot-land, September 2009.

16 Nick Benton and Chung-Kil Hur. Realizability and compositional compiler correctness fora polymorphic language. Technical Report MSR-TR-2010-62, Microsoft Research, April2010.

17 Lennart Beringer, Gordon Stewart, Robert Dockins, and Andrew W. Appel. Verified com-pilation for shared-memory C. In European Symposium on Programming (ESOP), April2014.

18 Adam Chlipala. A verified compiler for an impure functional language. In ACM Symposiumon Principles of Programming Languages (POPL), Madrid, Spain, January 2010.

19 Maulik A. Dave. Compiler verification: A bibliography. ACM SIGSOFT Software Engi-neering Notes, 28(6), 2003.

20 Christos Dimoulas, Sam Tobin-Hochstadt, and Matthias Felleisen. Complete monitors forbehavioral contracts. In European Symposium on Programming (ESOP), March 2012.

21 Derek Dreyer, Georg Neis, and Lars Birkedal. The impact of higher-order state and controleffects on local relational reasoning. Journal of Functional Programming, 22(4&5):477–528,2012.

22 Robert Bruce Findler and Matthias Felleisen. Contracts for higher-order functions. InInternational Conference on Functional Programming (ICFP), Pittsburgh, Pennsylvania,pages 48–59, September 2002.

23 Cormac Flanagan. Hybrid type checking. In ACM Symposium on Principles of Program-ming Languages (POPL), January 2006.

24 Matthew Fluet, Greg Morrisett, and Amal Ahmed. Linear regions are all you need. InEuropean Symposium on Programming (ESOP), pages 7–21, March 2006.

25 Cedric Fournet, Nikhil Swamy, Juan Chen, Pierre-Evariste Dagand, Pierre-Yves Strub,and Benjamin Livshits. Fully abstract compilation to JavaScript. In ACM Symposium onPrinciples of Programming Languages (POPL), Rome, Italy, pages 371–384, 2013.

26 Kathryn E Gray, Robert Bruce Findler, and Matthew Flatt. Fine-grained interoperabilitythrough mirrors and contracts. In ACM Symposium on Object Oriented Programming:Systems, Languages, and Applications (OOPSLA), 2005.

27 Michael Greenberg, Benjamin C. Pierce, and Stephanie Weirich. Contracts made manifest.In ACM Symposium on Principles of Programming Languages (POPL), Madrid, Spain,pages 353–364, January 2010.

28 Jessica Gronski, Kenneth Knowles, Aaron Tomb, Stephen N. Freund, and Cormac Flanagan.Sage: Hybrid checking for flexible specifications. In Scheme and Functional ProgrammingWorkshop (Scheme), pages 93–104, September 2006.

29 Chung-Kil Hur and Derek Dreyer. A Kripke logical relation between ML and assembly.In ACM Symposium on Principles of Programming Languages (POPL), Austin, Texas,January 2011.

Page 16: Verified Compilers for a Multi-Language World

16 Verified Compilers for a Multi-Language World

30 Shriram Krishnamurthi, Peter Walton Hopkins, Jay Mccarthy, Paul T. Graunke, GregPettyjohn, and Matthias Felleisen. Implementation and use of the PLT Scheme web server.2007.

31 Ramana Kumar, Magnus O. Myreen, Michael Norrish, and Scott Owens. CakeML : A veri-fied implementation of ML. In ACM Symposium on Principles of Programming Languages(POPL), San Diego, California, January 2014.

32 Xavier Leroy. Formal certification of a compiler back-end or: programming a compiler witha proof assistant. In ACM Symposium on Principles of Programming Languages (POPL),Charleston, South Carolina, January 2006.

33 Xavier Leroy. A formally verified compiler back-end. Journal of Automated Reasoning,43(4):363–446, 2009.

34 Andreas Lochbihler. Verifying a compiler for Java threads. In European Symposium onProgramming (ESOP), March 2010.

35 Jacob Matthews and Amal Ahmed. Parametric polymorphism through run-time sealing,or, theorems for low, low prices! In European Symposium on Programming (ESOP), pages16–31, March 2008.

36 Jacob Matthews and Robert Bruce Findler. Operational semantics for multi-languageprograms. In ACM Symposium on Principles of Programming Languages (POPL), Nice,France, pages 3–10, January 2007.

37 Yasuhiko Minamide, Greg Morrisett, and Robert Harper. Typed closure conversion. InACM Symposium on Principles of Programming Languages (POPL), St. Petersburg Beach,Florida, pages 271–283, January 1996.

38 Greg Morrisett, Amal Ahmed, and Matthew Fluet. L3 : A linear language with locations.In Typed Lambda Calculi and Applications (TLCA), Nara, Japan, pages 293–307, April2005.

39 Greg Morrisett, David Walker, Karl Crary, and Neal Glew. From System F to typedassembly language. ACM Transactions on Programming Languages and Systems, 21(3):527–568, May 1999.

40 Magnus O. Myreen. Verified just-in-time compiler on x86. In ACM Symposium on Princi-ples of Programming Languages (POPL), Madrid, Spain, January 2010.

41 Aleksandar Nanevski, Amal Ahmed, Greg Morrisett, and Lars Birkedal. Abstract predi-cates and mutable ADTs in Hoare Type Theory. In European Symposium on Programming(ESOP), pages 189–204, March 2007.

42 Aleksandar Nanevski, Anindya Banerjee, and Deepak Garg. Verification of informationflow and access control policies with dependent types. In IEEE Symposium on Securityand Privacy, pages 165–179, 2011.

43 Aleksandar Nanevski, Greg Morrisett, Avi Shinnar, Paul Govereau, and Lars Birkedal.Ynot: Dependent types for imperative programs. In International Conference on FunctionalProgramming (ICFP), Victoria, British Columbia, Canada, pages 229–240, 2006.

44 Georg Neis, Chung-Kil Hur, Jan-Oliver Kaiser, Craig McLaughlin, Derek Dreyer, andViktor Vafeiadis. Pilsner: A compositionally verified compiler for a higher-order imperativelanguage. Available at: http://www.mpi-sws.org/∼dreyer/papers/pilsner/paper.pdf,February 2015.

45 Peter-Michael Osera, Vilhelm Sjöberg, and Steve Zdancewic. Dependent interoperability.In Programming Languages meets Program Verification (PLPV), January 2012.

46 Xinming Ou, Gang Tan, Yitzhak Mandelbaum, and David Walker. Dynamic typing withdependent types. In IFIP International Conference on Theoretical Computer Science, pages437–450, August 2004.

47 James T. Perconti and Amal Ahmed. Verifying an open compiler using multi-languagesemantics. In European Symposium on Programming (ESOP), April 2014.

Page 17: Verified Compilers for a Multi-Language World

A. Ahmed 17

48 Christian Queinnec. Inverting back the inversion of control or, continuations versus page-centric programming. SIGPLAN Not., 38(2):57–64, February 2003.

49 Jaroslav Sevcik, Viktor Vafeiadis, Francesco Zappa Nardelli, Suresh Jagannathan, andPeter Sewell. Relaxed-memory concurrency and verified compilation. In ACM Symposiumon Principles of Programming Languages (POPL), Austin, Texas, 2011.

50 Jeremy G. Siek and Walid Taha. Gradual typing for functional languages. In Scheme andFunctional Programming Workshop (Scheme), pages 81–92, September 2006.

51 Jeremy G. Siek and Walid Taha. Gradual typing for objects. In European Conference onObject-Oriented Programming (ECOOP), 2007.

52 Gordon Stewart, Lennart Beringer, Santiago Cuellar, and Andrew W. Appel. Composi-tional compcert. In ACM Symposium on Principles of Programming Languages (POPL),Mumbai, India, 2015.

53 Nikhil Swamy, Cédric Fournet, Aseem Rastogi, Karthikeyan Bhargavan, Juan Chen, Pierre-Yves Strub, and Gavin M. Bierman. Gradual typing embedded securely in JavaScript. InACM Symposium on Principles of Programming Languages (POPL), San Diego, California,pages 425–438, 2014.

54 Asumu Takikawa, T. Stephen Strickland, Christos Dimoulas, Sam Tobin-Hochstadt, andMatthias Felleisen. Gradual typing for first-class classes. In ACM Symposium on ObjectOriented Programming: Systems, Languages, and Applications (OOPSLA), 2012.

55 The LLVM Development Team. The LLVM reference manual. http://llvm.org/docs/LangRef.html.

56 Jesse Tov. Stateful contracts for affine types. In European Symposium on Programming(ESOP), March 2010.

57 Aaron Turon, Jacob Thamsborg, Amal Ahmed, Lars Birkedal, and Derek Dreyer. Logicalrelations for fine-grained concurrency. In ACM Symposium on Principles of ProgrammingLanguages (POPL), Rome, Italy, pages 201–214, January 2013.

58 Philip Wadler and Robert Bruce Findler. Well-typed programs can’t be blamed. In Euro-pean Symposium on Programming (ESOP), pages 1–16, March 2009.

59 Peng Wang, Santiago Cuellar, and Adam Chlipala. Compiler verification meets cross-language linking via data abstraction. In ACM Symposium on Object Oriented Program-ming: Systems, Languages, and Applications (OOPSLA), October 2014.

60 Bennet Yee, David Sehr, Gregory Dardyk, J. Bradley Chen, Robert Muth, Tavis Ormandy,Shiki Okasaka, Neha Narula, and Nicholas Fullagar. Native Client: A sandbox for portable,untrusted x86 native code. Communications of the ACM, 53(1):91–99, 2010.

61 Jianzhou Zhao, Santosh Nagarakatte, Milo M. K. Martin, and Steve Zdancewic. Formaliz-ing the LLVM intermediate representation for verified program transformations. In ACMSymposium on Principles of Programming Languages (POPL), Philadelphia, Pennsylvania,January 2012.

62 Jianzhou Zhao, Santosh Nagarakatte, Milo M. K. Martin, and Steve Zdancewic. Formalverification of SSA-based optimizations for LLVM. In ACM SIGPLAN Conference onProgramming Language Design and Implementation (PLDI), Seattle, Washington, June2013.