Slides

advertisement
Lecture 4: Staging Interpreters
Tim Sheard
Oregon Graduate Institute
CSE 510 Section FSC
Winter 2004
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
2
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
3
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>
Cse583 Winterl 2002
Note how the let structure has
disappeared
4
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
val ans3
<let val
val
in b %+
= calc3 term [];
=
a = 3 %+ 4
b = 6 %* a
12 end> : <int>
Cse583 Winterl 2002
5
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
6
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
7
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 * Com)
| While of (Exp * Com)
| Dec of (string * Exp * Com)
Cse583 Winterl 2002
(*
5
(*
x
(* 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
*)
:= x - 1
*)
8
Environment ADT
Lookup: string
Set: string ->
Ext: string ->
Remove: env ->
type env
-> env -> int
int -> env -> env
int -> env -> 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;
Cse583 Winterl 2002
9
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
10
Interp0 : Com -> env
-> env
The meaning of a Com is an
fun interpret0 stmt env =
env transformer.
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
11
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
12
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)
Cse583 Winterl 2002
type stack = int list;
eval1 : Exp -> index ->
stack -> int
interp1: Com -> index ->
stack -> stack
13
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
|
|
|
fun
|
|
|
get
get
get
get
put
put
put
put
1
0
n
n
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";
Cse583 Winterl 2002
14
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
val b = eval1 y index
in a - b end
| Greater(x,y) => let val a = eval1 x index
val b = eval1 y index
in if a '>' b then 1 else
| Times(x,y) =>
let val a = eval1 x index
val b = eval1 y index
in a * b end;
Cse583 Winterl 2002
stack
stack
stack
stack
0 end
stack
stack
15
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
16
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
17
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
18
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
19
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
20
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
21
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
22
Beware
val s1 =
Declare("x",Constant 150,
Declare("y",Constant 200,
While(Greater(Variable "x",Constant
Seq(Assign("x",Minus(Variable
Assign("y",Minus(Variable
1))))));
val ans3 = <fn stack => ~(interp2 s1 []
Cse583 Winterl 2002
0),
"x",Constant 1)),
"y",Constant
<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>
23
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
24
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
25
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



1*x = x
0*x = 0
x+0 = x
Cse583 Winterl 2002
<fn a => 0 %+
(%nth 1 a) %+
(2 %* %nth 2 a)>
26
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 <fn
| opt <fn
| opt <fn
opt
| opt <fn
opt
| opt <fn
opt
| opt <fn
opt
| opt x =
x => ~(g
x => ~(g
<fn y =>
x => 0 +
x => 0 *
x => ~(e
x => ~(e
<fn y =>
x => 1 *
<fn y =>
x => ~(e
<fn y =>
x => ~(e
<fn y =>
x;
Cse583 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.



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
28
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
29
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
30
Things to think about
Code not very modular
What if we wanted to add “print” or some
other feature.
Cse583 Winterl 2002
31
Download