855 lines · 30.8 KB · Standard ML
RawDownload
(*  back.sml : translation of lambda terms to lists of instructions. *) (* 1996.07.27 -- e *) open List Fnlib Mixture Const Lambda Prim Instruct; (* "isReturn" determines if we're in tail call position. *) fun isReturn (Kreturn _ :: _ )             = true  | isReturn (Klabel _ :: Kreturn _ :: _ ) = true  | isReturn _                             = false; (* Label generation *) val labelCounter = ref 0; fun resetLabel() =  labelCounter := 0; fun new_label() =  (incr labelCounter; !labelCounter); (* the label ref in an Lshared node is used as follows:   NoLabel -> (~1) initial value   n  < 0  -> seen by the nth pass of unref   n >= 0  -> a real label, code has been emitted Whenever a Lshared node is processed by a rewriter, its label ref is set to !labelNotCtr. The counter is  bumped by each rewrite in UNdeBruijn's lftexp.*) val labelNotCtr = ref Nolabel; (* for Lshared *) fun resetLabelNot() =  labelNotCtr := Nolabel - 1; fun newLabelNot() =  (decr labelNotCtr; !labelNotCtr); (* Add a label to a list of instructions. *) fun labelCode C =  case C of    Kbranch lbl :: _ =>      (lbl, C)  | Klabel lbl :: _ =>      (lbl, C)  | _ =>      let val lbl = new_label()      in (lbl, Klabel lbl :: C) end; (* Generate a branch to the given list of instructions. *) fun makeBranch C =  case C of    (return as Kreturn _) :: _ => (return, C)  | (branch as Kbranch _) :: _ => (branch, C)  | Kraise :: _  => (Kraise, C)  | Klabel _ :: (return as Kreturn _) :: _ => (return, C)  | Klabel lbl :: _ => (Kbranch lbl, C)  | _ =>      let val lbl = new_label()      in (Kbranch lbl, Klabel lbl :: C) end; (* Discard all instructions up to the next label. *) fun discardDeadCode C =  case C of    [] => []  | Klabel _ :: _      => C  | Krestart :: _      => C  | Kset_global _ :: _ => C  | _ :: rest => discardDeadCode rest; (* compile time model of runtime environment     mapping Lvar id to stack or heapenv offsets   fv is freevars, a list; position of negative id is the runtime env index   va is vararray, an array mapping positive ids to stack index*) (* The nullEnv is used for compiling the initialization code of a   unit.  A size of 4000 seems generous; the required number rarely   exceeds 200.  The compiler reports Internal error: bindEnv if this   table is too small. *) val nullEnv = ([],Array.array(4000,(~1))) : int list * int Array.array; fun makeEnv fv maxstk = (fv, Array.array(maxstk,(~1))); fun findEnv (fv,va) sz n =  if n < 0  then let fun f i [] = fatalError ("findEnv fv n: " ^ (makestring n))             | f i (x::r) = if x = n then Kenvacc i else f (i + 1) r       in f 0 fv end  else let val i = Array.sub (va,n)       in if i < 0 then fatalError "findEnv va1" else Kaccess (sz - i) end       handle Subscript => fatalError ("findEnv va2 n: " ^ (makestring n));fun findStk (fv,va) sz n =  if n < 0  then fatalError ("findStk n: " ^ (makestring n))  else let val i = Array.sub (va,n)       in if i < 0 then fatalError "findStk va1" else Kassign (sz - i) end       handle Subscript => fatalError ("findStk va2 n: " ^ (makestring n)); (* e -- note: this destroys the env *) fun bindEnv (fv,va) n z =  let val n' = n + 1  in    Array.update (va,n,z); (* not n' !? *)    n'  end  handle Subscript => fatalError "bindEnv"; (* *) fun addPop n C =  if n = 0  then C  else    case C of      Kpop m :: C    => addPop (n + m) C    | Kreturn m :: C => Kreturn (n + m) :: C    | Kraise :: _    => C    | _              => Kpop n :: C; (* Generate a jump through table, unless unnecessary. *) exception JumpOut; fun add_SwitchTable switchtable C =  let open Array infix 9 sub in    (for (fn i => if (switchtable sub i) <> (switchtable sub 0) then                    raise JumpOut                  else ())         1 (length switchtable - 1);     case C of         Klabel lbl :: C1 =>           if lbl = (switchtable sub 0) then C           else             Kbranch (switchtable sub 0) :: discardDeadCode C       | _ =>          Kbranch (switchtable sub 0) :: discardDeadCode C)    handle JumpOut =>      Kswitch switchtable :: C  end; (* Compiling N-way integer branches *) (* Input: a list of (key, action) pairs, where keys are integers. *)(* Output: a decision tree with the format below *) datatype DecisionTree =    DTfail  | DTinterval of DecisionTree * Decision * DecisionTree withtype Decision ={  low: int,  act: Lambda Array.array,  high: int}; fun compileNBranch int_of_key clauses =  let open Array infix 9 sub      val clauses_i =        map (fn (key, act) => (int_of_key key : int, act)) clauses      val clauses_s =        Sort.sort (fn (key1, act1) => fn (key2, act2) => key1 <= key2)                  clauses_i      val keyv = Array.fromList (map fst clauses_s)      val actv = Array.fromList (map snd clauses_s)      val n    = length keyv      fun extractAct start stop =        let val a =              array((keyv sub stop) - (keyv sub start) + 1, Lstaticfail)        in          for (fn i =>                 update(a, (keyv sub i) - (keyv sub start), actv sub i))              start stop;          a        end      (* Now we partition the set of keys keyv into maximal         dense enough segments. A segment is dense enough         if its span (max point - min point) is less         than four times its size (number of points). *)      fun partition start =        if start >= n then [] else        let val stop = ref (n-1) in          while (keyv sub !stop) - (keyv sub start) >= 255 orelse                (keyv sub !stop) - (keyv sub start) > 4 * (!stop - start)          do decr stop;          (* We've found a segment that is dense enough.             In the worst case, !stop = start and the segment is             a single point *)          (* Now build the vector of actions *)          { low = keyv sub start,            act = extractAct start (!stop),            high = keyv sub !stop } :: partition (!stop + 1)        end      val part = Array.fromList (partition 0)      (* We build a balanced binary tree *)      fun make_tree start stop =        if start > stop then          DTfail        else          let val middle = (start + stop) div 2 in            DTinterval(make_tree start (middle-1),                       part sub middle,                       make_tree (middle+1) stop)          end  in make_tree 0 (length part - 1) end; (* Inversion of a boolean test ( < becomes >= and so on) *) val invertPrimTest = fn    PTeq => PTnoteq  | PTnoteq => PTeq  | PTnoteqimm x => fatalError "invertPrimTest"  | PTlt => PTge  | PTle => PTgt  | PTgt => PTle  | PTge => PTlt; val invertBoolTest = fn    Peq_test => Pnoteq_test  | Pnoteq_test => Peq_test  | Pint_test t => Pint_test(invertPrimTest t)  | Pfloat_test t => Pfloat_test(invertPrimTest t)  | Pstring_test t => Pstring_test(invertPrimTest t)  | Pword_test t => Pword_test(invertPrimTest t)  | Pnoteqtag_test t => fatalError "invertBoolTest"; (* Production of an immediate test *) val testForAtom = fn    INTscon x => Pint_test(PTnoteqimm x)  | WORDscon x => Pword_test(PTnoteqimm x)  | CHARscon x => Pint_test(PTnoteqimm (Char.ord x))  | REALscon x => Pfloat_test(PTnoteqimm x)  | STRINGscon x => Pstring_test(PTnoteqimm x); (* To keep track of function bodies that remain to be compiled. *) (* ... a stack of (lbl, nargs, free var ids list, max id, body) *) val stillToCompile  = (Stack.new () : (int * int * int list * int * Lambda) Stack.t); (* translation of lambda-deBruijn to lambda-merged-stkdepth   this requires giving all the Lvars unique ids within the enclosing function    [well, kinda. unique within their scope, anyway]   to do this requires maintaining an rstack depth model     then the translation is simply: deBruijn -> depth - deBruijn - 1    all functions are lifted to top level simultaneously    as a result all free variables have negative ids     a closure map is constructed for compileRest   an Lfn is replaced with the closure constuction code*) fun UNdeBruijn exp =  let    exception Refs'R'Us    val fv = ref []    val md = ref 0     fun ins x [] = [x]      | ins x (ls as (y::r)) =          if x > y then x :: ls          else if x = y then ls          else let val z = ins x r in if z = r then ls else y :: z end     fun extClo id =      if id >= 0 then ()      else fv := (ins id (!fv))     fun unref i exp = (* turn refs from heap into stack cells *)      case exp of        Lvar n => (* oops, maybe it's a real reference *)          if n = i then raise Refs'R'Us else exp      | Lassign (n,exp') => (* we'd only see n = i in an Lshared node? *)          Lassign(n, unref i exp')      | Lconst cst =>          exp      | Lapply(body, args) =>          Lapply(unref i body, List.map (unref i) args)      | Lfn body =>         fatalError "UNdeBruijn/unref Lfn!?"      | Llet(args, body) =>          Llet(List.map (unref i) args, unref i body)      | Lletrec(args, body) =>          Lletrec(List.map (unref i) args, unref i body)      | Lprim(Pfield 0, [opt as (Lvar v)]) =>          if v = i          then opt          else exp      | Lprim(Psetfield 0, [Lvar v, e]) =>          if v = i          then Lassign (i, unref i e)            (* new *)          else Lprim(Psetfield 0, [Lvar v, unref i e])      | Lprim(p, explist) =>          Lprim(p, List.map (unref i) explist)      | Lstatichandle(body, handler) =>          Lstatichandle(unref i body, unref i handler)      | Lstaticfail =>          Lstaticfail      | Lhandle(body, handler) =>          Lhandle(unref i body, unref i handler)      | Lif(cond, ifso, ifnot) =>          Lif(unref i cond, unref i ifso, unref i ifnot)      | Lseq(exp1, exp2) =>          Lseq(unref i exp1, unref i exp2)      | Lwhile(cond, body) =>          Lwhile(unref i cond, unref i body)      | Landalso(exp1, exp2) =>          Landalso(unref i exp1, unref i exp2)      | Lorelse(exp1, exp2) =>          Lorelse(unref i exp1, unref i exp2)      | Lcase(arg, clauses) =>          Lcase(unref i arg,                List.map (fn (tag,act) => (tag, unref i act)) clauses)      | Lswitch(size, arg, clauses) =>          Lswitch(size, unref i arg,                 List.map (fn (tag,act) => (tag, unref i act)) clauses)      | Lunspec =>          Lunspec      | Lshared(exp_ref, lbl_ref) =>          if !lbl_ref <> !labelNotCtr          then ( lbl_ref := !labelNotCtr;                  exp_ref := unref i (!exp_ref);                 exp )          else exp     fun lftexp depth exp =      (if depth > !md then md := depth else ();      case exp of        Lvar n =>          let val id = (depth - n - 1)          in extClo id; Lvar id end      | Lassign (n,exp') =>          let val id = (depth - n - 1)          in extClo id; Lassign (id,lftexp depth exp') end      | Lconst cst => exp      | Lapply(body, args) => Lapply(lftexp depth body, List.map (lftexp depth) args)      | Lfn body =>         let val (qfv, exp') = UNdeBruijn exp (* recurse *)         in if qfv > 0 then lftexp depth exp' else exp' end      (* Optimize special case arising from #lab arg *)      | Llet([arg], Lprim(p, [Lvar 0])) =>          Lprim(p, [lftexp depth arg])      | Llet(args, body) =>          let fun opt_refs body' i [] acc =                    Llet(acc, body')                | opt_refs body' i (* do we care what the tag is? *)                          ((exp as Lprim(Pmakeblock(CONtag(refTag,1)),[e]))                           ::rest) acc =                   (let val lnot = newLabelNot()                        (* val () = BasicIO.print ("Optref: " ^ (makestring lnot)) *)                        val acc' = List.map (unref i) acc                        val body'' = unref i body'                    in (* BasicIO.print " $\n"; *)                       opt_refs body'' (i - 1) rest (e::acc')                    end                    handle Refs'R'Us =>                      ( (* BasicIO.print ("\n"); *)                       opt_refs body' (i - 1) rest (exp::acc) ) )                | opt_refs body' i (exp::rest) acc =                    opt_refs body' (i - 1) rest (exp::acc)              fun lift_args ea [] acc =                    opt_refs (lftexp ea body) (ea - 1) acc []                | lift_args ea (exp::rest) acc =                    lift_args (ea + 1) rest ((lftexp ea exp) :: acc)          in lift_args depth args [] end      | Lletrec(args, body) =>          let val ea = depth + (List.length args)          in Lletrec(List.map (lftexp ea) args, lftexp ea body) end      | Lprim(p, explist) => Lprim(p, List.map (lftexp depth) explist)      | Lstatichandle(body, handler) =>          Lstatichandle(lftexp depth body, lftexp depth handler)      | Lstaticfail => Lstaticfail      | Lhandle(body, handler) =>          Lhandle(lftexp depth body, lftexp (depth + 1) handler)      | Lif(cond, ifso, ifnot) => Lif(lftexp depth cond, lftexp depth ifso, lftexp depth ifnot)      | Lseq(exp1, exp2) => Lseq(lftexp depth exp1, lftexp depth exp2)      | Lwhile(cond, body) => Lwhile(lftexp depth cond, lftexp depth body)      | Landalso(exp1, exp2) => Landalso(lftexp depth exp1, lftexp depth exp2)      | Lorelse(exp1, exp2) => Lorelse(lftexp depth exp1, lftexp depth exp2)      | Lcase(arg, clauses) =>          Lcase(lftexp depth arg, List.map (fn (tag,act) => (tag, lftexp depth act)) clauses)      | Lswitch(size, arg, clauses) =>          Lswitch(size, lftexp depth arg, List.map (fn (tag,act) => (tag, lftexp depth act)) clauses)      | Lunspec => Lunspec      | Lshared(exp_ref, lbl_ref) =>          if !lbl_ref = Nolabel          then ( lbl_ref := !labelNotCtr;                  exp_ref := lftexp depth (!exp_ref);                 exp )          else exp      )     fun liftbd depth exp = (* called with the body of an Lfn *)      let val exp' = lftexp depth exp          val lbl = new_label()          val fre = List.map (fn id => Lvar ((~1) - id)) (!fv)          val qfv = List.length fre          val exp'' = Lprim(Pclosure (lbl, qfv), fre)      in        Stack.push (lbl, depth, !fv, !md, exp') stillToCompile;        (* Pr_lam.printLam exp';  -- e *)        (* Pr_lam.printLam exp''; -- e *)        (qfv, exp'')      end     fun liftfn depth exp =      case exp of        Lfn body => liftfn (depth + 1) body      | _        => liftbd depth exp     fun liftit depth exp =      case exp of        Lfn body => liftfn (depth + 1) body      | _        => let val exp' = lftexp depth exp in (List.length (!fv), exp') end   in liftit 0 exp end; (* The translator from lambda terms to lists of instructions.    env: the map from Lvar ids to stackptr offsets; side-effected   staticfail : the pair (label,sz) where Lstaticfail must branch.   sz: the current runtime stack model depth  (includes codegen temporaries)   dp: the depth of the Front.sml stack model (excludes codegen temporaries)   exp : the lambda term to compile.   C : the continuation, i.e. the code that follows the code for lambda.    The tests on the continuation detect tail-calls and avoid jumps to jumps,   or jumps to function returns. *) fun compileExp env staticfail =  let    open Array infix 9 sub     fun compexp sz dp exp C =      case exp of        Lvar n =>          (findEnv env sz n) :: C      | Lassign (n,exp') =>          compexp sz dp exp' ((findStk env sz n) :: C)      | Lconst cst =>          (case C of               Kquote _      :: _ => C             | Kget_global _ :: _ => C             | Kaccess _     :: _ => C             | Kenvacc _     :: _ => C             | _ => Kquote cst :: C)      | Lapply(body, args) =>          let val na = List.length args          in            if isReturn C            then compExpList sz dp args                   (Kpush :: compexp (sz + na) dp body                               (Kappterm (na, sz + na) :: discardDeadCode C))            else if na < 5            then compExpList sz dp args                   (Kpush :: compexp (sz + na) dp body (Kapply na :: C))            else		(* 3 is the number of stack positions used by Kpush_retaddr *)              let val (lbl, C1) = labelCode C              in Kpush_retaddr lbl ::                   compExpList (sz + 3) dp args                     (Kpush :: compexp (sz + 3 + na) dp body (Kapply na :: C1))              end          end      | Lfn body =>          fatalError "compileExp Lfn!?"      (* Special case arising from val _ = arg *)      | Llet([arg], Lunspec) => compexp sz dp arg C      | Llet(args, body) =>          let val na = List.length args              fun complet sz dp [] =                    compexp sz dp body (addPop na C)                | complet sz dp (exp::rest) =

Lines 1–500 of 855.