From 67cf06ca8cdd20d884cebf9b5af2649d5e0b042b Mon Sep 17 00:00:00 2001 From: Quentin Carbonneaux Date: Wed, 18 Feb 2015 16:42:48 -0500 Subject: wip --- lo2.ml | 228 +++++++++++++++++------------------------------------------------ 1 file changed, 58 insertions(+), 170 deletions(-) diff --git a/lo2.ml b/lo2.ml index 22c1029..12790c9 100644 --- a/lo2.ml +++ b/lo2.ml @@ -91,7 +91,7 @@ let liveness (p: iprog) = (* ** Register allocation. ** *) -type loc = LVoid | LReg of int | LSpill of int +type loc = LVoid | LReg of int | LSpill of int | LCon of int type 'op rins = { ri_res: 'op; ri_ins: [ 'op seqi | `Mov of 'op ] } type 'op rphi = { rp_res: 'op; rp_list: (bref * loc) list } type rprog = (loc rins, loc rphi, loc jmpi) bb array @@ -116,7 +116,7 @@ let regalloc (p: iprog) = let bb = ref [] in (* Basic block in construction. *) let emiti l i = bb := {ri_res=l; ri_ins=i} :: !bb in let act = H.create 101 in (* The active list. *) - let free = ref [0;1;2;3;4] in (* Free registers. *) + let free = ref [0;1;2] in (* Free registers. *) let nspill = ref 0 in let newspill () = incr nspill; !nspill - 1 in @@ -136,11 +136,44 @@ let regalloc (p: iprog) = let l = match !free with | r :: f -> free := f; LReg r - | [] -> LSpill (newspill ()) (* Here we can try to spill the longer range instead. *) + | [] -> LSpill (newspill ()) in H.add act ir l; l | l -> l in + let rec getreg frz = (* Aggressively obtain one register. *) + match !free with + | r :: f when List.mem r frz -> (* Frozen, can't use it. *) + free := f; + let r' = getreg frz in + free := r :: !free; r' + | r :: f -> free := f; r + | [] -> (* Spill needed! *) + match + H.fold (fun ir loc l -> (* Find candidates. *) + match loc with + | LReg r when not (List.mem r frz) -> + (ir, r) :: l + | _ -> l + ) act [] (* |> sort by spill cost *) + with [] -> failwith "god damn it, not enough registers" + | (ir, r) :: _ -> + H.remove act ir; + let s = getspill ir in + let s = + if s >= 0 then s else + let s' = newspill () in + H.add act ir (LSpill s'); s' in + emiti (LReg r) (`Mov (LSpill s)); + r in + + let regloc frz ir = + match H.find act ir with + | LReg r -> r + | _ -> + let r = getreg frz in + H.add act ir (LReg r); r in + for b = nbb - 1 downto 0 do let bi = p.(b).bb_inss in let bl = Array.length bi in @@ -151,20 +184,15 @@ let regalloc (p: iprog) = *) let lvout = liveout lh (b, bl) in - IRSet.iter (fun ir -> ignore (loc ir)) lvout; outmaps.(b) <- begin - H.fold (fun ir l m -> - if IRSet.mem ir lvout - then (ir, l) :: m - else m - ) act [] + IRSet.fold (fun ir m -> (ir, loc ir) :: m) lvout [] end; let jmp = match p.(b).bb_jmp with | `Jmp br -> `Jmp br | `Brz (ir, br1, br2) -> - `Brz (LReg (regloc ir), br1, br2) in + `Brz (loc ir, br1, br2) in rp.(b).bb_jmp <- jmp; for i = bl - 1 downto 0 do @@ -172,35 +200,34 @@ let regalloc (p: iprog) = begin match H.find act ir with | LVoid -> () (* Dead code. *) | lir -> - let r = + let r, frz = match lir with - | LSpill spl1 -> - (* Restore in a register. - * - * In this situation, the register for the result - * must not be killed because it lives until the - * spill code executes. - * - * This is a bit silly because we will move it - * into a spill location right after that, there - * is no benefit from having it in register here. - *) - let r = getreg - | LReg r -> kill ir; r (* In register, we can kill it now. *) + | LSpill s -> + let r = getreg [] in + emiti (LSpill s) (`Mov (LReg r)); + if not (List.mem r !free) then + free := r :: !free; (* Add it straight back to free, but freeze it. *) + (r, [r]) + | LReg r -> (r, []) | _ -> assert false in + kill ir; + let s = getspill ir in begin match bi.(i) with | `Con k -> - emiti lir (`Mov (LCon k)) + if s >= 0 then emiti (LSpill s) (`Mov (LReg r)); + emiti (LReg r) (`Mov (LCon k)) | `Uop (op, ir') -> - let r' = regloc ir' in + let r' = regloc frz ir' in + if s >= 0 then emiti (LSpill s) (`Mov (LReg r)); emiti (LReg r) (`Uop (op, LReg r')) | `Bop (ir1, op, ir2) -> - let r1 = regloc ir1 in - let r2 = regloc ~block:r1 ir2 in + let r1 = regloc frz ir1 in + let frz = r1 :: frz in + let r2 = regloc frz ir2 in + if s >= 0 then emiti (LSpill s) (`Mov (LReg r)); emiti (LReg r) (`Bop (LReg r1, op, LReg r2)) end; - kill ir; end done; @@ -212,25 +239,11 @@ let regalloc (p: iprog) = ) end; - (* Spill everyting not in liveout of the predecessor block. - * Remove them from the active list (ensures Invariant 1). - *) - + (* Kill everything not in liveout of the predecessor block. *) let lvout = if b = 0 then IRSet.empty else liveout lh (b-1, Array.length p.(b-1).bb_inss) in - let spl = H.fold (fun ir l s -> - match l with - | LReg r -> - if IRSet.mem ir lvout then s else (ir, r) :: s - | _ -> s - ) act [] in - List.iter (fun (ir, r) -> - let spl = LSpill (newspill ()) in - free := r :: !free; - H.replace act ir spl; - emiti (LReg r) (`Mov spl) - ) spl; + IRSet.iter kill lvout; rp.(b).bb_inss <- Array.of_list !bb; bb := []; @@ -257,131 +270,6 @@ let regalloc (p: iprog) = *) -(* ** NEW attempt at a more clever allocator. ** *) - -let ircmp a b = - let blk = function IRPhi (b,_) | IRIns (b,_) -> b in - let cb = compare (blk a) (blk b) in - if cb <> 0 then cb else - match a, b with - | IRPhi _, IRIns _ -> -1 - | IRIns _, IRPhi _ -> +1 - | IRPhi (_,x), IRPhi (_,y) - | IRIns (_,x), IRIns (_,y) -> compare x y - -(* An interval specifies a region of the program text (usually where - * a variable is live. It has two bounds, lo is exclusive and hi is - * inclusive. - *) -type inter = { lo: iref; hi: iref } - -(* The register type is used to store the usage of a given register - * by the program. The list of intervals it stores has to be non- - * overlapping. - * Invariant: Intervals are stored. - *) -type reg = { mutable busy: (iref * inter) list } - -let reg_dispo {busy} i = - let rec f = function - | (_, {lo; hi}) :: l -> - if ircmp hi i.lo < 0 then f l else (* [lo, hi] ... [i] *) - if ircmp lo i.hi < 0 then true else (* [i] ... [lo, hi] *) - false (* overlap *) - | [] -> true in - f busy - -let reg_add r ir i = - assert (reg_dispo r i); - let c (_,a) (_,b) = ircmp a.lo b.lo in - r.busy <- List.sort c ((ir, i) :: r.busy) - -let mkinters (p: iprog) = - let module H = Hashtbl in - let lh = liveness p in - let ih = H.create 1001 in - let n = ref 0 in (* Fairly hashish. *) - let setlive ir loc = - let rec f = function - | [] -> [({lo=loc; hi=loc}, !n)] - | ({lo;_}, n') :: [] when n'+1 = !n -> [({lo; hi=loc}, !n)] - | x :: l' -> x :: f l' in - H.replace ih ir - (f (try H.find ih ir with Not_found -> [])) in - for b = 0 to Array.length p - 1 do - for i = -1 to Array.length p.(b).bb_inss do - let loc = IRIns (b,i) in - IRSet.iter (fun ir -> setlive ir loc) - (liveout lh (b,i)); - incr n; - done - done; - let hp = Heap.create (fun (_,a) (_,b) -> - match a, b with - | a::_, b::_ -> ircmp a.lo b.lo - | _ -> assert false - ) in - H.iter (fun ir il -> - Heap.add hp (ir, List.map fst il) - ) ih; - hp - -let regalloc2 ?(nr=4) (p: iprog) = - let nbb = Array.length p in - - let _regs = Array.init nr (fun _ -> {busy=[]}) in - let _spillh = Heap.create (fun (_,a) (_,b) -> ircmp b a) in - let act = Hashtbl.create 101 in (* Active list. *) - let _loc_ ir = - try Hashtbl.find act ir - with Not_found -> LVoid in - - let rp = Array.init nbb (fun i -> - { bb_name = p.(i).bb_name - ; bb_phis = [| |] - ; bb_inss = [| |] - ; bb_jmp = `Jmp (-1) - } - ) in - let bb = ref [] in (* Block in construction. *) - let _emit l i = bb := {ri_res=l;ri_ins=i} :: !bb in - - for b = 0 to nbb do - let bi = p.(b).bb_inss in - let bl = Array.length bi in - - (* Entering a block. - * Many phi intervals start at the same time. - * We need to allocate them registers and store - * the allocator state for the resolve phase. - *) - - for i = 0 to bl - 1 do - let _ir = IRIns (b,i) in - - (* For a regular instruction: - * 1. Get locations of arguments. - * Make sure they are in registers. - * 2. Free registers of dead variables. - * 3. Allocate for the new interval. - * 4. Emit the instruction. - *) - - () - done; - - (* Leaving a block. - * Rewrite the jump. - * Store the allocator state for the resolve - * phase. - *) - - rp.(b).bb_inss <- Array.of_list (List.rev !bb); - done; - - rp - - (* Little test programs. *) let pbasic: iprog = [| { bb_name = "start" -- cgit 1.4.1