Home My Page Projects Code Snippets Project Openings SML/NJ
Summary Activity Forums Tracker Lists Tasks Docs Surveys News SCM Files

SCM Repository

[smlnj] Diff of /sml/trunk/src/compiler/FLINT/opt/lcontract.sml
ViewVC logotype

Diff of /sml/trunk/src/compiler/FLINT/opt/lcontract.sml

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 24, Thu Mar 12 00:49:58 1998 UTC revision 45, Sun Mar 22 20:11:09 1998 UTC
# Line 3  Line 3 
3    
4  signature LCONTRACT =  signature LCONTRACT =
5  sig  sig
6    val lcontract : Lambda.lexp -> Lambda.lexp    val lcontract : FLINT.prog -> FLINT.prog
7  end  end
8    
9  structure LContract : LCONTRACT =  structure LContract : LCONTRACT =
10  struct  struct
11    
12  local structure DI = DebIndex  local structure DI = DebIndex
13        open Access Lambda        structure DA = Access
14          structure LT = LtyExtern
15          open FLINT
16  in  in
17    
18  val sameName = LambdaVar.sameName  fun bug s = ErrorMsg.impossible ("LContract: "^s)
19  fun bug s = ErrorMsg.impossible ("LambdaOpt: "^s)  val say = Control.Print.say
20  val ident = fn le => le  val ident = fn x => x
21  fun all p (a::r) = p a andalso all p r | all p nil = true  fun all p (a::r) = p a andalso all p r | all p nil = true
22    
23  fun isDiff(x, VAR v) = (x <> v)  fun isDiffs (vs, us) =
24    | isDiff(x, GENOP({default,table}, _, _, _)) =    let fun h (VAR x) = List.all (fn y => (y<>x)) vs
25        (x <> default) andalso (all (fn (_, w) => (x <> w)) table)          | h _ = true
26    | isDiff _ = true     in List.all h us
27      end
28    
29    fun isEqs (vs, us) =
30      let fun h (v::r, (VAR x)::z) = if v = x then h(r, z) else false
31            | h ([], []) = true
32            | h _ = false
33       in h(vs, us)
34      end
35    
36  datatype info  datatype info
37    = CompExp    = SimpVal of value
   | SimpVal of value  
38    | ListExp of value list    | ListExp of value list
39    | FunExp of lvar * lty * lexp    | FunExp of DI.depth * lvar list * lexp
40    | SimpExp    | ConExp of dcon * tyc list * value
41      | StdExp
 fun isPure(SVAL _) = true  
   | isPure(RECORD _) = true  
   | isPure(SRECORD _) = true  
   | isPure(VECTOR _) = true  
   | isPure(SELECT _) = true  
   | isPure(FN _) = true  
   | isPure(TFN _) = true  
   | isPure(CON _) = true  
   | isPure(DECON _) = true (* this can be problematic *)  
   | isPure(ETAG _) = true  
   | isPure(PACK _) = true  
   | isPure(WRAP _) = true  
   | isPure(UNWRAP _) = true  
   | isPure(SWITCH(v, _, ces, oe)) =  
       let fun g((_,x)::r) = if isPure x then g r else false  
             | g [] = case oe of NONE => true | SOME z => isPure z  
        in g ces  
       end  
   | isPure _ = false  
   (*** the cases for FIX and LET have already been flattened, thus  
        they should not occur ***)  
