Lecture 4 Staging Interpreters Tim Sheard Oregon Graduate

  • Slides: 31
Download presentation
Lecture 4: Staging Interpreters Tim Sheard Oregon Graduate Institute CSE 510 Section FSC Winter

Lecture 4: Staging Interpreters Tim Sheard Oregon Graduate Institute CSE 510 Section FSC Winter 2004 Algorithms CSE 532, Spring 2000

Languages and Calculation The calculator language datatype Exp = Var of string | Add

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; 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); Cse 583 Winterl 2002 2

Staged solution 1 Just add annoations fun calc 2 term bindings = case term

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

Results 1 val term = Local("x", Add(Const 3, Const 4), Local("y", Mult(Const 6, Var

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

Staged solution 2 fun calc 3 term bindings = case term of Var s

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

Why Stage Interpreters? Interpreters are the classic example for using staging. Interpreter takes a

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. n In transformer style w interp: program -> <data> -> <result> n In generator style w 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. Cse 583 Winterl 2002 6

Interpreter Characteristics Recursive descent over the syntax of the language. Ideally , the meaning

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 n n 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. Cse 583 Winterl 2002 7

The Language datatype Exp = Constant of int | Variable of string | Minus

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

Environment ADT Lookup: string Set: string -> Ext: string -> Remove: env -> type

Environment ADT Lookup: string Set: string -> Ext: string -> Remove: env -> type env -> int -> env 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; Cse 583 Winterl 2002 9

Simple unstaged interpeters Eval 0 : : Exp -> env -> int Value =

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

Interp 0 : Com -> env The meaning of a Com is an fun

Interp 0 : Com -> env The meaning of a Com is an fun interpret 0 stmt env = env transformer. case stmt of Assign(name, e) => let val v = eval 0 e env in set name v end | Seq(s 1, s 2) => let val env 1 = interpret 0 s 1 env val env 2 = interpret 0 s 2 env 1 in env 2 end | If(e, s 1, s 2) => let val x = eval 0 e env in if x=1 then interpret 0 s 1 env else interpret 0 s 2 env end | While(e, body) => let val v = eval 0 e env in if v=0 then env else interpret 0 (While(e, body))(interpret 0 body env) end | Declare(nm, e, stmt) => let val v = eval 0 e env val env 1 = ext nm v env in remove(interpret 0 stmt env 1) end; Cse 583 Winterl 2002 11

Getting ready to stage What is the structure of the source language? n Exp

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

Staging (Binding time) improvements type env = (string * int) list; type location =

Staging (Binding time) improvements type env = (string * int) list; type location = int; type index = string 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) Cse 583 Winterl 2002 type stack = int list; eval 1 : Exp -> index -> stack -> interp 1: Com -> index -> stack 13

Recoding up environments type location = int; type index = string list; type stack

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 fun | | | get get put put 1 0 n n (x: : xs) = x _ = error "No value at index 0. " (x: : xs) = get (n-1) xs [] = error "Stack is empty"; v (x: : xs) = (v: : xs) v _ = error "No value at index 0. " v (x: : xs) = x : : (put (n-1) v xs) v [] = error "Stack is empty"; Cse 583 Winterl 2002 14

eval 1 fun eval 1 exp index stack = case exp of Constant n

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

interp 1 fun interp 1 stmt index stack = case stmt of Assign(name, e)

interp 1 fun interp 1 stmt index stack = case stmt of Assign(name, e) => let val v = eval 1 e index stack val loc = pos name index in put loc v stack end | Seq(s 1, s 2) => let val stack 1 = interp 1 s 1 index stack val stack 2 = interp 1 s 2 index stack 1 in stack 2 end | If(e, s 1, s 2) => let val x = eval 1 e index stack in if x=1 then interp 1 s 1 index stack else interp 1 s 2 index stack end Cse 583 Winterl 2002 16

Interp 1 (cont. ) fun interp 1 stmt index stack = case stmt of.

Interp 1 (cont. ) fun interp 1 stmt index stack = case stmt of. . . | While(e, body) => let val v = eval 1 e index stack in if v=0 then stack else interp 1 (While(e, body)) index (interp 1 body index stack) end | Declare(nm, e, stmt) => let val v = eval 1 e index stack val stack 1 = v : : stack in tl (interp 1 stmt (nm: : index) stack 1) end; Cse 583 Winterl 2002 17

Adding staging annotations fun eval 2 exp index stack = case exp of Constant

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

interp 2 fun interp 2 stmt index stack = case stmt of Assign(name, e)

interp 2 fun interp 2 stmt index stack = case stmt of Assign(name, e) => <let val v = ~(eval 2 e index stack) in put ~(lift (pos name index)) v ~stack end> | Seq(s 1, s 2) => <let val stack 1 = ~(interp 2 s 1 index stack) val stack 2 = ~(interp 2 s 2 index <stack 1>) in stack 2 end> | If(e, s 1, s 2) => <let val x = ~(eval 2 e index stack) in if x=1 then ~(interp 2 s 1 index stack) else ~(interp 2 s 2 index stack) end> Cse 583 Winterl 2002 19

Interp 2 (cont) fun interp 2 stmt index stack = case stmt of. .

Interp 2 (cont) fun interp 2 stmt index stack = case stmt of. . . | While(e, body) => <let val v = ~(eval 2 e index stack) in if v=0 then ~stack else ~(interp 2 (While(e, body)) index (interp 2 body index stack)) end> | Declare(nm, e, stmt) => <let val v = ~(eval 2 e index stack) val stack 1 = v : : ~stack in tl ~(interp 2 stmt (nm: : index) <stack 1>) end>; Cse 583 Winterl 2002 20

Using the staged code val s 0 = Declare("x", Constant 150, Declare("y", Constant 200,

Using the staged code val s 0 = Declare("x", Constant 150, Declare("y", Constant 200, Seq(Assign("x", Minus(Variable "x", Constant 1)), Assign("y", Minus(Variable "y", Constant 1))))); val ans 2 = <fn stack => ~(interp 2 s 0 [] <stack>)>; Cse 583 Winterl 2002 21

Results -| val ans 2 = <(fn a => let val b = 150

Results -| val ans 2 = <(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)> : <int list -> int list> Cse 583 Winterl 2002 22

Beware val s 1 = Declare("x", Constant 150, Declare("y", Constant 200, While(Greater(Variable "x", Constant

Beware val s 1 = Declare("x", Constant 150, Declare("y", Constant 200, While(Greater(Variable "x", Constant Seq(Assign("x", Minus(Variable Assign("y", Minus(Variable 1)))))); val ans 3 = <fn stack => ~(interp 2 s 1 [] Cse 583 Winterl 2002 0), "x", Constant 1)), "y", Constant <stack>)>; fun interp 2 stmt index stack = case stmt of. . . | While(e, body) => <let val v = ~(eval 2 e index stack) in if v=0 then ~stack else ~(interp 2 (While(e, body)) index (interp 2 body index stack)) end> 23

Compare | While(e, body) => <let fun loop stk 0 = let val v

Compare | While(e, body) => <let fun loop stk 0 = let val v = ~(eval 2 e index <stk 0>) in if v=0 then stk 0 else let val stk 1 = ~(interp 2 body index <stk 0>) in loop stk 1 end in loop ~stack end> | While(e, body) => <let val v = ~(eval 2 e index stack) in if v=0 then ~stack else ~(interp 2 (While(e, body)) index (interp 2 body index stack)) end> Cse 583 Winterl 2002 24

Finally, results! -| val ans 3 = <(fn a => %tl (let fun b

Finally, results! -| val ans 3 = <(fn a => %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 in b (200 : : 150 : : a) end)))> : <int list -> int list> Cse 583 Winterl 2002 25

Generate and optimize vs Generate optimal code -| dotprod' 3 [0, 1, 2]; val

Generate and optimize vs Generate 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 n n n 1*x = x 0*x = 0 x+0 = x Cse 583 Winterl 2002 <fn a => 0 %+ (%nth 1 a) %+ (2 %* %nth 2 a)> 26

Writing an optimizer is not easy! (* rule 1: x+0 = x *) (*

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

Optimal Generation vs Post Generation Optimizing Complexity from several sources. n n n Walking

Optimal Generation vs Post Generation Optimizing Complexity from several sources. n n n 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. Cse 583 Winterl 2002 28

Better Approach fun | | | dpopt n [] ys = <0> dpopt n

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 ans 0 = gen 5 [2, 0, 1, 0, 4]; <(fn a => 2 %* %nth 5 a %+ %nth 7 a %+ 4 %* %nth 9 a)> Cse 583 Winterl 2002 29

Conclusion Interpreters need binding time improvements n n Split partially static data structures into

Conclusion Interpreters need binding time improvements n n 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. Cse 583 Winterl 2002 30

Things to think about Code not very modular What if we wanted to add

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