Home My Page Projects Code Snippets Project Openings diderot
Summary Activity Tracker Tasks SCM

SCM Repository

[diderot] Diff of /trunk/src/compiler/translate/translate.sml
ViewVC logotype

Diff of /trunk/src/compiler/translate/translate.sml

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

revision 1339, Mon Jun 13 19:56:59 2011 UTC revision 2636, Tue May 27 16:18:36 2014 UTC
# Line 18  Line 18 
18    end = struct    end = struct
19    
20      structure S = Simple      structure S = Simple
21      structure Ty = Types      structure Ty = SimpleTypes
22      structure VMap = Var.Map      structure VMap = SimpleVar.Map
23      structure VSet = Var.Set      structure VSet = SimpleVar.Set
24      structure IL = HighIL      structure IL = HighIL
25        structure Op = HighOps
26      structure DstTy = HighILTypes      structure DstTy = HighILTypes
27      structure Census = HighILCensus      structure Census = HighILCensus
28    
29        val cvtTy = TranslateTy.tr
30    
31    (* maps from SimpleAST variables to the current corresponding SSA variable *)    (* maps from SimpleAST variables to the current corresponding SSA variable *)
32      type env = IL.var VMap.map      type env = IL.var VMap.map
33    
34    (* +DEBUG *)
35        fun prEnv (prefix, env) = let
36              val wid = ref 0
37              fun pr s = (print s; wid := !wid + size s)
38              fun nl () = if (!wid > 0) then (print "\n"; wid := 0) else ()
39              fun prElem (src, dst) = let
40                    val s = String.concat [
41                            " ", SimpleVar.uniqueNameOf src, "->", IL.Var.toString dst
42                          ]
43                    in
44                      pr s;
45                      if (!wid >= 100) then (nl(); pr " ") else ()
46                    end
47              in
48                pr prefix; pr " ENV: {"; nl(); pr " ";
49                VMap.appi prElem env;
50                nl(); pr "}"; nl()
51              end
52    (* -DEBUG *)
53    
54      fun lookup env x = (case VMap.find (env, x)      fun lookup env x = (case VMap.find (env, x)
55             of SOME x' => x'             of SOME x' => x'
56              | NONE => raise Fail(concat[              | NONE => raise Fail(concat[
57                    "no binding for ", Var.uniqueNameOf x, " in environment"                    "no binding for ", SimpleVar.uniqueNameOf x, " in environment"
58                  ])                  ])
59            (* end case *))            (* end case *))
60    
     fun cvtTy ty = (case TypeUtil.prune ty  
            of Ty.T_Bool => DstTy.BoolTy  
             | Ty.T_Int => DstTy.IntTy  
             | Ty.T_String => DstTy.StringTy  
             | Ty.T_Kernel _ => DstTy.KernelTy  
             | Ty.T_Tensor(Ty.Shape dd) => let  
                 fun cvtDim (Ty.DimConst 1) = NONE  
                   | cvtDim (Ty.DimConst d) = SOME d  
                 in  
                   DstTy.TensorTy(List.mapPartial cvtDim dd)  
                 end  
             | Ty.T_Image{dim=Ty.DimConst d, shape} => DstTy.ImageTy d  
             | Ty.T_Field fld => DstTy.FieldTy  
             | ty => raise Fail("cvtTy: unexpected " ^ TypeUtil.toString ty)  
           (* end case *))  
   
