···11+open Ir
22+open Cfg
33+44+module IntMap = Map.Make(Int)
55+module IntSet = Set.Make(Int)
66+77+(* Immediate dominator, derived correctly from full dominator sets.
88+ The idom of n is the unique proper dominator d of n that is itself
99+ dominated by every other proper dominator of n (i.e. the deepest one).
1010+ This replaces the previous ad-hoc heuristic, which could mis-pick an outer
1111+ dominator when a node had 3+ dominators. *)
1212+let idom cfg doms id =
1313+ let s = IntMap.find id doms in
1414+ let without = IntSet.diff s (IntSet.singleton id) in
1515+ if IntSet.is_empty without then None
1616+ else
1717+ let strict_doms = IntSet.elements without in
1818+ (* Find the d in strict_doms that every other strict dominator dominates. *)
1919+ let rec find = function
2020+ | [] -> None
2121+ | d :: rest ->
2222+ if List.for_all (fun m -> m = d || IntSet.mem d (IntMap.find m doms)) strict_doms
2323+ then Some d
2424+ else find rest
2525+ in
2626+ find strict_doms
2727+2828+let dominators cfg =
2929+ let all = IntMap.fold (fun k _ s -> IntSet.add k s) cfg.blocks IntSet.empty in
3030+ let init = IntSet.singleton cfg.entry in
3131+ let rec fixpoint doms =
3232+ let new_doms = IntMap.mapi (fun id _ ->
3333+ if id = cfg.entry then init
3434+ else
3535+ let b = IntMap.find id cfg.blocks in
3636+ match b.preds with
3737+ | [] -> IntSet.empty
3838+ | p :: ps ->
3939+ let inter = List.fold_left (fun acc pr ->
4040+ try IntSet.inter acc (IntMap.find pr doms)
4141+ with Not_found -> acc
4242+ ) (try IntMap.find p doms with Not_found -> IntSet.empty) ps in
4343+ IntSet.add id inter
4444+ ) cfg.blocks in
4545+ if IntMap.equal IntSet.equal doms new_doms then doms
4646+ else fixpoint new_doms
4747+ in
4848+ fixpoint (IntMap.map (fun _ -> all) cfg.blocks)
4949+5050+let dominates doms a b =
5151+ IntSet.mem a (IntMap.find b doms)
5252+5353+let dominance_frontiers cfg doms =
5454+ let df = ref IntMap.empty in
5555+ IntMap.iter (fun id _ -> df := IntMap.add id IntSet.empty !df) cfg.blocks;
5656+ IntMap.iter (fun id b ->
5757+ match b.preds with
5858+ | [] | [_] -> ()
5959+ | _ ->
6060+ let idom_y = idom cfg doms id in
6161+ List.iter (fun p ->
6262+ (* Runner p is a predecessor of Y (p <> Y). Add Y to DF at each node
6363+ from p up to (but not including) Y's idom. p itself is included. *)
6464+ let rec walk cur =
6565+ if Some cur = idom_y then () (* reached idom(Y): stop, don't add *)
6666+ else begin
6767+ df := IntMap.add cur (IntSet.add id (IntMap.find cur !df)) !df;
6868+ (match idom cfg doms cur with
6969+ | None -> ()
7070+ | Some n -> walk n)
7171+ end
7272+ in
7373+ walk p
7474+ ) b.preds
7575+ ) cfg.blocks;
7676+ !df
···11+type value = int
22+33+type inst =
44+ | Alloca of value
55+ | Load of value * value
66+ | Store of value * value
77+ | Phi of value * (value * value) list
88+ | Move of value * value
99+ | BinOp of value * value * value
1010+ | Ret of value option
1111+ | Br of value
1212+ | CondBr of value * value * value
1313+1414+type block = {
1515+ id : value;
1616+ insts : inst list;
1717+ mutable preds : value list;
1818+ mutable succs : value list;
1919+}
2020+2121+type func = {
2222+ blocks : block list;
2323+ entry : value;
2424+}
2525+2626+let mk_block id insts =
2727+ { id; insts; preds = []; succs = [] }
···11+open Ir
22+open Cfg
33+open Dominance
44+55+module PairMap = Map.Make(struct
66+ type t = int * int
77+ let compare (a1, b1) (a2, b2) =
88+ match Stdlib.compare a1 a2 with
99+ | 0 -> Stdlib.compare b1 b2
1010+ | c -> c
1111+end)
1212+1313+type alloca_info = {
1414+ aid : value;
1515+ mutable stores : (value * value) list;
1616+ mutable loads : (value * value) list;
1717+}
1818+1919+let find_allocas cfg =
2020+ let info = ref IntMap.empty in
2121+ IntMap.iter (fun _ b ->
2222+ List.iter (function
2323+ | Alloca a -> info := IntMap.add a { aid = a; stores = []; loads = [] } !info
2424+ | _ -> ()
2525+ ) b.insts
2626+ ) cfg.blocks;
2727+ IntMap.iter (fun _ b ->
2828+ List.iter (function
2929+ | Store (v, p) ->
3030+ (try let ai = IntMap.find p !info in ai.stores <- (v, b.id) :: ai.stores with Not_found -> ())
3131+ | Load (r, p) ->
3232+ (try let ai = IntMap.find p !info in ai.loads <- (r, b.id) :: ai.loads with Not_found -> ())
3333+ | _ -> ()
3434+ ) b.insts
3535+ ) cfg.blocks;
3636+ IntMap.fold (fun _ ai acc -> ai :: acc) !info []
3737+3838+let place_phi cfg df allocas =
3939+ let phi_needed = ref PairMap.empty in
4040+ List.iter (fun ai ->
4141+ let blocks_with_store = ref (List.fold_left (fun s (_, b) -> IntSet.add b s) IntSet.empty ai.stores) in
4242+ let work = ref (IntSet.elements !blocks_with_store) in
4343+ while !work <> [] do
4444+ let b = List.hd !work in
4545+ work := List.tl !work;
4646+ (try
4747+ let frontiers = IntMap.find b df in
4848+ IntSet.iter (fun f ->
4949+ let key = (f, ai.aid) in
5050+ if not (PairMap.mem key !phi_needed) then begin
5151+ phi_needed := PairMap.add key true !phi_needed;
5252+ if not (IntSet.mem f !blocks_with_store) then begin
5353+ blocks_with_store := IntSet.add f !blocks_with_store;
5454+ work := f :: !work
5555+ end
5656+ end
5757+ ) frontiers
5858+ with Not_found -> ())
5959+ done
6060+ ) allocas;
6161+ !phi_needed
6262+6363+let rename cfg phi_needed allocas =
6464+ let stacks = ref IntMap.empty in
6565+ let push aid v =
6666+ let s = try IntMap.find aid !stacks with Not_found -> [] in
6767+ stacks := IntMap.add aid (v :: s) !stacks
6868+ in
6969+ let pop aid =
7070+ let s = IntMap.find aid !stacks in
7171+ if List.tl s = [] then stacks := IntMap.remove aid !stacks
7272+ else stacks := IntMap.add aid (List.tl s) !stacks
7373+ in
7474+ let top aid =
7575+ try Some (List.hd (IntMap.find aid !stacks))
7676+ with Not_found -> None
7777+ in
7878+ (* For each (block, alloca) needing a phi, accumulate operands as the DFS
7979+ visits each predecessor. Each entry maps a predecessor id to the SSA
8080+ version of the variable on that edge. These are read only AFTER the full
8181+ DFS completes, so every predecessor has been visited. *)
8282+ let phi_ops = ref (PairMap.map (fun _ -> ref []) phi_needed) in
8383+ let body_insts = ref IntMap.empty in
8484+ let process_block id b =
8585+ (* A block that needs a phi defines a new version of the variable (using
8686+ the alloca id as the SSA name). Push it so subsequent loads in this
8787+ block read the phi's result rather than a stale predecessor version. *)
8888+ let pushed = ref [] in
8989+ List.iter (fun ai ->
9090+ if PairMap.mem (id, ai.aid) phi_needed then (push ai.aid ai.aid; pushed := ai.aid :: !pushed)
9191+ ) allocas;
9292+ let insts = ref [] in
9393+ List.iter (fun i ->
9494+ match i with
9595+ | Alloca a -> () (* promoted away *)
9696+ | Store (v, p) ->
9797+ (* A store defines a new version of the variable. Push it once. *)
9898+ push p v; pushed := p :: !pushed
9999+ | Load (r, p) ->
100100+ (match top p with
101101+ | Some v -> insts := Move (r, v) :: !insts
102102+ | None -> ())
103103+ | Phi _ -> () (* input phis are replaced by the inserted ones *)
104104+ | _ -> insts := i :: !insts
105105+ ) b.insts;
106106+ (* Rev to restore original order (insts was built by consing). *)
107107+ body_insts := IntMap.add id (List.rev !insts) !body_insts;
108108+ !pushed
109109+ in
110110+ let add_phi_operand block aid pred v =
111111+ if PairMap.mem (block, aid) phi_needed then
112112+ let ops_ref = PairMap.find (block, aid) !phi_ops in
113113+ (* Store as (pred, value) so IntMap.of_list below builds a pred->value map. *)
114114+ ops_ref := (pred, v) :: !ops_ref
115115+ in
116116+ let rec dfs id visited =
117117+ if IntSet.mem id visited then visited
118118+ else
119119+ let visited = IntSet.add id visited in
120120+ let b = IntMap.find id cfg.blocks in
121121+ let pushed = process_block id b in
122122+ let visited = List.fold_left (fun vis s ->
123123+ (* Edge id -> s: the version of each variable live on entry to s is the
124124+ current top of its stack. Record it as s's phi operand from id,
125125+ BEFORE descending into s (so s's own phi push doesn't interfere). *)
126126+ List.iter (fun ai ->
127127+ match top ai.aid with
128128+ | Some v -> add_phi_operand s ai.aid id v
129129+ | None -> ()
130130+ ) allocas;
131131+ dfs s vis
132132+ ) visited b.succs in
133133+ (* Pop every version this block pushed before backtracking. *)
134134+ List.iter pop pushed;
135135+ visited
136136+ in
137137+ ignore (dfs cfg.entry IntSet.empty);
138138+ (* Now build phi instructions from the fully-collected operands. *)
139139+ let phi_insts = ref IntMap.empty in
140140+ PairMap.iter (fun (block, aid) ops_ref ->
141141+ let ops_map = IntMap.of_list !ops_ref in
142142+ let b = IntMap.find block cfg.blocks in
143143+ let phi_ops_list = List.map (fun p ->
144144+ match IntMap.find_opt p ops_map with
145145+ | Some v -> (v, p)
146146+ | None -> (0, p)
147147+ ) b.preds in
148148+ let existing = try IntMap.find block !phi_insts with Not_found -> [] in
149149+ phi_insts := IntMap.add block (Phi (aid, phi_ops_list) :: existing) !phi_insts
150150+ ) !phi_ops;
151151+ (* Final per-block instruction list: phis first, then the rewritten body. *)
152152+ let new_insts = ref IntMap.empty in
153153+ IntMap.iter (fun id body ->
154154+ let phis = try IntMap.find id !phi_insts with Not_found -> [] in
155155+ new_insts := IntMap.add id (phis @ body) !new_insts
156156+ ) !body_insts;
157157+ !new_insts
158158+159159+let mem2reg f =
160160+ let cfg = build_cfg f in
161161+ let doms = dominators cfg in
162162+ let df = dominance_frontiers cfg doms in
163163+ let allocas = find_allocas cfg in
164164+ let phi_needed = place_phi cfg df allocas in
165165+ let new_insts = rename cfg phi_needed allocas in
166166+ let new_blocks = IntMap.mapi (fun id b ->
167167+ { b with insts = try IntMap.find id new_insts with Not_found -> b.insts }
168168+ ) cfg.blocks in
169169+ (* Preserve the original input block order for deterministic output. *)
170170+ let ordered = List.map (fun b -> IntMap.find b.id new_blocks) f.blocks in
171171+ { f with blocks = ordered }
···11+open Ir
22+open Parser
33+open Nemo
44+open Printer
55+66+let () =
77+ if Array.length Sys.argv < 2 then begin
88+ Printf.eprintf "Usage: %s <file.ir>\n" Sys.argv.(0);
99+ exit 1
1010+ end;
1111+ let path = Sys.argv.(1) in
1212+ let f = parse_func_from_file path in
1313+ let f' = mem2reg f in
1414+ Printf.printf "Before (mem2reg input):\n";
1515+ print_func f;
1616+ Printf.printf "\nAfter (SSA):\n";
1717+ print_func f'