annotate src/fftw-3.3.8/genfft/annotate.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 (* Here, we take a schedule (produced by schedule.ml) ordering a
Chris@82 23 sequence of instructions, and produce an annotated schedule. The
Chris@82 24 annotated schedule has the same ordering as the original schedule,
Chris@82 25 but is additionally partitioned into nested blocks of temporary
Chris@82 26 variables. The partitioning is computed via a heuristic algorithm.
Chris@82 27
Chris@82 28 The blocking allows the C code that we generate to consist of
Chris@82 29 nested blocks that help communicate variable lifetimes to the
Chris@82 30 compiler. *)
Chris@82 31
Chris@82 32 open Schedule
Chris@82 33 open Expr
Chris@82 34 open Variable
Chris@82 35
Chris@82 36 type annotated_schedule =
Chris@82 37 Annotate of variable list * variable list * variable list * int * aschedule
Chris@82 38 and aschedule =
Chris@82 39 ADone
Chris@82 40 | AInstr of assignment
Chris@82 41 | ASeq of (annotated_schedule * annotated_schedule)
Chris@82 42
Chris@82 43 let addelem a set = if not (List.memq a set) then a :: set else set
Chris@82 44 let union l =
Chris@82 45 let f x = addelem x (* let is source of polymorphism *)
Chris@82 46 in List.fold_right f l
Chris@82 47
Chris@82 48 (* set difference a - b *)
Chris@82 49 let diff a b = List.filter (fun x -> not (List.memq x b)) a
Chris@82 50
Chris@82 51 let rec minimize f = function
Chris@82 52 [] -> failwith "minimize"
Chris@82 53 | [n] -> n
Chris@82 54 | n :: rest ->
Chris@82 55 let x = minimize f rest in
Chris@82 56 if (f x) >= (f n) then n else x
Chris@82 57
Chris@82 58 (* find all variables used inside a scheduling unit *)
Chris@82 59 let rec find_block_vars = function
Chris@82 60 Done -> []
Chris@82 61 | (Instr (Assign (v, x))) -> v :: (find_vars x)
Chris@82 62 | Par a -> List.flatten (List.map find_block_vars a)
Chris@82 63 | Seq (a, b) -> (find_block_vars a) @ (find_block_vars b)
Chris@82 64
Chris@82 65 let uniq l =
Chris@82 66 List.fold_right (fun a b -> if List.memq a b then b else a :: b) l []
Chris@82 67
Chris@82 68 let has_related x = List.exists (Variable.same_class x)
Chris@82 69
Chris@82 70 let rec overlap a b = Util.count (fun y -> has_related y b) a
Chris@82 71
Chris@82 72 (* reorder a list of schedules so as to maximize overlap of variables *)
Chris@82 73 let reorder l =
Chris@82 74 let rec loop = function
Chris@82 75 [] -> []
Chris@82 76 | (a, va) :: b ->
Chris@82 77 let c =
Chris@82 78 List.map
Chris@82 79 (fun (a, x) -> ((a, x), (overlap va x, List.length x))) b in
Chris@82 80 let c' =
Chris@82 81 Sort.list
Chris@82 82 (fun (_, (a, la)) (_, (b, lb)) ->
Chris@82 83 la < lb || a > b)
Chris@82 84 c in
Chris@82 85 let b' = List.map (fun (a, _) -> a) c' in
Chris@82 86 a :: (loop b') in
Chris@82 87 let l' = List.map (fun x -> x, uniq (find_block_vars x)) l in
Chris@82 88 (* start with smallest block --- does this matter ? *)
Chris@82 89 match l' with
Chris@82 90 [] -> []
Chris@82 91 | _ ->
Chris@82 92 let m = minimize (fun (_, x) -> (List.length x)) l' in
Chris@82 93 let l'' = Util.remove m l' in
Chris@82 94 loop (m :: l'')
Chris@82 95
Chris@82 96 (* remove Par blocks *)
Chris@82 97 let rec linearize = function
Chris@82 98 | Seq (a, Done) -> linearize a
Chris@82 99 | Seq (Done, a) -> linearize a
Chris@82 100 | Seq (a, b) -> Seq (linearize a, linearize b)
Chris@82 101
Chris@82 102 (* try to balance nested Par blocks *)
Chris@82 103 | Par [a] -> linearize a
Chris@82 104 | Par l ->
Chris@82 105 let n2 = (List.length l) / 2 in
Chris@82 106 let rec loop n a b =
Chris@82 107 if n = 0 then
Chris@82 108 (List.rev b, a)
Chris@82 109 else
Chris@82 110 match a with
Chris@82 111 [] -> failwith "loop"
Chris@82 112 | x :: y -> loop (n - 1) y (x :: b)
Chris@82 113 in let (a, b) = loop n2 (reorder l) []
Chris@82 114 in linearize (Seq (Par a, Par b))
Chris@82 115
Chris@82 116 | x -> x
Chris@82 117
Chris@82 118 let subset a b =
Chris@82 119 List.for_all (fun x -> List.exists (fun y -> x == y) b) a
Chris@82 120
Chris@82 121 let use_same_vars (Assign (av, ax)) (Assign (bv, bx)) =
Chris@82 122 is_temporary av &&
Chris@82 123 is_temporary bv &&
Chris@82 124 (let va = Expr.find_vars ax and vb = Expr.find_vars bx in
Chris@82 125 subset va vb && subset vb va)
Chris@82 126
Chris@82 127 let store_to_same_class (Assign (av, ax)) (Assign (bv, bx)) =
Chris@82 128 is_locative av &&
Chris@82 129 is_locative bv &&
Chris@82 130 Variable.same_class av bv
Chris@82 131
Chris@82 132 let loads_from_same_class (Assign (av, ax)) (Assign (bv, bx)) =
Chris@82 133 match (ax, bx) with
Chris@82 134 | (Load a), (Load b) when
Chris@82 135 Variable.is_locative a && Variable.is_locative b
Chris@82 136 -> Variable.same_class a b
Chris@82 137 | _ -> false
Chris@82 138
Chris@82 139 (* extract instructions from schedule *)
Chris@82 140 let rec sched_to_ilist = function
Chris@82 141 | Done -> []
Chris@82 142 | Instr a -> [a]
Chris@82 143 | Seq (a, b) -> (sched_to_ilist a) @ (sched_to_ilist b)
Chris@82 144 | _ -> failwith "sched_to_ilist" (* Par blocks removed by linearize *)
Chris@82 145
Chris@82 146 let rec find_friends friendp insn friends foes = function
Chris@82 147 | [] -> (friends, foes)
Chris@82 148 | a :: b ->
Chris@82 149 if (a == insn) || (friendp a insn) then
Chris@82 150 find_friends friendp insn (a :: friends) foes b
Chris@82 151 else
Chris@82 152 find_friends friendp insn friends (a :: foes) b
Chris@82 153
Chris@82 154 (* schedule all instructions in the equivalence class determined
Chris@82 155 by friendp at the point where the last one
Chris@82 156 is executed *)
Chris@82 157 let rec delay_friends friendp sched =
Chris@82 158 let rec recur insns = function
Chris@82 159 | Done -> (Done, insns)
Chris@82 160 | Instr a ->
Chris@82 161 let (friends, foes) = find_friends friendp a [] [] insns in
Chris@82 162 (Schedule.sequentially friends), foes
Chris@82 163 | Seq (a, b) ->
Chris@82 164 let (b', insnsb) = recur insns b in
Chris@82 165 let (a', insnsa) = recur insnsb a in
Chris@82 166 (Seq (a', b')), insnsa
Chris@82 167 | _ -> failwith "delay_friends"
Chris@82 168 in match recur (sched_to_ilist sched) sched with
Chris@82 169 | (s, []) -> s (* assert that all insns have been used *)
Chris@82 170 | _ -> failwith "delay_friends"
Chris@82 171
Chris@82 172 (* schedule all instructions in the equivalence class determined
Chris@82 173 by friendp at the point where the first one
Chris@82 174 is executed *)
Chris@82 175 let rec anticipate_friends friendp sched =
Chris@82 176 let rec recur insns = function
Chris@82 177 | Done -> (Done, insns)
Chris@82 178 | Instr a ->
Chris@82 179 let (friends, foes) = find_friends friendp a [] [] insns in
Chris@82 180 (Schedule.sequentially friends), foes
Chris@82 181 | Seq (a, b) ->
Chris@82 182 let (a', insnsa) = recur insns a in
Chris@82 183 let (b', insnsb) = recur insnsa b in
Chris@82 184 (Seq (a', b')), insnsb
Chris@82 185 | _ -> failwith "anticipate_friends"
Chris@82 186 in match recur (sched_to_ilist sched) sched with
Chris@82 187 | (s, []) -> s (* assert that all insns have been used *)
Chris@82 188 | _ -> failwith "anticipate_friends"
Chris@82 189
Chris@82 190 let collect_buddy_stores buddy_list sched =
Chris@82 191 let rec recur sched delayed_stores = match sched with
Chris@82 192 | Done -> (sched, delayed_stores)
Chris@82 193 | Instr (Assign (v, x)) ->
Chris@82 194 begin
Chris@82 195 try
Chris@82 196 let buddies = List.find (List.memq v) buddy_list in
Chris@82 197 let tmp = Variable.make_temporary () in
Chris@82 198 let i = Seq(Instr (Assign (tmp, x)),
Chris@82 199 Instr (Assign (v, Times (NaN MULTI_A, Load tmp))))
Chris@82 200 and delayed_stores = (v, Load tmp) :: delayed_stores in
Chris@82 201 try
Chris@82 202 (Seq (i,
Chris@82 203 Instr (Assign
Chris@82 204 (List.hd buddies,
Chris@82 205 Times (NaN MULTI_B,
Chris@82 206 Plus (List.map
Chris@82 207 (fun buddy ->
Chris@82 208 List.assq buddy
Chris@82 209 delayed_stores)
Chris@82 210 buddies))) )))
Chris@82 211 , delayed_stores
Chris@82 212 with Not_found -> (i, delayed_stores)
Chris@82 213 with Not_found -> (sched, delayed_stores)
Chris@82 214 end
Chris@82 215 | Seq (a, b) ->
Chris@82 216 let (newa, delayed_stores) = recur a delayed_stores in
Chris@82 217 let (newb, delayed_stores) = recur b delayed_stores in
Chris@82 218 (Seq (newa, newb), delayed_stores)
Chris@82 219 | _ -> failwith "collect_buddy_stores"
Chris@82 220 in let (sched, _) = recur sched [] in
Chris@82 221 sched
Chris@82 222
Chris@82 223 let schedule_for_pipeline sched =
Chris@82 224 let update_readytimes t (Assign (v, _)) ready_times =
Chris@82 225 (v, (t + !Magic.pipeline_latency)) :: ready_times
Chris@82 226 and readyp t ready_times (Assign (_, x)) =
Chris@82 227 List.for_all
Chris@82 228 (fun var ->
Chris@82 229 try
Chris@82 230 (List.assq var ready_times) <= t
Chris@82 231 with Not_found -> false)
Chris@82 232 (List.filter Variable.is_temporary (Expr.find_vars x))
Chris@82 233 in
Chris@82 234 let rec recur sched t ready_times delayed_instructions =
Chris@82 235 let (ready, not_ready) =
Chris@82 236 List.partition (readyp t ready_times) delayed_instructions
Chris@82 237 in match ready with
Chris@82 238 | a :: b ->
Chris@82 239 let (sched, t, ready_times, delayed_instructions) =
Chris@82 240 recur sched (t+1) (update_readytimes t a ready_times)
Chris@82 241 (b @ not_ready)
Chris@82 242 in
Chris@82 243 (Seq (Instr a, sched)), t, ready_times, delayed_instructions
Chris@82 244 | _ -> (match sched with
Chris@82 245 | Done -> (sched, t, ready_times, delayed_instructions)
Chris@82 246 | Instr a ->
Chris@82 247 if (readyp t ready_times a) then
Chris@82 248 (sched, (t+1), (update_readytimes t a ready_times),
Chris@82 249 delayed_instructions)
Chris@82 250 else
Chris@82 251 (Done, t, ready_times, (a :: delayed_instructions))
Chris@82 252 | Seq (a, b) ->
Chris@82 253 let (a, t, ready_times, delayed_instructions) =
Chris@82 254 recur a t ready_times delayed_instructions
Chris@82 255 in
Chris@82 256 let (b, t, ready_times, delayed_instructions) =
Chris@82 257 recur b t ready_times delayed_instructions
Chris@82 258 in (Seq (a, b)), t, ready_times, delayed_instructions
Chris@82 259 | _ -> failwith "schedule_for_pipeline")
Chris@82 260 in let rec recur_until_done sched t ready_times delayed_instructions =
Chris@82 261 let (sched, t, ready_times, delayed_instructions) =
Chris@82 262 recur sched t ready_times delayed_instructions
Chris@82 263 in match delayed_instructions with
Chris@82 264 | [] -> sched
Chris@82 265 | _ ->
Chris@82 266 (Seq (sched,
Chris@82 267 (recur_until_done Done (t+1) ready_times
Chris@82 268 delayed_instructions)))
Chris@82 269 in recur_until_done sched 0 [] []
Chris@82 270
Chris@82 271 let rec rewrite_declarations force_declarations
Chris@82 272 (Annotate (_, _, declared, _, what)) =
Chris@82 273 let m = !Magic.number_of_variables in
Chris@82 274
Chris@82 275 let declare_it declared =
Chris@82 276 if (force_declarations || List.length declared >= m) then
Chris@82 277 ([], declared)
Chris@82 278 else
Chris@82 279 (declared, [])
Chris@82 280
Chris@82 281 in match what with
Chris@82 282 ADone -> Annotate ([], [], [], 0, what)
Chris@82 283 | AInstr i ->
Chris@82 284 let (u, d) = declare_it declared
Chris@82 285 in Annotate ([], u, d, 0, what)
Chris@82 286 | ASeq (a, b) ->
Chris@82 287 let ma = rewrite_declarations false a
Chris@82 288 and mb = rewrite_declarations false b
Chris@82 289 in let Annotate (_, ua, _, _, _) = ma
Chris@82 290 and Annotate (_, ub, _, _, _) = mb
Chris@82 291 in let (u, d) = declare_it (declared @ ua @ ub)
Chris@82 292 in Annotate ([], u, d, 0, ASeq (ma, mb))
Chris@82 293
Chris@82 294 let annotate list_of_buddy_stores schedule =
Chris@82 295 let rec analyze live_at_end = function
Chris@82 296 Done -> Annotate (live_at_end, [], [], 0, ADone)
Chris@82 297 | Instr i -> (match i with
Chris@82 298 Assign (v, x) ->
Chris@82 299 let vars = (find_vars x) in
Chris@82 300 Annotate (Util.remove v (union live_at_end vars), [v], [],
Chris@82 301 0, AInstr i))
Chris@82 302 | Seq (a, b) ->
Chris@82 303 let ab = analyze live_at_end b in
Chris@82 304 let Annotate (live_at_begin_b, defined_b, _, depth_a, _) = ab in
Chris@82 305 let aa = analyze live_at_begin_b a in
Chris@82 306 let Annotate (live_at_begin_a, defined_a, _, depth_b, _) = aa in
Chris@82 307 let defined = List.filter is_temporary (defined_a @ defined_b) in
Chris@82 308 let declarable = diff defined live_at_end in
Chris@82 309 let undeclarable = diff defined declarable
Chris@82 310 and maxdepth = max depth_a depth_b in
Chris@82 311 Annotate (live_at_begin_a, undeclarable, declarable,
Chris@82 312 List.length declarable + maxdepth,
Chris@82 313 ASeq (aa, ab))
Chris@82 314 | _ -> failwith "really_analyze"
Chris@82 315
Chris@82 316 in
Chris@82 317 let () = Util.info "begin annotate" in
Chris@82 318 let x = linearize schedule in
Chris@82 319
Chris@82 320 let x =
Chris@82 321 if (!Magic.schedule_for_pipeline && !Magic.pipeline_latency > 0) then
Chris@82 322 schedule_for_pipeline x
Chris@82 323 else
Chris@82 324 x
Chris@82 325 in
Chris@82 326
Chris@82 327 let x =
Chris@82 328 if !Magic.reorder_insns then
Chris@82 329 linearize(anticipate_friends use_same_vars x)
Chris@82 330 else
Chris@82 331 x
Chris@82 332 in
Chris@82 333
Chris@82 334 (* delay stores to the real and imaginary parts of the same number *)
Chris@82 335 let x =
Chris@82 336 if !Magic.reorder_stores then
Chris@82 337 linearize(delay_friends store_to_same_class x)
Chris@82 338 else
Chris@82 339 x
Chris@82 340 in
Chris@82 341
Chris@82 342 (* move loads of the real and imaginary parts of the same number *)
Chris@82 343 let x =
Chris@82 344 if !Magic.reorder_loads then
Chris@82 345 linearize(anticipate_friends loads_from_same_class x)
Chris@82 346 else
Chris@82 347 x
Chris@82 348 in
Chris@82 349
Chris@82 350 let x = collect_buddy_stores list_of_buddy_stores x in
Chris@82 351 let x = analyze [] x in
Chris@82 352 let res = rewrite_declarations true x in
Chris@82 353 let () = Util.info "end annotate" in
Chris@82 354 res
Chris@82 355
Chris@82 356 let rec dump print (Annotate (_, _, _, _, code)) =
Chris@82 357 dump_code print code
Chris@82 358 and dump_code print = function
Chris@82 359 | ADone -> ()
Chris@82 360 | AInstr x -> print ((assignment_to_string x) ^ "\n")
Chris@82 361 | ASeq (a, b) -> dump print a; dump print b