42    
43  exception LContPass1  exception LContPass1
44  fun pass1 lexp =  fun pass1 fdec =
45    let val zz : (DI.depth option) Intmap.intmap = Intmap.new(32, LContPass1)    let val zz : (DI.depth option) Intmap.intmap = Intmap.new(32, LContPass1)
46        val add = Intmap.add zz        val add = Intmap.add zz
47        val get = Intmap.map zz        val get = Intmap.map zz
# Line 65  Line 53 
53               val _ = rmv x               val _ = rmv x
54            in case s            in case s
55                of NONE => ()                of NONE => ()
56                   | SOME _ => add(x, NONE)  (* depth no longer matters *)
57    (*
58                 | SOME d => if (d=nd) then add(x, NONE)                 | SOME d => if (d=nd) then add(x, NONE)
59                             else ()                             else ()
60    *)
61           end) handle _ => ()           end) handle _ => ()
62    
63        fun cand x = (get x; true) handle _ => false        fun cand x = (get x; true) handle _ => false
64    
65        fun loop (e, d) =        fun lpfd d (FK_FUN {isrec=SOME _,...}, v, vts, e) = lple d e
66            | lpfd d (_, v, vts, e) = (enter(v, d); lple d e)
67    
68          and lple d e =
69          let fun psv (VAR x) = kill x          let fun psv (VAR x) = kill x
70                | psv _ = ()                | psv _ = ()
71    
72              and pse (SVAL v) = psv v              and pst (v, vks, e) = lple (DI.next d) e
73                | pse (FN(v, _, e)) = pse e  
74                | pse (APP(VAR x, v2)) = (mark d x; psv v2)              and pse (RET vs) = app psv vs
75                | pse (APP(v1, v2)) = (psv v1; psv v2)                | pse (LET(vs, e1, e2)) = (pse e1; pse e2)
76                | pse (FIX(vs, ts, es, be)) = (app pse es; pse be)                | pse (FIX(fdecs, e)) = (app (lpfd d) fdecs; pse e)
77                | pse (LET(v, FN (_,_,e1), e2)) = (enter(v, d); pse e1; pse e2)                | pse (APP(VAR x, vs)) = (mark d x; app psv vs)
78                | pse (LET(v, e1, e2)) = (pse e1; pse e2)                | pse (APP(v, vs)) = (psv v; app psv vs)
79                | pse (TFN(ks, e)) = loop(e, DI.next d)                | pse (TFN(tfdec, e)) = (pst tfdec; pse e)
80                | pse (TAPP(v, _)) = psv v                | pse (TAPP(v, _)) = psv v
81                | pse (VECTOR(vs,_)) = app psv vs                | pse (RECORD(_,vs,_,e)) = (app psv vs; pse e)
82                | pse (RECORD vs) = app psv vs                | pse (SELECT(u,_,_,e)) = (psv u; pse e)
83                | pse (SRECORD vs) = app psv vs                | pse (CON(_,_,u,_,e)) = (psv u; pse e)
84                | pse (SELECT(_,v)) = psv v                | pse (SWITCH(u, _, ces, oe)) =
85                | pse (CON(_,_,v)) = psv v                    (psv u; app (fn (_,x) => pse x) ces;
               | pse (DECON(_,_,v)) = psv v  
               | pse (SWITCH(v, _, ces, oe)) =  
                   (psv v; app (fn (_,x) => pse x) ces;  
86                     case oe of NONE => () | SOME x => pse x)                     case oe of NONE => () | SOME x => pse x)
               | pse (ETAG(v, _)) = psv v  
               | pse (HANDLE(e,v)) = (pse e; psv v)  
               | pse (PACK(_,_,_,v)) = psv v  
               | pse (WRAP(_,_,v)) = psv v  
               | pse (UNWRAP(_,_,v)) = psv v  
