(* SIMPLIFICATION PASSES *) (* Order of simplifications : - cascade slices and selects - transform k = SLICE i j var when var = CONCAT var' var'' - simplify stupid things (a xor 0 = a, a and 0 = 0, etc.) transform k = SLICE i i var into k = SELECT i var - transform k = SELECT 0 var into k = var when var is also one bit - look for variables with same equation, put the second to identity - eliminate k' for each equation k' = k - topological sort TODO : eliminate unused variables. problem : they are hard to identify *) open Netlist_ast module Sset = Set.Make(String) module Smap = Map.Make(String) (* Simplify cascade slicing/selecting *) let cascade_slices p = let usefull = ref false in let slices = Hashtbl.create (Env.cardinal p.p_vars / 12) in let eqs_new = List.map (fun (n, eq) -> (n, match eq with | Eslice(u, v, Avar(x)) -> let dec, nx = if Hashtbl.mem slices x then begin Hashtbl.find slices x end else (0, x) in Hashtbl.add slices n (u + dec, nx); if nx <> x || dec <> 0 then usefull := true; Eslice(u + dec, v + dec, Avar(nx)) | Eselect(u, Avar(x)) -> begin try let ku, kx = Hashtbl.find slices x in usefull := true; Eselect(ku + u, Avar(kx)) with Not_found -> Eselect(u, Avar(x)) end | _ -> eq)) p.p_eqs in { p_eqs = eqs_new; p_inputs = p.p_inputs; p_outputs = p.p_outputs; p_vars = p.p_vars; }, !usefull (* If var = CONCAT a b x = SLICE i j var or y = SELECT i var then x or y may be simplified *) let pass_concat p = let usefull = ref false in let concats = Hashtbl.create (Env.cardinal p.p_vars / 12) in List.iter (fun (n, eq) -> match eq with | Econcat(x, y) -> let s1 = match x with | Aconst(a) -> Array.length a | Avar(z) -> Env.find z p.p_vars in let s2 = match y with | Aconst(a) -> Array.length a | Avar(z) -> Env.find z p.p_vars in Hashtbl.add concats n (x, s1, y, s2) | _ -> ()) p.p_eqs; let eqs_new = List.map (fun (n, eq) -> (n, match eq with | Eselect(i, Avar(n)) -> begin try let (x, s1, y, s2) = Hashtbl.find concats n in usefull := true; if i < s1 then Eselect(i, x) else Eselect(i-s1, y) with Not_found -> eq end | Eslice(i, j, Avar(n)) -> begin try let (x, s1, y, s2) = Hashtbl.find concats n in if j < s1 then begin usefull := true; Eslice(i, j, x) end else if i >= s1 then begin usefull := true; Eslice(i - s1, j - s1, y) end else eq with Not_found -> eq end | _ -> eq)) p.p_eqs in { p_eqs = eqs_new; p_inputs = p.p_inputs; p_outputs = p.p_outputs; p_vars = p.p_vars; }, !usefull (* Simplifies some trivial arithmetic possibilites : a and 1 = a a and 0 = 0 a or 1 = 1 a or 0 = a a xor 0 = a slice i i x = select i x concat const const = const.const slice i j const = const.[i..j] select i const = const.[i] *) let arith_simplify p = let usefull = ref false in { p_eqs = List.map (fun (n, eq) -> let useless = ref false in let neq = match eq with | Ebinop(Or, Aconst([|false|]), x) -> Earg(x) | Ebinop(Or, Aconst([|true|]), x) -> Earg(Aconst([|true|])) | Ebinop(Or, x, Aconst([|false|])) -> Earg(x) | Ebinop(Or, x, Aconst([|true|])) -> Earg(Aconst([|true|])) | Ebinop(And, Aconst([|false|]), x) -> Earg(Aconst([|false|])) | Ebinop(And, Aconst([|true|]), x) -> Earg(x) | Ebinop(And, x, Aconst([|false|])) -> Earg(Aconst([|false|])) | Ebinop(And, x, Aconst([|true|])) -> Earg(x) | Ebinop(Xor, Aconst([|false|]), x) -> Earg(x) | Ebinop(Xor, x, Aconst([|false|])) -> Earg(x) | Ebinop(Nand, Avar(a), Avar(b)) when a = b -> Enot(Avar(a)) | Ebinop(Xor, Avar(a), Avar(b)) when a = b -> let sz = Env.find a p.p_vars in Earg(Aconst(Array.make sz false)) | Ebinop(_, Avar(a), Avar(b)) when a = b -> Earg(Avar(a)) | Emux(_, a, b) when a = b -> Earg(a) | Emux(Aconst[|false|], a, b) -> Earg(a) | Emux(Aconst[|true|], a, b) -> Earg(b) | Emux(c, Aconst[|false|], Avar(b)) when Env.find b p.p_vars = 1 -> Ebinop(And, c, Avar(b)) | Emux(c, Avar(b), Aconst[|true|]) when Env.find b p.p_vars = 1 -> Ebinop(Or, c, Avar(b)) | Eslice(i, j, k) when i = j -> Eselect(i, k) | Econcat(Aconst(a), Aconst(b)) -> Earg(Aconst(Array.append a b)) | Eslice(i, j, Aconst(a)) -> Earg(Aconst(Array.sub a i (j - i + 1))) | Eselect(i, Aconst(a)) -> Earg(Aconst([|a.(i)|])) | _ -> useless := true; eq in if not !useless then usefull := true; (n, neq)) p.p_eqs; p_inputs = p.p_inputs; p_outputs = p.p_outputs; p_vars = p.p_vars; }, !usefull (* if x is one bit, then : select 0 x = x and same thing with select *) let select_to_id p = let usefull = ref false in { p_eqs = List.map (fun (n, eq) -> match eq with | Eselect(0, Avar(id)) when Env.find id p.p_vars = 1 -> usefull := true; (n, Earg(Avar(id))) | Eslice(0, sz, Avar(id)) when Env.find id p.p_vars = sz + 1 -> usefull := true; (n, Earg(Avar(id))) | _ -> (n, eq)) p.p_eqs; p_inputs = p.p_inputs; p_outputs = p.p_outputs; p_vars = p.p_vars; }, !usefull (* If a = eqn(v1, v2, ...) and b = eqn(v1, v2, ...) <- the same equation then say b = a *) let same_eq_simplify p = let usefull = ref false in let id_outputs = (List.fold_left (fun x k -> Sset.add k x) Sset.empty p.p_outputs) in let eq_map = Hashtbl.create (List.length p.p_eqs) in List.iter (fun (n, eq) -> if Sset.mem n id_outputs then Hashtbl.add eq_map eq n) p.p_eqs; let simplify_eq (n, eq) = if Sset.mem n id_outputs then (n, eq) else if Hashtbl.mem eq_map eq then begin usefull := true; (n, Earg(Avar(Hashtbl.find eq_map eq))) end else begin Hashtbl.add eq_map eq n; (n, eq) end in let eq2 = List.map simplify_eq p.p_eqs in { p_eqs = eq2; p_inputs = p.p_inputs; p_outputs = p.p_outputs; p_vars = p.p_vars; }, !usefull (* Replace one specific variable by another argument in the arguments of all equations (possibly a constant, possibly another variable) *) let eliminate_var var rep p = let rep_arg = function | Avar(i) when i = var -> rep | k -> k in let rep_eqs = List.map (fun (n, eq) -> (n, match eq with | Earg(a) -> Earg(rep_arg a) | Ereg(i) when i = var -> begin match rep with | Avar(j) -> Ereg(j) | Aconst(k) -> Earg(Aconst(k)) end | Ereg(j) -> Ereg(j) | Enot(a) -> Enot(rep_arg a) | Ebinop(o, a, b) -> Ebinop(o, rep_arg a, rep_arg b) | Emux(a, b, c) -> Emux(rep_arg a, rep_arg b, rep_arg c) | Erom(u, v, a) -> Erom(u, v, rep_arg a) | Eram(u, v, a, b, c, d) -> Eram(u, v, rep_arg a, rep_arg b, rep_arg c, rep_arg d) | Econcat(a, b) -> Econcat(rep_arg a, rep_arg b) | Eslice(u, v, a) -> Eslice(u, v, rep_arg a) | Eselect(u, a) -> Eselect(u, rep_arg a) )) p.p_eqs in { p_eqs = List.fold_left (fun x (n, eq) -> if n = var then x else (n, eq)::x) [] rep_eqs; p_inputs = p.p_inputs; p_outputs = p.p_outputs; p_vars = Env.remove var p.p_vars; } (* Remove all equations of type : a = b a = const (except if a is an output variable) *) let rec eliminate_id p = let id_outputs = (List.fold_left (fun x k -> Sset.add k x) Sset.empty p.p_outputs) in let rep = List.fold_left (fun x (n, eq) -> if x = None && (not (Sset.mem n id_outputs)) then match eq with | Earg(rarg) -> Some(n, rarg) | _ -> None else x) None p.p_eqs in match rep with | None -> p, false | Some(n, rep) -> fst (eliminate_id (eliminate_var n rep p)), true (* Eliminate dead variables *) let eliminate_dead p = let rec living basis = let new_basis = List.fold_left (fun b2 (n, eq) -> if Sset.mem n b2 then List.fold_left (fun x k -> Sset.add k x) b2 (Scheduler.read_exp_all eq) else b2) basis (List.rev p.p_eqs) in if Sset.cardinal new_basis > Sset.cardinal basis then living new_basis else new_basis in let outs = List.fold_left (fun x k -> Sset.add k x) Sset.empty p.p_outputs in let ins = List.fold_left (fun x k -> Sset.add k x) Sset.empty p.p_inputs in let live = living (Sset.union outs ins) in { p_eqs = List.filter (fun (n, _) -> Sset.mem n live) p.p_eqs; p_inputs = p.p_inputs; p_outputs = p.p_outputs; p_vars = Env.fold (fun k s newenv -> if Sset.mem k live then Env.add k s newenv else newenv) p.p_vars Env.empty }, (Sset.cardinal live < Env.cardinal p.p_vars) (* Topological sort *) let topo_sort p = (Scheduler.schedule p, false) (* Apply all the simplification passes, in the order given in the header of this file *) let rec simplify_with steps p = let pp, use = List.fold_left (fun (x, u) (f, n) -> Format.printf "%s...%!" n; let xx, uu = f x in Format.printf "%s\n%!" (if uu then " *" else ""); (xx, u || uu)) (p, false) steps in if use then simplify_with steps pp else pp let simplify p = let p = simplify_with [ cascade_slices, "cascade_slices"; pass_concat, "pass_concat"; select_to_id, "select_to_id"; arith_simplify, "arith_simplify"; ] p in let p = simplify_with [ arith_simplify, "arith_simplify"; same_eq_simplify, "same_eq_simplify"; eliminate_id, "eliminate_id"; ] p in let p = simplify_with [ cascade_slices, "cascade_slices"; pass_concat, "pass_concat"; arith_simplify, "arith_simplify"; select_to_id, "select_to_id"; same_eq_simplify, "same_eq_simplify"; eliminate_id, "eliminate_id"; ] p in let p = simplify_with [ eliminate_dead, "eliminate_dead"; topo_sort, "topo_sort"; (* make sure last step is a topological sort *) ] p in p