Title: Tim Sheard
1Fundamentals of
Staged Computation
Lecture 4 Staging Interpreters
- Tim Sheard
- Oregon Graduate Institute
CSE 510 Section FSC Winter 2004
2Languages and Calculation
- The calculator language
- datatype Exp
- Var of string
- Add of ExpExp
- Mult of ExpExp
- Const of int
- Local of stringExpExp
- The calculator program (un-staged)
- fun calc term bindings
- case term of
- Var s gt lookup s bindings
- Add(x,y) gt (calc x bindings) (calc y
bindings) - Mult(x,y) gt (calc x bindings) (calc y
bindings) - Const n gt n
- Local(s,x,y) gt calc y ((s,calc x
bindings)bindings)
3Staged solution 1
- Just add annoations
- fun calc2 term bindings
- case term of
- Var s gt lookup s bindings
- Add(x,y) gt lt (calc2 x bindings) (calc2 y
bindings) gt - Mult(x,y) gt lt (calc2 x bindings) (calc2 y
bindings) gt - Const n gt lift n
- Local(s,x,y) gt calc2 y ((s,calc2 x
bindings)bindings)
Note how the local let is inlined.
4Results 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
- lt6 3 4 12gt
- ltintgt
Note how the let structure has disappeared
5Staged solution 2
- fun calc3 term bindings
- case term of
- Var s gt lookup s bindings
- Add(x,y) gt lt (calc3 x bindings) (calc3 y
bindings) gt - Mult(x,y) gt lt (calc3 x bindings) (calc3 y
bindings) gt - Const n gt lift n
- Local(s,x,y) gt
- ltlet val w (calc3 x bindings)
- in (calc3 y ((s,ltwgt)bindings)) endgt
- val ans3 calc3 term
- val ans3
- ltlet val a 3 4
- val b 6 a
- in b 12 endgt ltintgt
6Why 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 -gt ltdatagt -gt ltresultgt
- In generator style
- interp program -gt ltdata -gt resultgt
- 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.
7Interpreter 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.
8The Language
- datatype Exp
- Constant of int ( 5
) - Variable of string ( x )
- Minus of (Exp Exp) ( x - 5 )
- Greater of (Exp Exp) ( x gt 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 xgt0 do x
x - 1 ) - Dec of (string Exp Com) ( Dec x 1 in x
x - 1 )
9Environment ADT
- Lookup string -gt env -gt int
- Set string -gt int -gt env -gt env
- Ext string -gt int -gt env -gt env
- Remove env -gt env
- type env (string int) list
- fun lookup x error ("variable not found
"x) - lookup x ((y,v)zs) if xy then v else
lookup x zs - fun set name v error ("name not found
"name) - set name v ((z as (y,_))zs)
- if namey then (y,v)zs else z(set name v
zs) - fun ext nm v zs (nm,v)zs
- fun remove (zzs) zs
10Simple unstaged interpeters
- Eval0 Exp -gt env -gt int
- Value env -gt int
- The meaning of an exp is a value
- fun eval0 exp env
- case exp of
- Constant n gt n
- Variable s gt lookup s env
- Minus(x,y) gt let val a eval0 x env
- val b eval0 y env
- in a - b end
- Greater(x,y) gt let val a eval0 x env
- val b eval0 y env
- in if a 'gt' b then 1 else 0 end
- Times(x,y) gt let val a eval0 x env
- val b eval0 y env in a
b end
11Interp0 Com -gt env -gt env
The meaning of a Com is an env transformer.
- fun interpret0 stmt env
- case stmt of
- Assign(name,e) gt let val v eval0 e env in
set name v env end - Seq(s1,s2) gt
- let val env1 interpret0 s1 env
- val env2 interpret0 s2 env1
- in env2 end
- If(e,s1,s2) gt
- let val x eval0 e env
- in if x1 then interpret0 s1 env else
interpret0 s2 env end - While(e,body) gt
- let val v eval0 e env
- in if v0
- then env
- else interpret0 (While(e,body))(interpret
0 body env) end - Declare(nm,e,stmt) gt
- let val v eval0 e env
- val env1 ext nm v env
- in remove(interpret0 stmt env1) end
12Getting 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.
13Staging (Binding time) improvements
- type env
- (string int) list
- Note the string and the spine of the list are
known, but the ints are not. - Separate env into two parts. An index (the string
and its position in the spine), and a stack (the
ints)
- type location int
- type index string list
- type stack int list
- eval1 Exp -gt index -gt
- stack -gt int
- interp1 Com -gt index -gt
- stack -gt stack
14Recoding up environments
- type location int
- type index string list
- type stack int list
- pos string -gt index -gt location
- get location -gt stack -gt value
- put location -gt value -gt stack -gt stack
- fun get 1 (xxs) x
- get 0 _ error "No value at index 0."
- get n (xxs) get (n-1) xs
- get n error "Stack is empty"
- fun put 1 v (xxs) (vxs)
- put 0 v _ error "No value at index 0."
- put n v (xxs) x (put (n-1) v xs)
- put n v error "Stack is empty"
15eval1
- fun eval1 exp index stack
- case exp of
- Constant n gt n
- Variable s gt get (pos s index) stack
- Minus(x,y) gt let val a eval1 x index stack
- val b eval1 y index stack
- in a - b end
- Greater(x,y) gt let val a eval1 x index stack
- val b eval1 y index stack
- in if a 'gt' b then 1 else 0 end
- Times(x,y) gt let val a eval1 x index stack
- val b eval1 y index stack
- in a b end
16interp1
- fun interp1 stmt index stack
- case stmt of
- Assign(name,e) gt
- let val v eval1 e index stack
- val loc pos name index
- in put loc v stack end
- Seq(s1,s2) gt
- let val stack1 interp1 s1 index stack
- val stack2 interp1 s2 index stack1
- in stack2 end
- If(e,s1,s2) gt
- let val x eval1 e index stack
- in if x1
- then interp1 s1 index stack
- else interp1 s2 index stack
- end
17Interp1 (cont.)
- fun interp1 stmt index stack
- case stmt of
- . . .
- While(e,body) gt
- let val v eval1 e index stack
- in if v0
- then stack
- else interp1 (While(e,body)) index
- (interp1 body index stack)
- end
- Declare(nm,e,stmt) gt
- let val v eval1 e index stack
- val stack1 v stack
- in tl (interp1 stmt (nmindex) stack1) end
18Adding staging annotations
- fun eval2 exp index stack
- case exp of
- Constant n gt lift n
- Variable s gt ltget (lift (pos s index))
stackgt - Minus(x,y) gt ltlet val a (eval2 x index
stack) - val b (eval2 y index
stack) - in a - b endgt
- Greater(x,y) gt ltlet val a (eval2 x index
stack) - val b (eval2 y index
stack) - in if a 'gt' b then 1 else 0
endgt - Times(x,y) gt ltlet val a (eval2 x index
stack) - val b (eval2 y index
stack) - in a b endgt
19interp2
- fun interp2 stmt index stack
- case stmt of
- Assign(name,e) gt
- ltlet val v (eval2 e index stack)
- in put (lift (pos name index)) v stack endgt
- Seq(s1,s2) gt
- ltlet val stack1 (interp2 s1 index stack)
- val stack2 (interp2 s2 index ltstack1gt)
- in stack2 endgt
- If(e,s1,s2) gt
- ltlet val x (eval2 e index stack)
- in if x1
- then (interp2 s1 index stack)
- else (interp2 s2 index stack)
- endgt
20Interp2 (cont)
- fun interp2 stmt index stack
- case stmt of
- . . .
- While(e,body) gt
- ltlet val v (eval2 e index stack)
- in if v0
- then stack
- else (interp2 (While(e,body)) index
- (interp2 body index stack))
- endgt
- Declare(nm,e,stmt) gt
- ltlet val v (eval2 e index stack)
- val stack1 v stack
- in tl (interp2 stmt (nmindex) ltstack1gt)
endgt
21Using 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 ltfn stack gt (interp2 s0 ltstackgt)gt
22Results
- - val ans2
- lt(fn a gt
- 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)gt
- ltint list -gt int listgt
23Beware
- 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 ltfn stack gt (interp2 s1
ltstackgt)gt
fun interp2 stmt index stack case stmt of . .
. While(e,body) gt ltlet val v (eval2 e
index stack) in if v0 then
stack else (interp2 (While(e,body))
index (interp2 body index
stack)) endgt
24Compare
- While(e,body) gt
- ltlet fun loop stk0
- let val v (eval2 e index ltstk0gt)
- in if v0
- then stk0
- else let val stk1 (interp2
body index ltstk0gt) - in loop stk1 end
- end
- in loop stack endgt
- While(e,body) gt
- ltlet val v (eval2 e index stack)
- in if v0
- then stack
- else (interp2 (While(e,body)) index
- (interp2 body index stack))
- endgt
25Finally, results!
- - val ans3
- lt(fn a gt
- tl
- (tl
- (let fun b c
- let val d get 1 c
- val e if d 'gt' 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)))gt
- ltint list -gt int listgt
26Generate and optimize vsGenerate optimal code
- - dotprod' 3 0,1,2
- val it
- lt(fn a gt (0 nth 0 a)
- (1 nth 1 a)
- (2 nth 2 a) 0)gt
- Rules
- 1x x
- 0x 0
- x0 x
ltfn a gt 0 (nth 1 a)
(2 nth 2 a)gt
27Writing an optimizer is not easy!
- ( rule 1 x0 x )
- ( rule 2 0x 0 )
- ( rule 3 1x x )
- fun opt ltfn x gt (g ltxgt) 0gt opt ltfn y gt (g
ltygt)gt - opt ltfn x gt (g ltxgt) 0 (h ltxgt)gt
- opt ltfn y gt (g ltygt) (h ltygt)gt
- opt ltfn x gt 0 (g ltxgt)gt opt ltfn y gt (g
ltygt)gt - opt ltfn x gt 0 (g ltxgt) (h ltxgt)gt opt
ltfn y gt (h ltygt)gt - opt ltfn x gt (e ltxgt) 0 (g ltxgt)gt opt
ltfn y gt (e ltygt)gt - opt ltfn x gt (e ltxgt) 0 (g ltxgt) (h
ltxgt)gt - opt ltfn y gt (e ltygt) (h ltygt)gt
- opt ltfn x gt 1 (g ltxgt) (h ltxgt)gt
- opt ltfn y gt (g ltygt) (h ltygt)gt
- opt ltfn x gt (e ltxgt) 1 (g ltxgt)gt
- opt ltfn y gt (e ltygt) (g ltygt)gt
- opt ltfn x gt (e ltxgt) 1 (g ltxgt) (h
ltxgt)gt - opt ltfn y gt (e ltygt) (g ltygt) (h
ltygt)gt - opt x x
28Optimal 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.
29Better Approach
- fun dpopt n ys lt0gt
- dpopt n 0 ys lt0gt
- dpopt n 1 ys ltnth (lift n) ysgt
- dpopt n x ys lt (lift x) (nth (lift n)
ys) gt - dpopt n (0xs) ys lt(dpopt (n1) xs ys)gt
- dpopt n (1xs) ys
- lt(nth (lift n) ys) (dpopt (n1) xs ys)gt
- dpopt n (xxs) ys
- lt((lift x) (nth (lift n) ys)) (dpopt
(n1) xs ys)gt - fun gen n xs ltfn ys gt (dpopt n xs ltysgt)gt
- val ans0 gen 5 2,0,1,0,4
- lt(fn a gt 2 nth 5 a nth 7 a 4 nth
9 a)gt
30Conclusion
- 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.
31Things to think about
- Code not very modular
- What if we wanted to add print or some other
feature.