1 / 31

Tim Sheard Oregon Graduate Institute

Fundamentals of. Staged Computation. Tim Sheard Oregon Graduate Institute. Lecture 4: Staging Interpreters. CSE 510 Section FSC Winter 2004. Languages and Calculation. The calculator language datatype Exp = Var of string | Add of Exp*Exp

oliana
Download Presentation

Tim Sheard Oregon Graduate Institute

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Fundamentals of Staged Computation Tim Sheard Oregon Graduate Institute Lecture 4: Staging Interpreters CSE 510 Section FSC Winter 2004

  2. Languages and Calculation • The calculator language datatype Exp = Var of string | Add of Exp*Exp | Mult of Exp*Exp | Const of int | Local of string*Exp*Exp; • The calculator program (un-staged) fun calc term bindings = case term of Var s => lookup s bindings | Add(x,y) => (calc x bindings) + (calc y bindings) | Mult(x,y) => (calc x bindings) * (calc y bindings) | Const n => n | Local(s,x,y) => calc y ((s,calc x bindings)::bindings); Cse583 Winterl 2002

  3. Staged solution 1 • Just add annoations fun calc2 term bindings = case term of Var s => lookup s bindings | Add(x,y) => < ~(calc2 x bindings) + ~(calc2 y bindings) > | Mult(x,y) => < ~(calc2 x bindings) * ~(calc2 y bindings) > | Const n => lift n | Local(s,x,y) => calc2 y ((s,calc2 x bindings)::bindings); Note how the local “let” is inlined. Cse583 Winterl 2002

  4. Results 1 val term = Local("x",Add(Const 3, Const 4), Local("y",Mult(Const 6,Var "x"), Add(Var "y",Const 12))); val ans2 = calc2 term []; -| ans2; val it = <6 %* 3 %+ 4 %+ 12> : <int> Note how the let structure has disappeared Cse583 Winterl 2002

  5. Staged solution 2 fun calc3 term bindings = case term of Var s => lookup s bindings | Add(x,y) => < ~(calc3 x bindings) + ~(calc3 y bindings) > | Mult(x,y) => < ~(calc3 x bindings) * ~(calc3 y bindings) > | Const n => lift n | Local(s,x,y) => <let val w = ~(calc3 x bindings) in ~(calc3 y ((s,<w>)::bindings)) end>; val ans3 = calc3 term []; val ans3 = <let val a = 3 %+ 4 val b = 6 %* a in b %+ 12 end> : <int> Cse583 Winterl 2002

  6. Why Stage Interpreters? • Interpreters are the classic example for using staging. • Interpreter takes a program, its data, and returns the result of applying the program to its data. • In transformer style • interp: program -> <data> -> <result> • In generator style • interp: program -> <data -> result> • Avoids the overhead of manipulating the data that represents the program each time it is applied. • Dramatic speedups are possible. • Abstract way of building a compiler. Cse583 Winterl 2002

  7. Interpreter Characteristics • Recursive descent over the syntax of the language. • Ideally , the meaning of an “expression” in a language should depend only on the meaning of its constituent “sub-expressions”. • Structure of the “syntax” is what guides the interpreter. • One “case” for each kind of expression or statement • The use of an environment abstract datatype • encodes the meaning of variables • encodes the “scope” of binding constructs • The use of a “value” or semantic meaning type as the return type of the interpreter. Cse583 Winterl 2002

  8. The Language datatype Exp = Constant of int (* 5 *) | Variable of string (* x *) | Minus of (Exp * Exp) (* x - 5 *) | Greater of (Exp * Exp) (* x > 1 *) | Times of (Exp * Exp) ; (* x * 4 *) datatype Com = Assign of (string * Exp) (* x := 1 *) | Seq of (Com * Com) (* { x := 1; y := 2 } *) | Cond of (Exp * Com * Com) (* if x then x := 1 else y := 0 *) | While of (Exp * Com) (* while x>0 do x := x - 1 *) | Dec of (string * Exp * Com) (* Dec x = 1 in x := x - 1 *) Cse583 Winterl 2002

  9. Environment ADT • Lookup: string -> env -> int • Set: string -> int -> env -> env • Ext: string -> int -> env -> env • Remove: env -> env type env = (string * int) list ; fun lookup x [] = error ("variable not found: "^x) | lookup x ((y,v)::zs) = if x=y then v else lookup x zs; fun set name v [] = error ("name not found: "^name) | set name v ((z as (y,_))::zs) = if name=y then (y,v)::zs else z::(set name v zs); fun ext nm v zs = (nm,v)::zs; fun remove (z::zs) = zs; Cse583 Winterl 2002

  10. Simple unstaged interpeters • Eval0 :: Exp -> env -> int • Value = env -> int • The meaning of an exp is a value fun eval0 exp env = case exp of Constant n => n | Variable s => lookup s env | Minus(x,y) => let val a = eval0 x env val b = eval0 y env in a - b end | Greater(x,y) => let val a = eval0 x env val b = eval0 y env in if a '>' b then 1 else 0 end | Times(x,y) => let val a = eval0 x env val b = eval0 y env in a * b end; Cse583 Winterl 2002

  11. Interp0 : Com -> env -> env The meaning of a Com is an env transformer. fun interpret0 stmt env = case stmt of Assign(name,e) => let val v = eval0 e env in set name v env end | Seq(s1,s2) => let val env1 = interpret0 s1 env val env2 = interpret0 s2 env1 in env2 end | If(e,s1,s2) => let val x = eval0 e env in if x=1 then interpret0 s1 env else interpret0 s2 env end | While(e,body) => let val v = eval0 e env in if v=0 then env else interpret0 (While(e,body))(interpret0 body env) end | Declare(nm,e,stmt) => let val v = eval0 e env val env1 = ext nm v env in remove(interpret0 stmt env1) end; Cse583 Winterl 2002

  12. Getting ready to stage • What is the structure of the source language? • Exp and Com • What is the structure of the target language? • MetaML with “let” and operations on environments and arithmetic • What are the staging issues? • What is completely known at compile-time • Exp, Com, part of the environment (the names but not the values) • How do I connect the structure of the source and target languages. Cse583 Winterl 2002

  13. type env = (string * int) list; Note the string and the “spine” of the list are known, but the int’s are not. Separate env into two parts. An index (the string and its position in the spine), and a stack (the int’s) type location = int; type index = string list; type stack = int list; eval1 : Exp -> index -> stack -> int interp1: Com -> index -> stack -> stack Staging (Binding time) improvements Cse583 Winterl 2002

  14. Recoding up environments • type location = int; • type index = string list; • type stack = int list; • pos :: string -> index -> location • get : location -> stack -> value • put: location -> value -> stack -> stack fun get 1 (x::xs) = x | get 0 _ = error "No value at index 0." | get n (x::xs) = get (n-1) xs | get n [] = error "Stack is empty"; fun put 1 v (x::xs) = (v::xs) | put 0 v _ = error "No value at index 0." | put n v (x::xs) = x :: (put (n-1) v xs) | put n v [] = error "Stack is empty"; Cse583 Winterl 2002

  15. eval1 fun eval1 exp index stack = case exp of Constant n => n | Variable s => get (pos s index) stack | Minus(x,y) => let val a = eval1 x index stack val b = eval1 y index stack in a - b end | Greater(x,y) => let val a = eval1 x index stack val b = eval1 y index stack in if a '>' b then 1 else 0 end | Times(x,y) => let val a = eval1 x index stack val b = eval1 y index stack in a * b end; Cse583 Winterl 2002

  16. interp1 fun interp1 stmt index stack = case stmt of Assign(name,e) => let val v = eval1 e index stack val loc = pos name index in put loc v stack end | Seq(s1,s2) => let val stack1 = interp1 s1 index stack val stack2 = interp1 s2 index stack1 in stack2 end | If(e,s1,s2) => let val x = eval1 e index stack in if x=1 then interp1 s1 index stack else interp1 s2 index stack end Cse583 Winterl 2002

  17. Interp1 (cont.) fun interp1 stmt index stack = case stmt of . . . | While(e,body) => let val v = eval1 e index stack in if v=0 then stack else interp1 (While(e,body)) index (interp1 body index stack) end | Declare(nm,e,stmt) => let val v = eval1 e index stack val stack1 = v :: stack in tl (interp1 stmt (nm::index) stack1) end; Cse583 Winterl 2002

  18. Adding staging annotations fun eval2 exp index stack = case exp of Constant n => lift n | Variable s => <get ~(lift (pos s index)) ~stack> | Minus(x,y) => <let val a = ~(eval2 x index stack) val b = ~(eval2 y index stack) in a - b end> | Greater(x,y) => <let val a = ~(eval2 x index stack) val b = ~(eval2 y index stack) in if a '>' b then 1 else 0 end> | Times(x,y) => <let val a = ~(eval2 x index stack) val b = ~(eval2 y index stack) in a * b end>; Cse583 Winterl 2002

  19. interp2 fun interp2 stmt index stack = case stmt of Assign(name,e) => <let val v = ~(eval2 e index stack) in put ~(lift (pos name index)) v ~stack end> | Seq(s1,s2) => <let val stack1 = ~(interp2 s1 index stack) val stack2 = ~(interp2 s2 index <stack1>) in stack2 end> | If(e,s1,s2) => <let val x = ~(eval2 e index stack) in if x=1 then ~(interp2 s1 index stack) else ~(interp2 s2 index stack) end> Cse583 Winterl 2002

  20. Interp2 (cont) fun interp2 stmt index stack = case stmt of . . . | While(e,body) => <let val v = ~(eval2 e index stack) in if v=0 then ~stack else ~(interp2 (While(e,body)) index (interp2 body index stack)) end> | Declare(nm,e,stmt) => <let val v = ~(eval2 e index stack) val stack1 = v :: ~stack in tl ~(interp2 stmt (nm::index) <stack1>) end>; Cse583 Winterl 2002

  21. Using the staged code val s0 = Declare("x",Constant 150, Declare("y",Constant 200, Seq(Assign("x",Minus(Variable "x",Constant 1)), Assign("y",Minus(Variable "y",Constant 1))))); val ans2 = <fn stack => ~(interp2 s0 [] <stack>)>; Cse583 Winterl 2002

  22. Results -| val ans2 = <(fn a => let val b = 150 val c = b :: a in %tl (let val d = 200 val e = d :: c in %tl (let val f = %get 1 e val g = 1 val h = f %- g val i = %put 1 h e val j = %get 0 i val k = 1 val l = j %- k val m = %put 0 l i in m end) end) end)> : <int list -> int list> Cse583 Winterl 2002

  23. Beware val s1 = Declare("x",Constant 150, Declare("y",Constant 200, While(Greater(Variable "x",Constant 0), Seq(Assign("x",Minus(Variable "x",Constant 1)), Assign("y",Minus(Variable "y",Constant 1)))))); val ans3 = <fn stack => ~(interp2 s1 [] <stack>)>; fun interp2 stmt index stack = case stmt of . . . | While(e,body) => <let val v = ~(eval2 e index stack) in if v=0 then ~stack else ~(interp2 (While(e,body)) index (interp2 body index stack)) end> Cse583 Winterl 2002

  24. Compare | While(e,body) => <let fun loop stk0 = let val v = ~(eval2 e index <stk0>) in if v=0 then stk0 else let val stk1 = ~(interp2 body index <stk0>) in loop stk1 end end in loop ~stack end> | While(e,body) => <let val v = ~(eval2 e index stack) in if v=0 then ~stack else ~(interp2 (While(e,body)) index (interp2 body index stack)) end> Cse583 Winterl 2002

  25. Finally, results! -| val ans3 = <(fn a => %tl (%tl (let fun b c = let val d = %get 1 c val e = if d %'>' 0 then 1 else 0 in if e %= 0 then c else let val f = %get 1 c val g = f %- 1 val h = %put 1 g c val i = %get 0 h val j = i %- 1 val k = %put 0 j h in b k end end in b (200 :: 150 :: a) end)))> : <int list -> int list> Cse583 Winterl 2002

  26. Generate and optimize vsGenerate optimal code -| dotprod' 3 [0,1,2]; val it = <(fn a => (0 %* %nth 0 a) %+ (1 %* %nth 1 a) %+ (2 %* %nth 2 a) %+ 0)> • Rules • 1*x = x • 0*x = 0 • x+0 = x <fn a => 0 %+ (%nth 1 a) %+ (2 %* %nth 2 a)> Cse583 Winterl 2002

  27. Writing an optimizer is not easy! (* rule 1: x+0 = x *) (* rule 2: 0*x = 0 *) (* rule 3: 1*x = x *) fun opt <fn x => ~(g <x>) + 0> = opt <fn y => ~(g <y>)> | opt <fn x => ~(g <x>) + 0 + ~(h <x>)> = opt <fn y => ~(g <y>) + ~(h <y>)> | opt <fn x => 0 + ~(g <x>)> = opt <fn y => ~(g <y>)> | opt <fn x => 0 * ~(g <x>) + ~(h <x>)> = opt <fn y => ~(h <y>)> | opt <fn x => ~(e <x>) + 0 * ~(g <x>)> = opt <fn y => ~(e <y>)> | opt <fn x => ~(e <x>) + 0 * ~(g <x>) + ~(h <x>)> = opt <fn y => ~(e <y>) + ~(h <y>)> | opt <fn x => 1 * ~(g <x>) + ~(h <x>)> = opt <fn y => ~(g <y>) + ~(h <y>)> | opt <fn x => ~(e <x>) + 1 * ~(g <x>)> = opt <fn y => ~(e <y>) + ~(g <y>)> | opt <fn x => ~(e <x>) + 1 * ~(g <x>) + ~(h <x>)> = opt <fn y => ~(e <y>) + ~(g <y>) + ~(h <y>)> | opt x = x; Cse583 Winterl 2002

  28. Optimal Generation vs Post Generation Optimizing • Complexity from several sources. • Walking deep over the tree • Complexity of pattern matching against code (vs matching against values) • Dealing with binding occurrences • Optimal generation can be directed by values at generation time. Cse583 Winterl 2002

  29. Better Approach fun dpopt n [] ys = <0> | dpopt n [0] ys = <0> | dpopt n [1] ys = <nth ~(lift n) ~ys> | dpopt n [x] ys = < ~(lift x) * (nth ~(lift n) ~ys) > | dpopt n (0::xs) ys = <~(dpopt (n+1) xs ys)> | dpopt n (1::xs) ys = <(nth ~(lift n) ~ys) + ~(dpopt (n+1) xs ys)> | dpopt n (x::xs) ys = <(~(lift x) * (nth ~(lift n) ~ys)) + ~(dpopt (n+1) xs ys)>; fun gen n xs = <fn ys => ~(dpopt n xs <ys>)>; val ans0 = gen 5 [2,0,1,0,4]; <(fn a => 2 %* %nth 5 a %+ %nth 7 a %+ 4 %* %nth 9 a)> Cse583 Winterl 2002

  30. Conclusion • Interpreters need binding time improvements • Split partially static data structures into two parts. E.g. Env = index + stack • Split recursion so that it depends only on structure of the data being interpreted • Use transformer style for easy construction • Let lifting makes code look nice. Cse583 Winterl 2002

  31. Things to think about • Code not very modular • What if we wanted to add “print” or some other feature. Cse583 Winterl 2002

More Related