annotate src/fftw-3.3.8/genfft/to_alist.ml @ 83:ae30d91d2ffe

Replace these with versions built using an older toolset (so as to avoid ABI compatibilities when linking on Ubuntu 14.04 for packaging purposes)
author Chris Cannam
date Fri, 07 Feb 2020 11:51:13 +0000
parents d0c2a83c1364
children
rev   line source
Chris@82 1 (*
Chris@82 2 * Copyright (c) 1997-1999 Massachusetts Institute of Technology
Chris@82 3 * Copyright (c) 2003, 2007-14 Matteo Frigo
Chris@82 4 * Copyright (c) 2003, 2007-14 Massachusetts Institute of Technology
Chris@82 5 *
Chris@82 6 * This program is free software; you can redistribute it and/or modify
Chris@82 7 * it under the terms of the GNU General Public License as published by
Chris@82 8 * the Free Software Foundation; either version 2 of the License, or
Chris@82 9 * (at your option) any later version.
Chris@82 10 *
Chris@82 11 * This program is distributed in the hope that it will be useful,
Chris@82 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Chris@82 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Chris@82 14 * GNU General Public License for more details.
Chris@82 15 *
Chris@82 16 * You should have received a copy of the GNU General Public License
Chris@82 17 * along with this program; if not, write to the Free Software
Chris@82 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Chris@82 19 *
Chris@82 20 *)
Chris@82 21
Chris@82 22 (*************************************************************
Chris@82 23 * Conversion of the dag to an assignment list
Chris@82 24 *************************************************************)
Chris@82 25 (*
Chris@82 26 * This function is messy. The main problem is that we want to
Chris@82 27 * inline dag nodes conditionally, depending on how many times they
Chris@82 28 * are used. The Right Thing to do would be to modify the
Chris@82 29 * state monad to propagate some of the state backwards, so that
Chris@82 30 * we know whether a given node will be used again in the future.
Chris@82 31 * This modification is trivial in a lazy language, but it is
Chris@82 32 * messy in a strict language like ML.
Chris@82 33 *
Chris@82 34 * In this implementation, we just do the obvious thing, i.e., visit
Chris@82 35 * the dag twice, the first to count the node usages, and the second to
Chris@82 36 * produce the output.
Chris@82 37 *)
Chris@82 38
Chris@82 39 open Monads.StateMonad
Chris@82 40 open Monads.MemoMonad
Chris@82 41 open Expr
Chris@82 42
Chris@82 43 let fresh = Variable.make_temporary
Chris@82 44 let node_insert x = Assoctable.insert Expr.hash x
Chris@82 45 let node_lookup x = Assoctable.lookup Expr.hash (==) x
Chris@82 46 let empty = Assoctable.empty
Chris@82 47
Chris@82 48 let fetchAl =
Chris@82 49 fetchState >>= (fun (al, _, _) -> returnM al)
Chris@82 50
Chris@82 51 let storeAl al =
Chris@82 52 fetchState >>= (fun (_, visited, visited') ->
Chris@82 53 storeState (al, visited, visited'))
Chris@82 54
Chris@82 55 let fetchVisited = fetchState >>= (fun (_, v, _) -> returnM v)
Chris@82 56
Chris@82 57 let storeVisited visited =
Chris@82 58 fetchState >>= (fun (al, _, visited') ->
Chris@82 59 storeState (al, visited, visited'))
Chris@82 60
Chris@82 61 let fetchVisited' = fetchState >>= (fun (_, _, v') -> returnM v')
Chris@82 62 let storeVisited' visited' =
Chris@82 63 fetchState >>= (fun (al, visited, _) ->
Chris@82 64 storeState (al, visited, visited'))
Chris@82 65 let lookupVisitedM' key =
Chris@82 66 fetchVisited' >>= fun table ->
Chris@82 67 returnM (node_lookup key table)
Chris@82 68 let insertVisitedM' key value =
Chris@82 69 fetchVisited' >>= fun table ->
Chris@82 70 storeVisited' (node_insert key value table)
Chris@82 71
Chris@82 72 let counting f x =
Chris@82 73 fetchVisited >>= (fun v ->
Chris@82 74 match node_lookup x v with
Chris@82 75 Some count ->
Chris@82 76 let incr_cnt =
Chris@82 77 fetchVisited >>= (fun v' ->
Chris@82 78 storeVisited (node_insert x (count + 1) v'))
Chris@82 79 in
Chris@82 80 begin
Chris@82 81 match x with
Chris@82 82 (* Uminus is always inlined. Visit child *)
Chris@82 83 Uminus y -> f y >> incr_cnt
Chris@82 84 | _ -> incr_cnt
Chris@82 85 end
Chris@82 86 | None ->
Chris@82 87 f x >> fetchVisited >>= (fun v' ->
Chris@82 88 storeVisited (node_insert x 1 v')))
Chris@82 89
Chris@82 90 let with_varM v x =
Chris@82 91 fetchAl >>= (fun al -> storeAl ((v, x) :: al)) >> returnM (Load v)
Chris@82 92
Chris@82 93 let inlineM = returnM
Chris@82 94
Chris@82 95 let with_tempM x = match x with
Chris@82 96 | Load v when Variable.is_temporary v -> inlineM x (* avoid trivial moves *)
Chris@82 97 | _ -> with_varM (fresh ()) x
Chris@82 98
Chris@82 99 (* declare a temporary only if node is used more than once *)
Chris@82 100 let with_temp_maybeM node x =
Chris@82 101 fetchVisited >>= (fun v ->
Chris@82 102 match node_lookup node v with
Chris@82 103 Some count ->
Chris@82 104 if (count = 1 && !Magic.inline_single) then
Chris@82 105 inlineM x
Chris@82 106 else
Chris@82 107 with_tempM x
Chris@82 108 | None ->
Chris@82 109 failwith "with_temp_maybeM")
Chris@82 110 type fma =
Chris@82 111 NO_FMA
Chris@82 112 | FMA of expr * expr * expr (* FMA (a, b, c) => a + b * c *)
Chris@82 113 | FMS of expr * expr * expr (* FMS (a, b, c) => -a + b * c *)
Chris@82 114 | FNMS of expr * expr * expr (* FNMS (a, b, c) => a - b * c *)
Chris@82 115
Chris@82 116 let good_for_fma (a, b) =
Chris@82 117 let good = function
Chris@82 118 | NaN I -> true
Chris@82 119 | NaN CONJ -> true
Chris@82 120 | NaN _ -> false
Chris@82 121 | Times(NaN _, _) -> false
Chris@82 122 | Times(_, NaN _) -> false
Chris@82 123 | _ -> true
Chris@82 124 in good a && good b
Chris@82 125
Chris@82 126 let build_fma l =
Chris@82 127 if (not !Magic.enable_fma) then NO_FMA
Chris@82 128 else match l with
Chris@82 129 | [a; Uminus (Times (b, c))] when good_for_fma (b, c) -> FNMS (a, b, c)
Chris@82 130 | [Uminus (Times (b, c)); a] when good_for_fma (b, c) -> FNMS (a, b, c)
Chris@82 131 | [Uminus a; Times (b, c)] when good_for_fma (b, c) -> FMS (a, b, c)
Chris@82 132 | [Times (b, c); Uminus a] when good_for_fma (b, c) -> FMS (a, b, c)
Chris@82 133 | [a; Times (b, c)] when good_for_fma (b, c) -> FMA (a, b, c)
Chris@82 134 | [Times (b, c); a] when good_for_fma (b, c) -> FMA (a, b, c)
Chris@82 135 | _ -> NO_FMA
Chris@82 136
Chris@82 137 let children_fma l = match build_fma l with
Chris@82 138 | FMA (a, b, c) -> Some (a, b, c)
Chris@82 139 | FMS (a, b, c) -> Some (a, b, c)
Chris@82 140 | FNMS (a, b, c) -> Some (a, b, c)
Chris@82 141 | NO_FMA -> None
Chris@82 142
Chris@82 143
Chris@82 144 let rec visitM x =
Chris@82 145 counting (function
Chris@82 146 | Load v -> returnM ()
Chris@82 147 | Num a -> returnM ()
Chris@82 148 | NaN a -> returnM ()
Chris@82 149 | Store (v, x) -> visitM x
Chris@82 150 | Plus a -> (match children_fma a with
Chris@82 151 None -> mapM visitM a >> returnM ()
Chris@82 152 | Some (a, b, c) ->
Chris@82 153 (* visit fma's arguments twice to make sure they are not inlined *)
Chris@82 154 visitM a >> visitM a >>
Chris@82 155 visitM b >> visitM b >>
Chris@82 156 visitM c >> visitM c)
Chris@82 157 | Times (a, b) -> visitM a >> visitM b
Chris@82 158 | CTimes (a, b) -> visitM a >> visitM b
Chris@82 159 | CTimesJ (a, b) -> visitM a >> visitM b
Chris@82 160 | Uminus a -> visitM a)
Chris@82 161 x
Chris@82 162
Chris@82 163 let visit_rootsM = mapM visitM
Chris@82 164
Chris@82 165
Chris@82 166 let rec expr_of_nodeM x =
Chris@82 167 memoizing lookupVisitedM' insertVisitedM'
Chris@82 168 (function x -> match x with
Chris@82 169 | Load v ->
Chris@82 170 if (Variable.is_temporary v) then
Chris@82 171 inlineM (Load v)
Chris@82 172 else if (Variable.is_locative v && !Magic.inline_loads) then
Chris@82 173 inlineM (Load v)
Chris@82 174 else if (Variable.is_constant v && !Magic.inline_loads_constants) then
Chris@82 175 inlineM (Load v)
Chris@82 176 else
Chris@82 177 with_tempM (Load v)
Chris@82 178 | Num a ->
Chris@82 179 if !Magic.inline_constants then
Chris@82 180 inlineM (Num a)
Chris@82 181 else
Chris@82 182 with_temp_maybeM x (Num a)
Chris@82 183 | NaN a -> inlineM (NaN a)
Chris@82 184 | Store (v, x) ->
Chris@82 185 expr_of_nodeM x >>=
Chris@82 186 (if !Magic.trivial_stores then with_tempM else inlineM) >>=
Chris@82 187 with_varM v
Chris@82 188
Chris@82 189 | Plus a ->
Chris@82 190 begin
Chris@82 191 match build_fma a with
Chris@82 192 FMA (a, b, c) ->
Chris@82 193 expr_of_nodeM a >>= fun a' ->
Chris@82 194 expr_of_nodeM b >>= fun b' ->
Chris@82 195 expr_of_nodeM c >>= fun c' ->
Chris@82 196 with_temp_maybeM x (Plus [a'; Times (b', c')])
Chris@82 197 | FMS (a, b, c) ->
Chris@82 198 expr_of_nodeM a >>= fun a' ->
Chris@82 199 expr_of_nodeM b >>= fun b' ->
Chris@82 200 expr_of_nodeM c >>= fun c' ->
Chris@82 201 with_temp_maybeM x
Chris@82 202 (Plus [Times (b', c'); Uminus a'])
Chris@82 203 | FNMS (a, b, c) ->
Chris@82 204 expr_of_nodeM a >>= fun a' ->
Chris@82 205 expr_of_nodeM b >>= fun b' ->
Chris@82 206 expr_of_nodeM c >>= fun c' ->
Chris@82 207 with_temp_maybeM x
Chris@82 208 (Plus [a'; Uminus (Times (b', c'))])
Chris@82 209 | NO_FMA ->
Chris@82 210 mapM expr_of_nodeM a >>= fun a' ->
Chris@82 211 with_temp_maybeM x (Plus a')
Chris@82 212 end
Chris@82 213 | CTimes (Load _ as a, b) when !Magic.generate_bytw ->
Chris@82 214 expr_of_nodeM b >>= fun b' ->
Chris@82 215 with_tempM (CTimes (a, b'))
Chris@82 216 | CTimes (a, b) ->
Chris@82 217 expr_of_nodeM a >>= fun a' ->
Chris@82 218 expr_of_nodeM b >>= fun b' ->
Chris@82 219 with_tempM (CTimes (a', b'))
Chris@82 220 | CTimesJ (Load _ as a, b) when !Magic.generate_bytw ->
Chris@82 221 expr_of_nodeM b >>= fun b' ->
Chris@82 222 with_tempM (CTimesJ (a, b'))
Chris@82 223 | CTimesJ (a, b) ->
Chris@82 224 expr_of_nodeM a >>= fun a' ->
Chris@82 225 expr_of_nodeM b >>= fun b' ->
Chris@82 226 with_tempM (CTimesJ (a', b'))
Chris@82 227 | Times (a, b) ->
Chris@82 228 expr_of_nodeM a >>= fun a' ->
Chris@82 229 expr_of_nodeM b >>= fun b' ->
Chris@82 230 begin
Chris@82 231 match a' with
Chris@82 232 Num a'' when !Magic.strength_reduce_mul && Number.is_two a'' ->
Chris@82 233 (inlineM b' >>= fun b'' ->
Chris@82 234 with_temp_maybeM x (Plus [b''; b'']))
Chris@82 235 | _ -> with_temp_maybeM x (Times (a', b'))
Chris@82 236 end
Chris@82 237 | Uminus a ->
Chris@82 238 expr_of_nodeM a >>= fun a' ->
Chris@82 239 inlineM (Uminus a'))
Chris@82 240 x
Chris@82 241
Chris@82 242 let expr_of_rootsM = mapM expr_of_nodeM
Chris@82 243
Chris@82 244 let peek_alistM roots =
Chris@82 245 visit_rootsM roots >> expr_of_rootsM roots >> fetchAl
Chris@82 246
Chris@82 247 let wrap_assign (a, b) = Expr.Assign (a, b)
Chris@82 248
Chris@82 249 let to_assignments dag =
Chris@82 250 let () = Util.info "begin to_alist" in
Chris@82 251 let al = List.rev (runM ([], empty, empty) peek_alistM dag) in
Chris@82 252 let res = List.map wrap_assign al in
Chris@82 253 let () = Util.info "end to_alist" in
Chris@82 254 res
Chris@82 255
Chris@82 256
Chris@82 257 (* dump alist in `dot' format *)
Chris@82 258 let dump print alist =
Chris@82 259 let vs v = "\"" ^ (Variable.unparse v) ^ "\"" in
Chris@82 260 begin
Chris@82 261 print "digraph G {\n";
Chris@82 262 print "\tsize=\"6,6\";\n";
Chris@82 263
Chris@82 264 (* all input nodes have the same rank *)
Chris@82 265 print "{ rank = same;\n";
Chris@82 266 List.iter (fun (Expr.Assign (v, x)) ->
Chris@82 267 List.iter (fun y ->
Chris@82 268 if (Variable.is_locative y) then print("\t" ^ (vs y) ^ ";\n"))
Chris@82 269 (Expr.find_vars x))
Chris@82 270 alist;
Chris@82 271 print "}\n";
Chris@82 272
Chris@82 273 (* all output nodes have the same rank *)
Chris@82 274 print "{ rank = same;\n";
Chris@82 275 List.iter (fun (Expr.Assign (v, x)) ->
Chris@82 276 if (Variable.is_locative v) then print("\t" ^ (vs v) ^ ";\n"))
Chris@82 277 alist;
Chris@82 278 print "}\n";
Chris@82 279
Chris@82 280 (* edges *)
Chris@82 281 List.iter (fun (Expr.Assign (v, x)) ->
Chris@82 282 List.iter (fun y -> print("\t" ^ (vs y) ^ " -> " ^ (vs v) ^ ";\n"))
Chris@82 283 (Expr.find_vars x))
Chris@82 284 alist;
Chris@82 285
Chris@82 286 print "}\n";
Chris@82 287 end
Chris@82 288