Prana - chrisdone.com · Prana begins its work here. Compiles with GHC to STG. Updates the name...

19
Prana Haskell Interpreter

Transcript of Prana - chrisdone.com · Prana begins its work here. Compiles with GHC to STG. Updates the name...

PranaHaskell Interpreter

Goals● Hotswappable modules while the program is running a la Emacs.● Gracefully handle data types and functions changing their shape at runtime.

○ An exception is better than a segfault.

● Be able to print any object whatsoever (aside from Addr#’s internal contents), in an interactive way, a la JS Console’s object printer.

● Easily set breakpoints.● Handle exceptions without unwinding the stack (CL’s conditions).● Track and see how many threads are running, what vars they’re waiting on,

etc.● Permit a JS interpreter, supporting most of the basic primops -- useful for

teaching, code demonstrations, etc.

Printing objects● Interactively expand objects, arrays, etc.● Don’t rely on Show.

The pipeline

GHC pipeline refresher

GHC pipeline + prana

GHCi

How GHCi works● GHCi takes Core and produces bytecode from there.● All primops are called via GHC.PrimopWrappers which are curried wrappers

to primops.● GHCi’s memory representation is the same as regular compiled GHC, this is

why GHCi can re-use the primops without ceremony.

What’s wrong with GHCi?● Nothing is wrong. It’s efficient and does the job well.● However, the following downsides exist:

○ GHC’s runtime representation of objects erases type information and does not care to check whether a field exists or function has enough arguments, etc.

■ Result: wrong code segfaults.○ Type erasure is not good for an interpreter that you want to be able to update in place.○ The interpreter is written in C, and ain’t pretty.

Prana

We use STG● It’s a cleaned up, restricted form of Core● At this point you don’t have to worry about anything else● PrimOps, FFI calls, user primops, are fully saturated● There are no lambdas, only closures/thunks● We want to implement primops ourselves

○ Re-using GHC’s implementations for all the simple types (Int#, Word#, Char#, ByteArray#)○ Write our own implementation for complex functions (fork#, raise#, catch#, etc.)

■ Keep track of threads forked■ Handle exceptions especially with a better UX

Clean STG

● An STG type that doesn’t depend directly on GHC○ Protects from API changes○ Simplifies dependency foot-print○ Full control over instances, representation○ Tighter restrictions on structure (e.g. we have an improved case expression constructor)

● We serialize the STG to disk as binary● Each package gets written as a binary blob file foo.prana● Includes ghc-prim, integer-gmp and base

Implementation

An alternative GHC frontend● Copy the Main.hs from GHC● Add an extra compile step to generate prana code● If PRANA_MODE=install, then install libraries after finishing compiling.

○ ghc-prim.prana○ integer-gmp.prana○ base.prana○ etc

Looks likeBuilding library for ghc-prim-0.5.2.0..[1 of 8] Compiling GHC.Types[2 of 8] Compiling GHC.IntWord64[3 of 8] Compiling GHC.CString[4 of 8] Compiling GHC.Tuple[5 of 8] Compiling GHC.PrimopWrappers[6 of 8] Compiling GHC.Debug[7 of 8] Compiling GHC.Magic[8 of 8] Compiling GHC.Classes[1 of 8] Converting GHC.Types[2 of 8] Converting GHC.IntWord64[3 of 8] Converting GHC.CString[4 of 8] Converting GHC.Tuple[5 of 8] Converting GHC.PrimopWrappers[6 of 8] Converting GHC.Debug[7 of 8] Converting GHC.Magic[8 of 8] Converting GHC.ClassesUpdating index ... Writing library ghc-prim ...

Rewrites the STG to our own AST.

Prana begins its work here. Compiles with GHC to STG.

Updates the name index (a binary file) with newly seen names.

Writes ghc-prim.prana bytecode file.

Primops

Primitive data types

● End with #.○ Char#○ Int#○ Etc.

● Built-in to GHC● Unboxed● Have a number of operations on them

Char#

The Char type is:

data Char = C# Char#

Operations are simple. We can re-use these functions in our interpreter.

Deriving primops● Most primop implementations are derivable in terms of the host interpreter’s

runtime via GHC.Prim:○ +#, etc.○ newMutVar# :: a -> State# s -> (#State# s, MutVar# s a#)

● The remaining few are “interesting”:○ fork#, raise#, catch#, tagToEnum, etc.○ And we don’t want to derive those, we’ll implement those manually.

● The remaining primops are SIMD, which is only supported on the LLVM backend anyway, so we won’t bother implementing them now.