cannam@167: (* cannam@167: * Copyright (c) 1997-1999 Massachusetts Institute of Technology cannam@167: * Copyright (c) 2003, 2007-14 Matteo Frigo cannam@167: * Copyright (c) 2003, 2007-14 Massachusetts Institute of Technology cannam@167: * cannam@167: * This program is free software; you can redistribute it and/or modify cannam@167: * it under the terms of the GNU General Public License as published by cannam@167: * the Free Software Foundation; either version 2 of the License, or cannam@167: * (at your option) any later version. cannam@167: * cannam@167: * This program is distributed in the hope that it will be useful, cannam@167: * but WITHOUT ANY WARRANTY; without even the implied warranty of cannam@167: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the cannam@167: * GNU General Public License for more details. cannam@167: * cannam@167: * You should have received a copy of the GNU General Public License cannam@167: * along with this program; if not, write to the Free Software cannam@167: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA cannam@167: * cannam@167: *) cannam@167: cannam@167: (* policies for loading/computing twiddle factors *) cannam@167: open Complex cannam@167: open Util cannam@167: cannam@167: type twop = TW_FULL | TW_CEXP | TW_NEXT cannam@167: cannam@167: let optostring = function cannam@167: | TW_CEXP -> "TW_CEXP" cannam@167: | TW_NEXT -> "TW_NEXT" cannam@167: | TW_FULL -> "TW_FULL" cannam@167: cannam@167: type twinstr = (twop * int * int) cannam@167: cannam@167: let rec unroll_twfull l = match l with cannam@167: | [] -> [] cannam@167: | (TW_FULL, v, n) :: b -> cannam@167: (forall [] cons 1 n (fun i -> (TW_CEXP, v, i))) cannam@167: @ unroll_twfull b cannam@167: | a :: b -> a :: unroll_twfull b cannam@167: cannam@167: let twinstr_to_c_string l = cannam@167: let one (op, a, b) = Printf.sprintf "{ %s, %d, %d }" (optostring op) a b cannam@167: in let rec loop first = function cannam@167: | [] -> "" cannam@167: | a :: b -> (if first then "\n" else ",\n") ^ (one a) ^ (loop false b) cannam@167: in "{" ^ (loop true l) ^ "}" cannam@167: cannam@167: let twinstr_to_simd_string vl l = cannam@167: let one sep = function cannam@167: | (TW_NEXT, 1, 0) -> sep ^ "{TW_NEXT, " ^ vl ^ ", 0}" cannam@167: | (TW_NEXT, _, _) -> failwith "twinstr_to_simd_string" cannam@167: | (TW_CEXP, v, b) -> sep ^ (Printf.sprintf "VTW(%d,%d)" v b) cannam@167: | _ -> failwith "twinstr_to_simd_string" cannam@167: in let rec loop first = function cannam@167: | [] -> "" cannam@167: | a :: b -> (one (if first then "\n" else ",\n") a) ^ (loop false b) cannam@167: in "{" ^ (loop true (unroll_twfull l)) ^ "}" cannam@167: cannam@167: let rec pow m n = cannam@167: if (n = 0) then 1 cannam@167: else m * pow m (n - 1) cannam@167: cannam@167: let rec is_pow m n = cannam@167: n = 1 || ((n mod m) = 0 && is_pow m (n / m)) cannam@167: cannam@167: let rec log m n = if n = 1 then 0 else 1 + log m (n / m) cannam@167: cannam@167: let rec largest_power_smaller_than m i = cannam@167: if (is_pow m i) then i cannam@167: else largest_power_smaller_than m (i - 1) cannam@167: cannam@167: let rec smallest_power_larger_than m i = cannam@167: if (is_pow m i) then i cannam@167: else smallest_power_larger_than m (i + 1) cannam@167: cannam@167: let rec_array n f = cannam@167: let g = ref (fun i -> Complex.zero) in cannam@167: let a = Array.init n (fun i -> lazy (!g i)) in cannam@167: let h i = f (fun i -> Lazy.force a.(i)) i in cannam@167: begin cannam@167: g := h; cannam@167: h cannam@167: end cannam@167: cannam@167: cannam@167: let ctimes use_complex_arith a b = cannam@167: if use_complex_arith then cannam@167: Complex.ctimes a b cannam@167: else cannam@167: Complex.times a b cannam@167: cannam@167: let ctimesj use_complex_arith a b = cannam@167: if use_complex_arith then cannam@167: Complex.ctimesj a b cannam@167: else cannam@167: Complex.times (Complex.conj a) b cannam@167: cannam@167: let make_bytwiddle sign use_complex_arith g f i = cannam@167: if i = 0 then cannam@167: f i cannam@167: else if sign = 1 then cannam@167: ctimes use_complex_arith (g i) (f i) cannam@167: else cannam@167: ctimesj use_complex_arith (g i) (f i) cannam@167: cannam@167: (* various policies for computing/loading twiddle factors *) cannam@167: cannam@167: let twiddle_policy_load_all v use_complex_arith = cannam@167: let bytwiddle n sign w f = cannam@167: make_bytwiddle sign use_complex_arith (fun i -> w (i - 1)) f cannam@167: and twidlen n = 2 * (n - 1) cannam@167: and twdesc r = [(TW_FULL, v, r);(TW_NEXT, 1, 0)] cannam@167: in bytwiddle, twidlen, twdesc cannam@167: cannam@167: (* cannam@167: * if i is a power of two, then load w (log i) cannam@167: * else let x = largest power of 2 less than i in cannam@167: * let y = i - x in cannam@167: * compute w^{x+y} = w^x * w^y cannam@167: *) cannam@167: let twiddle_policy_log2 v use_complex_arith = cannam@167: let bytwiddle n sign w f = cannam@167: let g = rec_array n (fun self i -> cannam@167: if i = 0 then Complex.one cannam@167: else if is_pow 2 i then w (log 2 i) cannam@167: else let x = largest_power_smaller_than 2 i in cannam@167: let y = i - x in cannam@167: ctimes use_complex_arith (self x) (self y)) cannam@167: in make_bytwiddle sign use_complex_arith g f cannam@167: and twidlen n = 2 * (log 2 (largest_power_smaller_than 2 (2 * n - 1))) cannam@167: and twdesc n = cannam@167: (List.flatten cannam@167: (List.map cannam@167: (fun i -> cannam@167: if i > 0 && is_pow 2 i then cannam@167: [TW_CEXP, v, i] cannam@167: else cannam@167: []) cannam@167: (iota n))) cannam@167: @ [(TW_NEXT, 1, 0)] cannam@167: in bytwiddle, twidlen, twdesc cannam@167: cannam@167: let twiddle_policy_log3 v use_complex_arith = cannam@167: let rec terms_needed i pi s n = cannam@167: if (s >= n - 1) then i cannam@167: else terms_needed (i + 1) (3 * pi) (s + pi) n cannam@167: in cannam@167: let rec bytwiddle n sign w f = cannam@167: let nterms = terms_needed 0 1 0 n in cannam@167: let maxterm = pow 3 (nterms - 1) in cannam@167: let g = rec_array (3 * n) (fun self i -> cannam@167: if i = 0 then Complex.one cannam@167: else if is_pow 3 i then w (log 3 i) cannam@167: else if i = (n - 1) && maxterm >= n then cannam@167: w (nterms - 1) cannam@167: else let x = smallest_power_larger_than 3 i in cannam@167: if (i + i >= x) then cannam@167: let x = min x (n - 1) in cannam@167: ctimesj use_complex_arith (self (x - i)) (self x) cannam@167: else let x = largest_power_smaller_than 3 i in cannam@167: ctimes use_complex_arith (self (i - x)) (self x)) cannam@167: in make_bytwiddle sign use_complex_arith g f cannam@167: and twidlen n = 2 * (terms_needed 0 1 0 n) cannam@167: and twdesc n = cannam@167: (List.map cannam@167: (fun i -> cannam@167: let x = min (pow 3 i) (n - 1) in cannam@167: TW_CEXP, v, x) cannam@167: (iota ((twidlen n) / 2))) cannam@167: @ [(TW_NEXT, 1, 0)] cannam@167: in bytwiddle, twidlen, twdesc cannam@167: cannam@167: let current_twiddle_policy = ref twiddle_policy_load_all cannam@167: cannam@167: let twiddle_policy use_complex_arith = cannam@167: !current_twiddle_policy use_complex_arith cannam@167: cannam@167: let set_policy x = Arg.Unit (fun () -> current_twiddle_policy := x) cannam@167: let set_policy_int x = Arg.Int (fun i -> current_twiddle_policy := x i) cannam@167: cannam@167: let undocumented = " Undocumented twiddle policy" cannam@167: cannam@167: let speclist = [ cannam@167: "-twiddle-load-all", set_policy twiddle_policy_load_all, undocumented; cannam@167: "-twiddle-log2", set_policy twiddle_policy_log2, undocumented; cannam@167: "-twiddle-log3", set_policy twiddle_policy_log3, undocumented; cannam@167: ]