61    (* create a new instance of a variable *)    (* create a new instance of a variable *)
62      fun newVar x = IL.Var.new (Var.nameOf x, cvtTy(Var.monoTypeOf x))      fun newVar x = IL.Var.new (SimpleVar.nameOf x, cvtTy(SimpleVar.typeOf x))
63    
64    (* generate fresh SSA variables and add them to the environment *)    (* generate fresh SSA variables and add them to the environment *)
65      fun freshVars (env, xs) = let      fun freshVars (env, xs) = let
# Line 113  Line 120 
120                          val rhs = List.tabulate (arity, fn i => if (i = predIndex) then dstVar else dstVar')                          val rhs = List.tabulate (arity, fn i => if (i = predIndex) then dstVar else dstVar')
121                          in                          in
122  (*  (*
123  print(concat["recordAssign: ", Var.uniqueNameOf srcVar, " --> ", IL.Var.toString lhs,  print(concat["recordAssign: ", SimpleVar.uniqueNameOf srcVar, " --> ", IL.Var.toString lhs,
124  " @ ", IL.Node.toString nd, "\n"]);  " @ ", IL.Node.toString nd, "\n"]);
125  *)  *)
126                            phiMap := VMap.insert (m, srcVar, (lhs, rhs))                            phiMap := VMap.insert (m, srcVar, (lhs, rhs))
# Line 135  Line 142 
142     *)     *)
143      fun commitJoin (joinStk, JOIN{env, arity, nd, phiMap, predKill}) = (case !arity      fun commitJoin (joinStk, JOIN{env, arity, nd, phiMap, predKill}) = (case !arity
144             of 0 => (env, NONE)             of 0 => (env, NONE)
145              | 1 => (* there is only one path to the join, so we do not need phi nodes *)              | 1 => let
146                  (* there is only one path to the join, so we do not need phi nodes, but
147                   * we still need to propogate assignments to the next join on the stack.
148                   *)
149                    val IL.ND{kind=IL.JOIN{phis, ...}, ...} = nd
150                    val ix = let (* find pred of this join *)
151                          fun find i = if Array.sub(predKill, i) then find(i+1) else i
152                          in
153                            find 0
154                          end
155                    fun doVar (srcVar, (_, xs), env) = let
156                          val dstVar = List.nth(xs, ix)
157                          in
158    (*
159    print(concat["doVar (", SimpleVar.uniqueNameOf srcVar, ", ", IL.phiToString phi, ", _) @ ", IL.Node.toString nd, "\n"]);
160    *)
161                            recordAssign (joinStk, srcVar, dstVar);
162                            VMap.insert (env, srcVar, dstVar)
163                          end
164                    val env = VMap.foldli doVar env (!phiMap)
165                    in
166                  (env, SOME nd)                  (env, SOME nd)
167                    end
168              | n => if (n = Array.length predKill)              | n => if (n = Array.length predKill)
169                  then let                  then let
170                    val IL.ND{kind=IL.JOIN{phis, ...}, ...} = nd                    val IL.ND{kind=IL.JOIN{phis, ...}, ...} = nd
171                    fun doVar (srcVar, phi as (dstVar, _), (env, phis)) = (                    fun doVar (srcVar, phi as (dstVar, _), (env, phis)) = (
172  (*  (*
173  print(concat["doVar (", Var.uniqueNameOf srcVar, ", ", IL.phiToString phi, ", _) @ ", IL.Node.toString nd, "\n"]);  print(concat["doVar (", SimpleVar.uniqueNameOf srcVar, ", ", IL.phiToString phi, ", _) @ ", IL.Node.toString nd, "\n"]);
174  *)  *)
175                          recordAssign (joinStk, srcVar, dstVar);                          recordAssign (joinStk, srcVar, dstVar);
176                          (VMap.insert (env, srcVar, dstVar), phi::phis))                          (VMap.insert (env, srcVar, dstVar), phi::phis))
# Line 156  Line 184 
184    
185    (* expression translation *)    (* expression translation *)
186      fun cvtExp (env : env, lhs, exp) = (case exp      fun cvtExp (env : env, lhs, exp) = (case exp
187             of S.E_Var x => [(lhs, IL.VAR(lookup env x))]             of S.E_Var x => [IL.ASSGN(lhs, IL.VAR(lookup env x))]
188              | S.E_Lit lit => [(lhs, IL.LIT lit)]              | S.E_Lit lit => [IL.ASSGN(lhs, IL.LIT lit)]
189              | S.E_Tuple xs => raise Fail "E_Tuple not implemeted"              | S.E_Tuple xs => raise Fail "E_Tuple not implemeted"
190              | S.E_Apply(f, tyArgs, args, ty) => let              | S.E_Apply _ => raise Fail "unexpected E_Apply"
191                | S.E_Prim(f, tyArgs, args, ty) => let
192                  val args' = List.map (lookup env) args                  val args' = List.map (lookup env) args
193                  in                  in
194                    TranslateBasis.translate (lhs, f, tyArgs, args')                    TranslateBasis.translate (lhs, f, tyArgs, args')
195                  end                  end
196              | S.E_Cons args => [(lhs, IL.CONS(IL.Var.ty lhs, List.map (lookup env) args))]              | S.E_Cons args => [IL.ASSGN(lhs, IL.CONS(IL.Var.ty lhs, List.map (lookup env) args))]
197                | S.E_Seq args => [IL.ASSGN(lhs, IL.CONS(IL.Var.ty lhs, List.map (lookup env) args))]
198              | S.E_Slice(x, indices, ty) => let              | S.E_Slice(x, indices, ty) => let
199                  val x = lookup env x                  val x = lookup env x
200                  val mask = List.map isSome indices                  val mask = List.map isSome indices
# Line 173  Line 203 
203                  val indices = List.mapPartial cvt indices                  val indices = List.mapPartial cvt indices
204                  in                  in
205                    if List.all (fn b => b) mask                    if List.all (fn b => b) mask
206                      then [(lhs, IL.OP(HighOps.Subscript(IL.Var.ty x), x::indices))]                      then [IL.ASSGN(lhs, IL.OP(Op.TensorSub(IL.Var.ty x), x::indices))]
207                      else [(lhs, IL.OP(HighOps.Slice(IL.Var.ty lhs, mask), x::indices))]                      else [IL.ASSGN(lhs, IL.OP(Op.Slice(IL.Var.ty x, mask), x::indices))]
208                  end                  end
209              | S.E_Input(_, name, desc, NONE) =>              | S.E_Coerce{srcTy, dstTy, x} => (case (srcTy, dstTy)
210                  [(lhs, IL.OP(HighOps.Input(IL.Var.ty lhs, name, desc), []))]                   of (Ty.T_Int, Ty.T_Tensor _) =>
211              | S.E_Input(_, name, desc, SOME dflt) =>                        [IL.ASSGN(lhs, IL.OP(Op.IntToReal, [lookup env x]))]
212                  [(lhs, IL.OP(HighOps.InputWithDefault(IL.Var.ty lhs, name, desc), [lookup env dflt]))]                    | (Ty.T_Field _, Ty.T_Field _) =>
213              | S.E_LoadImage(info, name) => [(lhs, IL.OP(HighOps.LoadImage info, [lookup env name]))]                      (* change in continuity is a no-op *)
214                          [IL.ASSGN(lhs, IL.VAR(lookup env x))]
215                      | _ => raise Fail(concat[
216                            "unsupported type coercion: ", Ty.toString srcTy,
217                            " ==> ", Ty.toString dstTy
218                          ])
219            (* end case *))            (* end case *))
220                | S.E_LoadImage(ty, nrrd, info) => [IL.ASSGN(lhs, IL.OP(Op.LoadImage(cvtTy ty, nrrd, info), []))]
221              (* end case *))
222    
223      (* add nodes to save the strand state, followed by an exit node *)
224        fun saveStrandState (env, (srcState, dstState), exit) = let
225              val stateOut = List.map (lookup env) srcState
226              fun save (x, x', cfg) = IL.CFG.appendNode (cfg, IL.Node.mkSAVE(x, x'))
227              in
228                IL.CFG.appendNode (
229                  ListPair.foldlEq save IL.CFG.empty (dstState, stateOut),
230                  exit)
231              end
232    (*DEBUG*)handle ex => raise ex
233    
234      fun cvtBlock (state, env : env, joinStk, S.Block stms) = let      fun cvtBlock (state, env : env, joinStk, S.Block stms) = let
235            fun cvt (env : env, cfg, []) = (cfg, env)            fun cvt (env : env, cfg, []) = (cfg, env)
# Line 257  Line 305 
305                    | S.S_Die => (                    | S.S_Die => (
306                        killPath joinStk;                        killPath joinStk;
307                        (IL.CFG.appendNode (cfg, IL.Node.mkDIE ()), env))                        (IL.CFG.appendNode (cfg, IL.Node.mkDIE ()), env))
308                    | S.S_Stabilize => let                    | S.S_Stabilize => (
                       val stateOut = List.map (lookup env) state  
                       in  
309                          killPath joinStk;                          killPath joinStk;
310                          (IL.CFG.appendNode (cfg, IL.Node.mkSTABILIZE stateOut), env)                        (IL.CFG.concat (cfg, saveStrandState (env, state, IL.Node.mkSTABILIZE())), env))
311                      | S.S_Return _ => raise Fail "unexpected return"
312                      | S.S_Print args => let
313                          val args = List.map (lookup env) args
314                          val nd = IL.Node.mkMASSIGN([], Op.Print(List.map IL.Var.ty args), args)
315                          in
316                            cvt (env, IL.CFG.appendNode (cfg, nd), stms)
317                        end                        end
318                  (* end case *))                  (* end case *))
319            in            in
# Line 270  Line 322 
322  (*DEBUG*)handle ex => raise ex  (*DEBUG*)handle ex => raise ex
323    
324      fun cvtTopLevelBlock (env, blk, mkExit) = let      fun cvtTopLevelBlock (env, blk, mkExit) = let
325            val (cfg, env) = cvtBlock ([], env, [], blk)            val (cfg, env) = cvtBlock (([], []), env, [], blk)
326            val entry = IL.Node.mkENTRY ()            val cfg = IL.CFG.prependNode (IL.Node.mkENTRY(), cfg)
327            val exit = mkExit env            val cfg = IL.CFG.concat (cfg, mkExit env)
328            in            in
329              if IL.CFG.isEmpty cfg              (cfg, env)
               then IL.Node.addEdge (entry, exit)  
               else (  
                 IL.Node.addEdge (entry, IL.CFG.entry cfg);  
               (* NOTE: this addEdge could fail if all control paths end in DIE or STABILIZE,  
                * so we wrap it in a handler  
                *)  
                 IL.Node.addEdge (IL.CFG.exit cfg, exit) handle _ => ());  
             (IL.CFG{entry = entry, exit = exit}, env)  