87                | pse (RAISE _) = ()                | pse (RAISE _) = ()
88                  | pse (HANDLE(e,v)) = (pse e; psv v)
89                  | pse (BRANCH(_, vs, e1, e2)) = (app psv vs; pse e1; pse e2)
90                  | pse (PRIMOP(_, vs, _, e)) = (app psv vs; pse e)
91    
92           in pse e           in pse e
93          end          end
94    
95     in loop (lexp, DI.top); cand     in lpfd DI.top fdec; (cand, fn () => Intmap.clear zz)
96    end    end (* pass1 *)
97    
98  (************************************************************************  (************************************************************************
99   *                      THE MAIN FUNCTION                               *   *                      THE MAIN FUNCTION                               *
100   ************************************************************************)   ************************************************************************)
101  fun lcontract lexp =  fun lcontract (fdec, init) =
102  let  let
103    
104  val isCand = pass1 lexp  (* In pass1, we calculate the list of functions that are the candidates
105     * for contraction. To be such a candidate, a function must be called
106     * only once, and furthermore, the call site must be at the same
107     * depth as the definition site. (ZHONG)
108     *
109     * Being at the same depth is not strictly necessary, we'll relax this
110     * constraint in the future.
111     *)
112    val (isCand, cleanUp) =
113     if init then (fn _ => false, fn () => ()) else pass1 fdec
114    
115  exception LContract  exception LContract
116  val m : (int ref * info) Intmap.intmap = Intmap.new(32, LContract)  val m : (int ref * info) Intmap.intmap = Intmap.new(32, LContract)
# Line 123  Line 121 
121    
122  fun chkIn (v, info) = enter(v, (ref 0, info))  fun chkIn (v, info) = enter(v, (ref 0, info))
123    
124  fun refer v =  (** check if a variable is dead *)
125    ((case get v  fun dead v = (case get v of (ref 0, _) => true
126       of (_, SimpVal sv) => SOME sv                            | _ => false) handle _ => false
127        | (x, _) => (x := (!x) + 1; NONE)) handle _ => NONE)  
128    (** check if all variables are dead *)
129    fun alldead [] = true
130      | alldead (v::r) = if dead v then alldead r else false
131    
132  fun selInfo v = (SOME(get v)) handle _ => NONE  (** renaming a value *)
133    fun rename (u as (VAR v)) =
134          ((case get v
135             of (_, SimpVal sv) => rename sv
136              | (x, _) => (x := (!x) + 1; u)) handle _ => u)
137      | rename u = u
138    
139  fun chkOut v =  (** selecting a field from a potentially known record *)
140    (let val x = get v  fun selInfo (VAR v, i)  =
141      in kill v; SOME x        ((case get v
142     end handle _ => NONE)           of (_, SimpVal u) => selInfo (u, i)
143              | (_, ListExp vs) =>
   
 fun mkInfo (_, RECORD vs) = ListExp vs  
   | mkInfo (_, SRECORD vs) = ListExp vs  
   | mkInfo (v, SELECT(i, VAR x)) =  
       let fun h z =  
             (case selInfo z  
               of SOME(_, ListExp vs) =>  
144                     let val nv = List.nth(vs, i)                     let val nv = List.nth(vs, i)
145                           handle _ => bug "unexpected List.Nth in SELECT"                             handle _ => bug "unexpected List.Nth in selInfo"
146                      in SimpVal nv                 in SOME nv
147                     end                     end
148                 | SOME(_, SimpVal (VAR w)) => h w            | _ => NONE) handle _ => NONE)
149                 | _ => SimpExp)    | selInfo _ = NONE
150          in h x  
151    (** applying a switch to a data constructor *)
152    fun swiInfo (VAR v, ces, oe) =
153          ((case get v
154             of (_, SimpVal u) => swiInfo(u, ces, oe)
155              | (_, ConExp (dc as (_,rep,_), ts, u)) =>
156                   let fun h ((DATAcon(dc as (_,nrep,_),ts,x),e)::r) =
157                             if rep=nrep then SOME(LET([x], RET [u], e)) else h r
158                         | h (_::r) = bug "unexpected case in swiInfo"
159                         | h [] = oe
160                    in h ces
161         end         end
162              | _ => NONE) handle _ => NONE)
163      | swiInfo _ = NONE
164    
165    | mkInfo (v, e as FN x) = if isCand v then FunExp x else SimpExp  (** contracting a function application *)
166    | mkInfo (_, e) = if isPure e then SimpExp else CompExp  fun appInfo (VAR v) =
167          ((case get v
168             of (ref 0, FunExp (d, vs, e)) => SOME (d, vs, e)
169              | _ => NONE) handle _ => NONE)
170      | appInfo _ = NONE
171    
172    fun transform [] = bug "unexpected case in transform"
173      | transform (cfg as ((d, od, k)::rcfg)) = let
174         fun h (f, t, (d, od, k)::r, sk) = h(f, f(t, od, d, k+sk), r, k+sk)
175           | h (f, t, [], _) = t
176         fun ltf t = h(LT.lt_adj_k, t, cfg, 0)
177         fun tcf t = h(LT.tc_adj_k, t, cfg, 0)
178    
179  fun lpacc (LVAR v) =       fun lpacc (DA.LVAR v) =
180        (case lpsv (VAR v)             (case lpsv (VAR v) of VAR w => DA.LVAR w
         of VAR w => LVAR w  
181           | _ => bug "unexpected in lpacc")           | _ => bug "unexpected in lpacc")
182    | lpacc _ = bug "unexpected path in lpacc"    | lpacc _ = bug "unexpected path in lpacc"
183    
184  and lpdc (s, EXN acc, t) = (s, EXN(lpacc acc), t)       and lpdc (s, DA.EXN acc, t) = (s, DA.EXN(lpacc acc), ltf t)
185    | lpdc x = x         | lpdc (s, rep, t) = (s, rep, ltf t)
186    
187  and lpcon (DATAcon dc) = DATAcon(lpdc dc)       and lpcon (DATAcon (dc, ts, v)) = DATAcon(lpdc dc, map tcf ts, v)
188    | lpcon c = c    | lpcon c = c
189    
190  and lpdt {default=v, table=ws} =       and lpdt (SOME {default=v, table=ws}) =
191    let fun h x = case (refer x)             let fun h x =
192                   of SOME(VAR nv) => nv                   case rename (VAR x) of VAR nv => nv
193                    | NONE => x                                        | _ => bug "unexpected acse in lpdt"
194     in {default=h v, table=map (fn (ts,w) => (ts,h w)) ws}              in (SOME {default=h v, table=map (fn (ts,w) => (ts,h w)) ws})
195    end             end
196           | lpdt NONE = NONE
197  and lpsv x =  
198    (case x       and lpsv x = (case x of VAR v => rename x | _ => x)
199      of VAR v => (case (refer v) of SOME nsv => lpsv nsv  
200                                   | NONE => (x : value))       and lpfd (fk, v, vts, e) =
201       | GENOP(dict, p, lt, ts) => GENOP(lpdt dict, p, lt, ts)         (fk, v, map (fn (v,t) => (v,ltf t)) vts, #1(loop e))
202       | _ => x)  
203         and lplet (hdr: lexp -> lexp, pure, v: lvar, info: info, e) =
204           let val _ = chkIn(v, info)
205               val (ne, b) = loop e
206            in if pure then (if dead v then (ne, b) else (hdr ne, b))
207               else (hdr ne, false)
208           end (* function lplet *)
209    
210  and loop le =  and loop le =
211    (case le    (case le
212      of SVAL v => SVAL(lpsv v)           of RET vs => (RET (map lpsv vs), true)
213       | FN(v, t, e) => FN(v, t, loop e)            | LET(vs, RET us, e) =>
214       | APP(v1 as VAR x, v2) =>                (ListPair.app chkIn (vs, map SimpVal us); loop e)
215           (case selInfo x            | LET(vs, LET(us, e1, e2), e3) =>
216             of SOME(ref c, FunExp(z,_,b)) =>                loop(LET(us, e1, LET(vs, e2, e3)))
217                 (if (c = 0) then loop(LET(z, SVAL v2, b))            | LET(vs, FIX(fdecs, e1), e2) =>
218                  else bug "unexpected FunExp in APP")                loop(FIX(fdecs, LET(vs, e1, e2)))
219  (* commented out because it won't have any effect for the time being.            | LET(vs, TFN(tfd, e1), e2) =>
220              | SOME(_, SimpVal (y as VAR _)) => loop(APP(y, v2))                loop(TFN(tfd, LET(vs, e1, e2)))
221  *)            | LET(vs, CON(dc, ts, u, v, e1), e2) =>
222              | _ => APP(lpsv v1, lpsv v2))                loop(CON(dc, ts, u, v, LET(vs, e1, e2)))
223       | APP(v1, v2) => APP(lpsv v1, lpsv v2)            | LET(vs, RECORD(rk, us, v, e1), e2) =>
224       | FIX(vs, ts, es, b) =>                loop(RECORD(rk, us, v, LET(vs, e1, e2)))
225           let fun g ((FN _)::r) = g r            | LET(vs, SELECT(u, i, v, e1), e2) =>
226                 | g (_::r) = false                loop(SELECT(u, i, v, LET(vs, e1, e2)))
227                 | g [] = true            | LET(vs, PRIMOP(p, us, v, e1), e2) =>
228               val _ = if g es then () else bug "unexpected cases in loop-FIX"                loop(PRIMOP(p, us, v, LET(vs, e1, e2)))
229               val _ = app (fn x => chkIn(x, SimpExp)) vs            | LET(vs, e1, e2 as (RET us)) =>
230               val nb = loop b                if isEqs(vs, us) then loop e1
231               val ws = map chkOut vs                else let val (ne1, b1) = loop e1
232                           val nus = map lpsv us
233               fun h ((SOME(ref 0, _))::r) = h r                      in if (isDiffs(vs, nus)) andalso b1 then (RET nus, true)
234                 | h (_::r) = false                         else (LET(vs, ne1, RET nus), b1)
235                 | h [] = true                     end
236            in if h ws then nb            | LET(vs, e1, e2) =>
237               else FIX(vs, ts, map loop es, nb)                let val _ = app (fn v => chkIn(v, StdExp)) vs
238           end                    val (ne1, b1) = loop e1
239       | LET(v, LET(u, e1, e2), e3) =>                    val (ne2, b2) = loop e2
240           loop(LET(u, e1, LET(v, e2, e3)))                 in if (alldead vs) andalso b1 then (ne2, b2)
241       | LET(v, FIX(vs, ts, es, b), e) =>                    else (case ne2
242           loop(FIX(vs, ts, es, LET(v, b, e)))                           of (RET us) =>
243       | LET(v, SVAL sv, e2) =>                                if isEqs(vs, us) then (ne1, b1)
244           (chkIn(v, SimpVal sv); loop e2)                                else (LET(vs, ne1, ne2), b1)
245       | LET(v, e1, e2 as SVAL (VAR x)) =>                            | _ => (LET(vs, ne1, ne2), b1 andalso b2))
246           if (v = x) then loop e1                end
247           else if isPure e1 then loop e2  
248                else LET(v, loop e1, loop e2)            | FIX(fdecs, e) =>
249       | LET(v, e1 as FN(v1, t1, b1), e2 as APP(VAR x, sv)) =>                let fun g (FK_FUN {isrec=SOME _, ...} :fkind, v, _, _) =
250           if isDiff(v, sv) then                           chkIn(v, StdExp)
251             (if (v = x) then loop(LET(v1, SVAL sv, b1)) else loop e2)                      | g ((_, v, vts, xe) : fundec) =
252           else LET(v, loop e1, loop e2)                           chkIn(v, if isCand v then FunExp(od, map #1 vts, xe)
253       | LET(v, e1, e2) =>                                    else StdExp)
254           let val _ = chkIn(v, mkInfo(v,e1))                    val _ = app g fdecs
255               val ne2 = loop e2                    val (ne, b) = loop e
256               val w = chkOut v                 in if alldead (map #2 fdecs) then (ne, b)
257            in case w                    else (FIX(map lpfd fdecs, ne), b)
258                of SOME(_, CompExp) => LET(v, loop e1, ne2)                end
259                 | SOME(ref 0, _) => ne2            | APP(u, us) =>
260                 | _ => (case (e1, ne2)                (case appInfo u
261                          of (FN(v1,t1,b1), APP(VAR x, sv)) =>                  of SOME(od', vs, e) =>
262                               if isDiff(v, sv) then                       let val ne = LET(vs, RET us, e)
263                                (if (v=x) then loop(LET(v1, SVAL sv,b1))                        in transform ((od, od', 0)::cfg) ne
264                                 else ne2)                       end
265                               else LET(v, loop e1, ne2)                   | _ => (APP(lpsv u, map lpsv us), false))
266                           | (_, SVAL(VAR x)) =>  
267                               if isPure e1 then (if v=x then loop e1            | TFN(tfdec as (v, tvks, xe), e) =>
268                                                  else ne2)                lplet ((fn z => TFN((v, tvks,
269                               else LET(v, loop e1, ne2)                                #1(transform ((DI.next d, DI.next od,
270                           | _ => LET(v, loop e1, ne2))                                              k+1)::rcfg) xe)), z)),
271           end                       true, v, StdExp, e)
272       | TFN(ks, e) => TFN(ks, loop e)            | TAPP(u, ts) => (TAPP(lpsv u, map tcf ts), true)
273       | TAPP(v, ts) => TAPP(lpsv v, ts)  
274       | VECTOR(vs, t) => VECTOR(map lpsv vs, t)            | CON(c, ts, u, v, e) =>   (* this could be made more finegrain *)
275       | RECORD vs => RECORD (map lpsv vs)                lplet ((fn z => CON(lpdc c, map tcf ts, lpsv u, v, z)),
276       | SRECORD vs => SRECORD (map lpsv vs)                       true, v, ConExp(c,ts,u), e)
      | SELECT(i, v as VAR x) =>  
          (case selInfo x  
            of SOME(_, ListExp vs) =>  
                 let val nv = List.nth(vs, i)  
                       handle _ => bug "unexpected List.Nth in SELECT"  
                  in SVAL(lpsv nv)  
                 end  
             | SOME(_, SimpVal (y as VAR _)) => loop(SELECT(i, y))  
             | _ => SELECT(i, lpsv v))  
      | SELECT(i, v) => SELECT(i, lpsv v)  
      | CON(c, ts, v) => CON(lpdc c, ts, lpsv v)  
      | DECON(c, ts, v) => DECON(lpdc c, ts, lpsv v)  
277       | SWITCH (v, cs, ces, oe) =>       | SWITCH (v, cs, ces, oe) =>
278           let val nv = lpsv v                (case swiInfo(v, ces, oe)
279               val nces = map (fn (c, e) => (lpcon c, loop e)) ces                  of SOME ne => loop ne
280               val noe = case oe of NONE => NONE | SOME e => SOME (loop e)                   | _ => let val nv = lpsv v
281            in SWITCH(nv, cs, nces, noe)                              fun h ((c, e), (es, b)) =
282           end                                let val nc = lpcon c
283       | ETAG(v, t) => ETAG(lpsv v, t)                                    val (ne, nb) = loop e
284       | RAISE(v, t) => RAISE(lpsv v, t)                                 in ((nc, ne)::es, nb andalso b)
285       | HANDLE(e, v) => HANDLE(loop e, lpsv v)                                end
286       | PACK(t, ts1, ts2, v) => PACK(t, ts1, ts2, lpsv v)                              val (nces, ncb) = foldr h ([], true) ces
287       | WRAP(t, b, v) => WRAP(t, b, lpsv v)                              val (noe, nb) =
288       | UNWRAP(t, b, v) => UNWRAP(t, b, lpsv v))                                case oe
289                                   of NONE => (NONE, ncb)
290                                    | SOME e => let val (ne, b) = loop e
291                                                 in (SOME ne, b andalso ncb)
292                                                end
293                             in (SWITCH(nv, cs, nces, noe), nb)
294                            end)
295    
296              | RECORD (rk, us, v, e) =>
297                  lplet ((fn z => RECORD(rk, map lpsv us, v, z)),
298                         true, v, ListExp us, e)
299              | SELECT(u, i, v, e) =>
300                  (case selInfo (u, i)
301                    of SOME nv => (chkIn(v, SimpVal nv); loop e)
302                     | NONE => lplet ((fn z => SELECT(lpsv u, i, v, z)),
303                                      true, v, StdExp, e))
304    
305              | RAISE(v, ts) => (RAISE(lpsv v, map ltf ts), false)
306              | HANDLE(e, v) =>
307                  let val (ne, b) = loop e
308                   in if b then (ne, true)
309                      else (HANDLE(ne, lpsv v), false)
310                  end
311    
312              | BRANCH(px as (d, p, lt, ts), vs, e1, e2) =>
313                  let val (ne1, b1) = loop e1
314                      val (ne2, b2) = loop e2
315                   in (BRANCH(case (d,ts) of (NONE, []) => px
316                                           | _ => (lpdt d, p, lt, map tcf ts),
317                              map lpsv vs, ne1, ne2), false)
318                  end
319              | PRIMOP(px as (dt, p, lt, ts), vs, v, e) =>
320                  lplet ((fn z => PRIMOP((case (dt, ts)
321                                           of (NONE, []) => px
322                                            | _ => (lpdt dt, p, lt, map tcf ts)),
323                                         map lpsv vs, v, z)),
324                         false (* isPure p *), v, StdExp, e))
325    
326         in loop
327        end (* function transform *)
328    
329    val d = DI.top
330    val (fk, f, vts, e) = fdec
331    in (fk, f, vts, #1 (transform [(d, d, 0)] e))
332       before (Intmap.clear m; cleanUp())
333    end (* function lcontract *)
334    
335  val nlexp = loop lexp  (** run the lambda contraction twice *)
336  in (Intmap.clear m; nlexp)  val lcontract = fn fdec => lcontract(lcontract(fdec, true), false)
 end  
337    
338  end (* toplevel local *)  end (* toplevel local *)
339  end (* structure LContract *)  end (* structure LContract *)

Legend:
Removed from v.24  
changed lines
  Added in v.45

root@smlnj-gforge.cs.uchicago.edu
ViewVC Help
Powered by ViewVC 1.0.0