annotate fft/fftw/fftw-3.3.4/genfft/to_alist.ml @ 40:223f770b5341 kissfft-double tip

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