330            end            end
331  (*DEBUG*)handle ex => raise ex  (*DEBUG*)handle ex => raise ex
332    
333  (* FIXME: the following function could be refactored with cvtTopLevelBlock to share code *)  (* FIXME: the following function could be refactored with cvtTopLevelBlock to share code *)
334      fun cvtFragmentBlock (env0, blk) = let      fun cvtFragmentBlock (env0, blk) = let
335            val (cfg, env) = cvtBlock ([], env0, [], blk)            val (cfg, env) = cvtBlock (([], []), env0, [], blk)
336            val entry = IL.Node.mkENTRY ()            val entry = IL.Node.mkENTRY ()
337          (* the live variables out are those that were not live coming in *)          (* the live variables out are those that were not live coming in *)
338            val liveOut = VMap.foldli            val liveOut = VMap.foldli
# Line 305  Line 349 
349            end            end
350  (*DEBUG*)handle ex => raise ex  (*DEBUG*)handle ex => raise ex
351    
352      fun cvtMethod (env, name, state, blk) = let      fun cvtMethod (env, name, state, svars, blk) = let
353          (* allocate fresh variables for the state variables *)          (* load the state into fresh variables *)
354              val (env, loadCFG) = let
355                  (* allocate shadow variables for the state variables *)
356            val (env, stateIn) = freshVars (env, state)            val (env, stateIn) = freshVars (env, state)
357                    fun load (x, x') = IL.ASSGN(x, IL.STATE x')
358                    in
359                      (env, IL.CFG.mkBlock (ListPair.map load (stateIn, svars)))
360                    end
361          (* convert the body of the method *)          (* convert the body of the method *)
362            val (cfg, env) = cvtBlock (state, env, [], blk)            val (cfg, env) = cvtBlock ((state, svars), env, [], blk)
363          (* add the entry/exit nodes *)          (* add the entry/exit nodes *)
           val stateOut = List.map (lookup env) state  
