Slide 1

Slide 1 text

Spineless Tagless G-Machine Haskell compiler Rafal Lasocha Wroclaw, 28th January 2016 Rafal Lasocha Haskell compiler

Slide 2

Slide 2 text

Spineless Tagless G-Machine Phases of compilation Spineless Tagless G-Machine Rafal Lasocha Haskell compiler

Slide 3

Slide 3 text

Spineless Tagless G-Machine Rafal Lasocha Haskell compiler

Slide 4

Slide 4 text

Spineless Tagless G-Machine Rafal Lasocha Haskell compiler

Slide 5

Slide 5 text

Spineless Tagless G-Machine Rewriting rules {−# RULES ”map/map” forall f g xs. map f (map g xs) = map (f.g) xs ”map/append” forall f xs ys. map f (xs ++ ys) = map f xs ++ map f ys #−} Rafal Lasocha Haskell compiler

Slide 6

Slide 6 text

Spineless Tagless G-Machine Operational semantics map f [] = [] map f (y:ys) = (f y) : (map f ys) map = {} \n {f,xs} -> case xs of Nil {} -> Nil {} Cons {y,ys} -> let fy = {f,y} \u {} -> f {y} mfy = {f,ys} \u {} -> map {f,ys} in Cons {fy,mfy} Lambda form: free vars X args -> expression Rafal Lasocha Haskell compiler

Slide 7

Slide 7 text

Spineless Tagless G-Machine Operational semantics constructors are always saturated no types on the RHS of let(rec) there are only lambda forms only thing which may force us to evaluate, is case expression Rafal Lasocha Haskell compiler

Slide 8

Slide 8 text

Spineless Tagless G-Machine Operational semantics Updateable flag: boolean, either ”u” (updatable) or ”n” (not updatable) Our language is lazy, so after evaluation of suspended computation, we should update something, to not evaluate the same code again but maybe already during compilation we can deduce, that we won’t have to update some closures? functions with non-empty argument list - they don’t need updating partial applications - the same ( vs \n {} -> f {x1, ..., xm} ) saturated constructor - the same thunks - these are rather updatable Rafal Lasocha Haskell compiler

Slide 9

Slide 9 text

Spineless Tagless G-Machine Operational semantics few stacks: argument stack, return stack, update stack, heap, global environment value, on the argument stack are either heap addresses or primitive integers 4 possible states of compiler Eval e p - evaluate expression e in environemnt p Enter a - apply the closure at address a to the arguments on the argument stack ReturnCon c ws - Return the constructor c applied to values ws to the continuation on the return stack ReturnInt k - Return the primitive integer k to the continuation on the return stack Rafal Lasocha Haskell compiler

Slide 10

Slide 10 text

Spineless Tagless G-Machine Operational semantics Initial state Rafal Lasocha Haskell compiler

Slide 11

Slide 11 text

Spineless Tagless G-Machine Operational semantics Function application Eval e p - evaluate expression e in environment p Rafal Lasocha Haskell compiler

Slide 12

Slide 12 text

Spineless Tagless G-Machine Operational semantics Enter non-updatable closure Enter a - apply the closure at address a to the arguments on the argument stack Rafal Lasocha Haskell compiler

Slide 13

Slide 13 text

Spineless Tagless G-Machine Operational semantics Evaluate case statement ReturnCon c ws - Return the constructor c applied to values ws to the continuation on the return stack Rafal Lasocha Haskell compiler

Slide 14

Slide 14 text

Spineless Tagless G-Machine Operational semantics Applying updatable closures assuming a is an address of the updatable closure we try to Enter (Enter a) clean argument and return stacks, and save triple (”update frame”) (as, rs, a) to the update frames stack now just evaluate and wait until our program ”crash” because lack of values in either return stack or argument stack Rafal Lasocha Haskell compiler

Slide 15

Slide 15 text

Spineless Tagless G-Machine Operational semantics Applying updatable closures Rafal Lasocha Haskell compiler

Slide 16

Slide 16 text

Spineless Tagless G-Machine Operational semantics Generating code target language will be C it’s not perfect, it’s not very efficient, but it works mapping haskell functions to C functions doesn’t work each closure becomes C function, but without arguments stacks will be managed by us but subsequently entering functions will cause our stack to grow... while (TRUE) { cont = (*cont)(); } Rafal Lasocha Haskell compiler

Slide 17

Slide 17 text

Spineless Tagless G-Machine Operational semantics Closure representation in memory Rafal Lasocha Haskell compiler

Slide 18

Slide 18 text

Spineless Tagless G-Machine Operational semantics Garbage collection two space GC evacuating code handles moving specific closure from 1st space to 2nd it also overrides in 1st space with forwarding pointer ... and returns new closure address to the caller scavenging code knows how to iterate evacuating code on each pointer in closure it also replace old pointers with new ones forwarding pointers - simple return of associated pointer Rafal Lasocha Haskell compiler

Slide 19

Slide 19 text

Spineless Tagless G-Machine Operational semantics Something about translating to C STG has two stack pointers - one for integers, and one for pointers. # apply3 = {} \n {f,x} -> f {x,x,x} Node = SpA[0] t = SpA[1] SpA[0] = t SpA[-1] = t SpA = SpA - 1 ENTER( Node ) # Node is a closure, not code ptr Rafal Lasocha Haskell compiler