364            val entry = IL.Node.mkENTRY ()            val entry = IL.Node.mkENTRY ()
365            val exit = IL.Node.mkACTIVE stateOut            val loadCFG = IL.CFG.prependNode (entry, loadCFG)
366              val exit = (case name
367                     of StrandUtil.Update => IL.Node.mkACTIVE ()
368                      | StrandUtil.Stabilize => IL.Node.mkRETURN []
369                    (* end case *))
370              val body = IL.CFG.concat (loadCFG, cfg)
371    (*DEBUG**val _ = prEnv (StrandUtil.nameToString name, env);*)
372    (* FIXME: the following code doesn't work properly *)
373              val body = if IL.Node.hasSucc(IL.CFG.exit body)
374                    then IL.CFG.concat (body, saveStrandState (env, (state, svars), exit))
375                    else IL.CFG{entry = IL.CFG.entry body, exit = exit}
376            in            in
             if IL.CFG.isEmpty cfg  
               then IL.Node.addEdge (entry, exit)  
               else (  
                 IL.Node.addEdge (entry, IL.CFG.entry cfg);  
               (* NOTE: this addEdge could fail if all control paths end in DIE or STABILIZE,  
                * so we wrap it in a handler  
                *)  
                 IL.Node.addEdge (IL.CFG.exit cfg, exit) handle _ => ());  
377              IL.Method{              IL.Method{
378                  name = name,                  name = name,
379                  stateIn = stateIn,                  body = body
                 body = IL.CFG{entry = entry, exit = exit}  
380                }                }
381            end            end
382  (*DEBUG*)handle ex => (print(concat["error in cvtMethod(", Atom.toString name, ", ...)\n"]); raise ex)  (*DEBUG*)handle ex => (print(concat["error in cvtMethod(", StrandUtil.nameToString name, ", ...)\n"]); raise ex)
383    
384    (* convert the initially code *)    (* convert the initially code *)
385      fun cvtInitially (env, S.Initially{isArray, rangeInit, create, iters}) = let      fun cvtInitially (env, S.Initially{isArray, rangeInit, create, iters}) = let
# Line 353  Line 403 
403                }                }
404            end            end
405    
406      fun translate (S.Program{globals, globalInit, init, strands}) = let    (* check strands for properties *)
407            val (globalInit, env) =      fun checkProps strands = let
408                  cvtTopLevelBlock (            val hasDie = ref false
409                    VMap.empty, globalInit,            val hasNew = ref false
410                    fn env => IL.Node.mkRETURN(VMap.listItems env))            fun chkStm e = (case e
411          (* construct a reduced environment that just defines the globals. *)                   of S.S_IfThenElse(_, b1, b2) => (chkBlk b1; chkBlk b2)
412                      | S.S_New _ => (hasNew := true)
413                      | S.S_Die => (hasDie := true)
414                      | _ => ()
415                  (* end case *))
416              and chkBlk (S.Block body) = List.app chkStm body
417              fun chkStrand (S.Strand{stateInit, methods, ...}) = let
418                    fun chkMeth (S.Method(_, body)) = chkBlk body
419                    in
420                      chkBlk stateInit;
421                      List.app chkMeth methods
422                    end
423              fun condCons (x, v, l) = if !x then v::l else l
424              in
425                List.app chkStrand strands;
426                condCons (hasDie, StrandUtil.StrandsMayDie,
427                condCons (hasNew, StrandUtil.NewStrands, []))
428              end
429    
430        fun cvtInputs inputs = let
431              fun cvt ((x, inp), (env, stms)) = let
432                    val x' = newVar x
433                    val stm = IL.ASSGN(x', IL.OP(Op.Input(Inputs.map cvtTy inp), []))
434                    in
435                      (VMap.insert(env, x, x'), stm::stms)
436                    end
437              val (env, stms) = List.foldr cvt (VMap.empty, []) inputs
438              in
439                (IL.CFG.mkBlock stms, env)
440              end
441    
442      (* gather the top-level definitions in a block.  This is a hack that is used to make all
443       * of the globally defined variables visible to the rest of the program (including intermediate
444       * results) so that later transforms (e.g., field normalization) will work.  Eventually the
445       * variable analysis phase ought to clean things up.
446       *)
447        fun definedVars (IL.CFG{entry, ...}) = let
448              fun gather (nd, vars) = (case IL.Node.kind nd
449                     of IL.NULL => vars
450                      | IL.ENTRY{succ, ...} => gather(!succ, vars)
451                      | IL.COND{trueBranch, ...} => let
452                          val (phis, succ) = findJoin (!trueBranch)
453                          val vars = List.foldl (fn ((x, _), vars) => x::vars) vars (!phis)
454                          in
455                            gather (succ, vars)
456                          end
457                      | IL.COM{succ, ...} => gather (!succ, vars)
458                      | IL.ASSIGN{stm=(x, _), succ, ...} => gather(!succ, x::vars)
459                      | IL.MASSIGN{stm=(xs, _, _), succ, ...} => gather(!succ, xs@vars)
460                      | _ => raise Fail("gather: unexpected " ^ IL.Node.toString nd)
461                    (* end case *))
462              and findJoin nd = (case IL.Node.kind nd
463                     of IL.JOIN{phis, succ, ...} => (phis, !succ)
464                      | IL.COND{trueBranch, ...} => findJoin (#2 (findJoin (!trueBranch)))
465                      | IL.COM{succ, ...} => findJoin (!succ)
466                      | IL.ASSIGN{succ, ...} => findJoin (!succ)
467                      | IL.MASSIGN{succ, ...} => findJoin (!succ)
468                      | _ => raise Fail("findJoin: unexpected " ^ IL.Node.toString nd)
469                    (* end case *))
470              in
471                List.rev (gather (entry, []))
472              end
473    
474        fun translate (S.Program{props, inputs, globals, globalInit, init, strands, ...}) = let
475              val (globalInit, env) = let
476                    val (inputBlk, inputEnv) = cvtInputs inputs
477                    val (globBlk, env) = cvtBlock (([], []), inputEnv, [], globalInit)
478                    val cfg = IL.CFG.prependNode (IL.Node.mkENTRY(), inputBlk)
479                    val cfg = IL.CFG.concat(cfg, globBlk)
480                    val exit = IL.Node.mkRETURN(VMap.listItems inputEnv @ definedVars globBlk)
481                    val cfg = IL.CFG.concat (cfg, IL.CFG{entry = exit, exit = exit})
482                    in
483                      (cfg, env)
484                    end
485            (* construct a reduced environment that just defines the globals (including inputs). *)
486            val env = let            val env = let
487                  val lookup = lookup env                  val lookup = lookup env
488                  fun cvtVar (x, env) = VMap.insert(env, x, lookup x)                  fun cvtVar (x, env) = VMap.insert(env, x, lookup x)
489                  val env = List.foldl cvtVar VMap.empty globals                  val env = List.foldl (fn ((x, _), env) => cvtVar(x, env)) VMap.empty inputs
490                    val env = List.foldl cvtVar env globals
491                  in                  in
492                    env                    env
493                  end                  end
# Line 379  Line 504 
504                        in                        in
505                          (env, List.rev params)                          (env, List.rev params)
506                        end                        end
507                  (* create the state variables *)
508                    val svars = let
509                          fun newSVar x = IL.StateVar.new (
510                                SimpleVar.kindOf x = S.StrandOutputVar,
511                                SimpleVar.nameOf x, cvtTy(SimpleVar.typeOf x))
512                          in
513                            List.map newSVar state
514                          end
515                (* convert the state initialization code *)                (* convert the state initialization code *)
516                  val (stateInit, env) = let                  val (stateInit, env) = let
517                        fun mkExit env = IL.Node.mkSINIT(List.map (lookup env) state)                        fun mkExit env = saveStrandState (env, (state, svars), IL.Node.mkSINIT())
518                        in                        in
519                          cvtTopLevelBlock (env, stateInit, mkExit)                          cvtTopLevelBlock (env, stateInit, mkExit)
520                        end                        end
521                (* the state-variable list is constructed by generating fresh variables for the                  fun cvtMeth (S.Method(name, blk)) = cvtMethod (env, name, state, svars, blk)
                * state variables and pairing them with a boolean that is true if the variable  
                * is an output variable.  Note that these IL variables are not defined or used.  
                *)  
                 val state' = let  
                       fun cvtStateVar x = (Var.kindOf x = S.StrandOutputVar, newVar x)  
                       in  
                         List.map cvtStateVar state  
                       end  
                 fun cvtMeth (S.Method(name, blk)) = cvtMethod (env, name, state, blk)  
522                  in                  in
523                    IL.Strand{                    IL.Strand{
524                        name = name,                        name = name,
525                        params = params,                        params = params,
526                        state = state',                        state = svars,
527                        stateInit = stateInit,                        stateInit = stateInit,
528                        methods = List.map cvtMeth methods                        methods = List.map cvtMeth methods
529                      }                      }
530                  end                  end
531            val prog = IL.Program{            val prog = IL.Program{
532    (* FIXME: we should just use the properties from the Simple program *)
533                      props = checkProps strands,
534                    globalInit = globalInit,                    globalInit = globalInit,
535                    initially = init,                    initially = init,
536                    strands = List.map cvtStrand strands                    strands = List.map cvtStrand strands

Legend:
Removed from v.1339  
changed lines
  Added in v.2636

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