362 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			OCaml
		
	
	
	
	
	
			
		
		
	
	
			362 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			OCaml
		
	
	
	
	
	
(*
 | 
						|
 * Copyright (c) 1997-1999 Massachusetts Institute of Technology
 | 
						|
 * Copyright (c) 2003, 2007-14 Matteo Frigo
 | 
						|
 * Copyright (c) 2003, 2007-14 Massachusetts Institute of Technology
 | 
						|
 *
 | 
						|
 * This program is free software; you can redistribute it and/or modify
 | 
						|
 * it under the terms of the GNU General Public License as published by
 | 
						|
 * the Free Software Foundation; either version 2 of the License, or
 | 
						|
 * (at your option) any later version.
 | 
						|
 *
 | 
						|
 * This program is distributed in the hope that it will be useful,
 | 
						|
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
 * GNU General Public License for more details.
 | 
						|
 *
 | 
						|
 * You should have received a copy of the GNU General Public License
 | 
						|
 * along with this program; if not, write to the Free Software
 | 
						|
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 | 
						|
 *
 | 
						|
 *)
 | 
						|
 | 
						|
(* Here, we take a schedule (produced by schedule.ml) ordering a
 | 
						|
   sequence of instructions, and produce an annotated schedule.  The
 | 
						|
   annotated schedule has the same ordering as the original schedule,
 | 
						|
   but is additionally partitioned into nested blocks of temporary
 | 
						|
   variables.  The partitioning is computed via a heuristic algorithm.
 | 
						|
 | 
						|
   The blocking allows the C code that we generate to consist of
 | 
						|
   nested blocks that help communicate variable lifetimes to the
 | 
						|
   compiler. *)
 | 
						|
 | 
						|
open Schedule
 | 
						|
open Expr
 | 
						|
open Variable
 | 
						|
 | 
						|
type annotated_schedule = 
 | 
						|
    Annotate of variable list * variable list * variable list * int * aschedule
 | 
						|
and aschedule = 
 | 
						|
    ADone
 | 
						|
  | AInstr of assignment
 | 
						|
  | ASeq of (annotated_schedule * annotated_schedule)
 | 
						|
 | 
						|
let addelem a set = if not (List.memq a set) then a :: set else set
 | 
						|
let union l = 
 | 
						|
  let f x = addelem x   (* let is source of polymorphism *)
 | 
						|
  in List.fold_right f l
 | 
						|
 | 
						|
(* set difference a - b *)
 | 
						|
let diff a b = List.filter (fun x -> not (List.memq x b)) a
 | 
						|
 | 
						|
let rec minimize f = function
 | 
						|
    [] -> failwith "minimize"
 | 
						|
  | [n] -> n
 | 
						|
  | n :: rest ->
 | 
						|
      let x = minimize f rest in
 | 
						|
      if (f x) >= (f n) then n else x
 | 
						|
 | 
						|
(* find all variables used inside a scheduling unit *)
 | 
						|
let rec find_block_vars = function
 | 
						|
    Done -> []
 | 
						|
  | (Instr (Assign (v, x))) -> v :: (find_vars x)
 | 
						|
  | Par a -> List.flatten (List.map find_block_vars a)
 | 
						|
  | Seq (a, b) -> (find_block_vars a) @ (find_block_vars b)
 | 
						|
 | 
						|
let uniq l = 
 | 
						|
  List.fold_right (fun a b -> if List.memq a b then b else a :: b) l []
 | 
						|
 | 
						|
let has_related x = List.exists (Variable.same_class x)
 | 
						|
 | 
						|
let rec overlap a b = Util.count (fun y -> has_related y b) a
 | 
						|
 | 
						|
(* reorder a list of schedules so as to maximize overlap of variables *)
 | 
						|
let reorder l =
 | 
						|
  let rec loop = function
 | 
						|
      [] -> []
 | 
						|
    | (a, va) :: b ->
 | 
						|
	let c = 
 | 
						|
	  List.map 
 | 
						|
	    (fun (a, x) -> ((a, x), (overlap va x, List.length x))) b in
 | 
						|
	let c' =
 | 
						|
	  List.sort 
 | 
						|
	    (fun (_, (a, la)) (_, (b, lb)) ->
 | 
						|
              if la < lb || a > b then -1 else 1)
 | 
						|
	    c in
 | 
						|
	let b' = List.map (fun (a, _) -> a) c' in
 | 
						|
	a :: (loop b') in
 | 
						|
  let l' = List.map (fun x -> x, uniq (find_block_vars x)) l in
 | 
						|
  (* start with smallest block --- does this matter ? *)
 | 
						|
  match l' with
 | 
						|
    [] -> []
 | 
						|
  | _ ->  
 | 
						|
      let m = minimize (fun (_, x) -> (List.length x)) l' in
 | 
						|
      let l'' = Util.remove m l' in
 | 
						|
      loop (m :: l'')
 | 
						|
 | 
						|
(* remove Par blocks *)
 | 
						|
let rec linearize = function
 | 
						|
  | Seq (a, Done) -> linearize a
 | 
						|
  | Seq (Done, a) -> linearize a
 | 
						|
  | Seq (a, b) -> Seq (linearize a, linearize b)
 | 
						|
 | 
						|
  (* try to balance nested Par blocks *)
 | 
						|
  | Par [a] -> linearize a
 | 
						|
  | Par l -> 
 | 
						|
      let n2 = (List.length l) / 2 in
 | 
						|
      let rec loop n a b =
 | 
						|
	if n = 0 then
 | 
						|
	  (List.rev b, a)
 | 
						|
	else
 | 
						|
	  match a with
 | 
						|
	    [] -> failwith "loop"
 | 
						|
	  | x :: y -> loop (n - 1) y (x :: b)
 | 
						|
      in let (a, b) = loop n2 (reorder l) []
 | 
						|
      in linearize (Seq (Par a, Par b))
 | 
						|
 | 
						|
  | x -> x 
 | 
						|
 | 
						|
let subset a b =
 | 
						|
  List.for_all (fun x -> List.exists (fun y -> x == y) b) a
 | 
						|
 | 
						|
let use_same_vars (Assign (av, ax)) (Assign (bv, bx)) =
 | 
						|
  is_temporary av &&
 | 
						|
  is_temporary bv &&
 | 
						|
  (let va = Expr.find_vars ax and vb = Expr.find_vars bx in
 | 
						|
   subset va vb && subset vb va)
 | 
						|
 | 
						|
let store_to_same_class (Assign (av, ax)) (Assign (bv, bx)) =
 | 
						|
  is_locative av &&
 | 
						|
  is_locative bv &&
 | 
						|
  Variable.same_class av bv
 | 
						|
 | 
						|
let loads_from_same_class (Assign (av, ax)) (Assign (bv, bx)) =
 | 
						|
  match (ax, bx) with
 | 
						|
    | (Load a), (Load b) when 
 | 
						|
	Variable.is_locative a && Variable.is_locative b 
 | 
						|
	-> Variable.same_class a b
 | 
						|
    | _ -> false
 | 
						|
 | 
						|
(* extract instructions from schedule *)
 | 
						|
let rec sched_to_ilist = function
 | 
						|
  | Done -> []
 | 
						|
  | Instr a -> [a]
 | 
						|
  | Seq (a, b) -> (sched_to_ilist a) @ (sched_to_ilist b)
 | 
						|
  | _ -> failwith "sched_to_ilist" (* Par blocks removed by linearize *)
 | 
						|
 | 
						|
let rec find_friends friendp insn friends foes = function
 | 
						|
  | [] -> (friends, foes)
 | 
						|
  | a :: b -> 
 | 
						|
      if (a == insn) || (friendp a insn) then
 | 
						|
	find_friends friendp insn (a :: friends) foes b
 | 
						|
      else
 | 
						|
	find_friends friendp insn friends (a :: foes) b
 | 
						|
 | 
						|
(* schedule all instructions in the equivalence class determined
 | 
						|
   by friendp at the point where the last one
 | 
						|
   is executed *)
 | 
						|
let rec delay_friends friendp sched =
 | 
						|
  let rec recur insns = function
 | 
						|
    | Done -> (Done, insns)
 | 
						|
    | Instr a ->
 | 
						|
	let (friends, foes) = find_friends friendp a [] [] insns in
 | 
						|
	(Schedule.sequentially friends), foes
 | 
						|
    | Seq (a, b) ->
 | 
						|
	let (b', insnsb) = recur insns b in
 | 
						|
	let (a', insnsa) = recur insnsb a in
 | 
						|
	(Seq (a', b')), insnsa
 | 
						|
    | _ -> failwith "delay_friends"
 | 
						|
  in match recur (sched_to_ilist sched) sched with
 | 
						|
  | (s, []) -> s (* assert that all insns have been used *)
 | 
						|
  | _ -> failwith "delay_friends"
 | 
						|
 | 
						|
(* schedule all instructions in the equivalence class determined
 | 
						|
   by friendp at the point where the first one
 | 
						|
   is executed *)
 | 
						|
let rec anticipate_friends friendp sched =
 | 
						|
  let rec recur insns = function
 | 
						|
    | Done -> (Done, insns)
 | 
						|
    | Instr a ->
 | 
						|
	let (friends, foes) = find_friends friendp a [] [] insns in
 | 
						|
	(Schedule.sequentially friends), foes
 | 
						|
    | Seq (a, b) ->
 | 
						|
	let (a', insnsa) = recur insns a in
 | 
						|
	let (b', insnsb) = recur insnsa b in
 | 
						|
	(Seq (a', b')), insnsb
 | 
						|
    | _ -> failwith "anticipate_friends"
 | 
						|
  in match recur (sched_to_ilist sched) sched with
 | 
						|
  | (s, []) -> s (* assert that all insns have been used *)
 | 
						|
  | _ -> failwith "anticipate_friends"
 | 
						|
 | 
						|
let collect_buddy_stores buddy_list sched =
 | 
						|
  let rec recur sched delayed_stores = match sched with
 | 
						|
    | Done -> (sched, delayed_stores)
 | 
						|
    | Instr (Assign (v, x)) ->
 | 
						|
	begin
 | 
						|
	  try
 | 
						|
	    let buddies = List.find (List.memq v) buddy_list in 
 | 
						|
	    let tmp = Variable.make_temporary () in
 | 
						|
	    let i = Seq(Instr (Assign (tmp, x)),
 | 
						|
			Instr (Assign (v, Times (NaN MULTI_A, Load tmp))))
 | 
						|
	    and delayed_stores = (v, Load tmp) :: delayed_stores in
 | 
						|
	      try
 | 
						|
		(Seq (i,
 | 
						|
		      Instr (Assign 
 | 
						|
			       (List.hd buddies,
 | 
						|
				Times (NaN MULTI_B,
 | 
						|
				       Plus (List.map 
 | 
						|
					       (fun buddy ->
 | 
						|
						  List.assq buddy 
 | 
						|
						    delayed_stores)
 | 
						|
					       buddies))) )))
 | 
						|
		  , delayed_stores
 | 
						|
	      with Not_found -> (i, delayed_stores)
 | 
						|
	  with Not_found -> (sched, delayed_stores)
 | 
						|
	end
 | 
						|
    | Seq (a, b) ->
 | 
						|
	let (newa, delayed_stores) = recur a delayed_stores in
 | 
						|
	let (newb, delayed_stores) = recur b delayed_stores in
 | 
						|
	  (Seq (newa, newb), delayed_stores)
 | 
						|
    | _ -> failwith "collect_buddy_stores"
 | 
						|
  in let (sched, _) = recur sched [] in
 | 
						|
    sched
 | 
						|
 | 
						|
let schedule_for_pipeline sched =
 | 
						|
  let update_readytimes t (Assign (v, _)) ready_times = 
 | 
						|
    (v, (t + !Magic.pipeline_latency)) :: ready_times
 | 
						|
  and readyp t ready_times (Assign (_, x)) =
 | 
						|
    List.for_all 
 | 
						|
      (fun var -> 
 | 
						|
	 try 
 | 
						|
	   (List.assq var ready_times) <= t
 | 
						|
	 with Not_found -> false)
 | 
						|
      (List.filter Variable.is_temporary (Expr.find_vars x))
 | 
						|
  in
 | 
						|
  let rec recur sched t ready_times delayed_instructions =
 | 
						|
    let (ready, not_ready) = 
 | 
						|
      List.partition (readyp t ready_times) delayed_instructions 
 | 
						|
    in match ready with
 | 
						|
      | a :: b -> 
 | 
						|
	  let (sched, t, ready_times, delayed_instructions) =
 | 
						|
	    recur sched (t+1) (update_readytimes t a ready_times)
 | 
						|
	      (b @ not_ready)
 | 
						|
	  in
 | 
						|
	    (Seq (Instr a, sched)), t, ready_times, delayed_instructions
 | 
						|
      | _ -> (match sched with
 | 
						|
		| Done -> (sched, t, ready_times, delayed_instructions)
 | 
						|
		| Instr a ->
 | 
						|
		    if (readyp t ready_times a) then
 | 
						|
		      (sched, (t+1), (update_readytimes t a ready_times),
 | 
						|
		       delayed_instructions)
 | 
						|
		    else
 | 
						|
		      (Done, t, ready_times, (a :: delayed_instructions))
 | 
						|
		| Seq (a, b) ->
 | 
						|
		    let (a, t, ready_times, delayed_instructions) =
 | 
						|
		      recur a t ready_times delayed_instructions 
 | 
						|
		    in
 | 
						|
		    let (b, t, ready_times, delayed_instructions) =
 | 
						|
		      recur b t ready_times delayed_instructions 
 | 
						|
		    in (Seq (a, b)), t, ready_times, delayed_instructions
 | 
						|
	        | _ -> failwith "schedule_for_pipeline")
 | 
						|
  in let rec recur_until_done sched t ready_times delayed_instructions =
 | 
						|
      let (sched, t, ready_times, delayed_instructions) = 
 | 
						|
	recur sched t ready_times delayed_instructions
 | 
						|
      in match delayed_instructions with
 | 
						|
	| [] -> sched
 | 
						|
	| _ -> 
 | 
						|
	    (Seq (sched,
 | 
						|
		  (recur_until_done Done (t+1) ready_times 
 | 
						|
		     delayed_instructions)))
 | 
						|
  in recur_until_done sched 0 [] []
 | 
						|
  
 | 
						|
let rec rewrite_declarations force_declarations 
 | 
						|
    (Annotate (_, _, declared, _, what)) =
 | 
						|
  let m = !Magic.number_of_variables in
 | 
						|
 | 
						|
  let declare_it declared =
 | 
						|
    if (force_declarations || List.length declared >= m) then
 | 
						|
      ([], declared)
 | 
						|
    else
 | 
						|
      (declared, [])
 | 
						|
 | 
						|
  in match what with
 | 
						|
    ADone -> Annotate ([], [], [], 0, what)
 | 
						|
  | AInstr i -> 
 | 
						|
      let (u, d) = declare_it declared
 | 
						|
      in Annotate ([], u, d, 0, what)
 | 
						|
  | ASeq (a, b) ->
 | 
						|
      let ma = rewrite_declarations false a
 | 
						|
      and mb = rewrite_declarations false b
 | 
						|
      in let Annotate (_, ua, _, _, _) = ma
 | 
						|
      and Annotate (_, ub, _, _, _) = mb
 | 
						|
      in let (u, d) = declare_it (declared @ ua @ ub)
 | 
						|
      in Annotate ([], u, d, 0, ASeq (ma, mb))
 | 
						|
 | 
						|
let annotate list_of_buddy_stores schedule =
 | 
						|
  let rec analyze live_at_end = function
 | 
						|
      Done -> Annotate (live_at_end, [], [], 0, ADone)
 | 
						|
    | Instr i -> (match i with
 | 
						|
	Assign (v, x) -> 
 | 
						|
	  let vars = (find_vars x) in
 | 
						|
	  Annotate (Util.remove v (union live_at_end vars), [v], [],
 | 
						|
		    0, AInstr i))
 | 
						|
    | Seq (a, b) ->
 | 
						|
	let ab = analyze live_at_end b in
 | 
						|
	let Annotate (live_at_begin_b, defined_b, _, depth_a, _) = ab in
 | 
						|
	let aa = analyze live_at_begin_b a in
 | 
						|
	let Annotate (live_at_begin_a, defined_a, _, depth_b, _) = aa in
 | 
						|
	let defined = List.filter is_temporary (defined_a @ defined_b) in
 | 
						|
	let declarable = diff defined live_at_end in
 | 
						|
	let undeclarable = diff defined declarable 
 | 
						|
	and maxdepth = max depth_a depth_b in
 | 
						|
	Annotate (live_at_begin_a, undeclarable, declarable, 
 | 
						|
		  List.length declarable + maxdepth,
 | 
						|
		  ASeq (aa, ab))
 | 
						|
    | _ -> failwith "really_analyze"
 | 
						|
 | 
						|
  in 
 | 
						|
  let () = Util.info "begin annotate" in
 | 
						|
  let x = linearize schedule in
 | 
						|
 | 
						|
  let x =
 | 
						|
    if (!Magic.schedule_for_pipeline && !Magic.pipeline_latency > 0) then
 | 
						|
      schedule_for_pipeline x 
 | 
						|
    else
 | 
						|
      x
 | 
						|
  in
 | 
						|
 | 
						|
  let x = 
 | 
						|
    if !Magic.reorder_insns then 
 | 
						|
      linearize(anticipate_friends use_same_vars x) 
 | 
						|
    else 
 | 
						|
      x
 | 
						|
  in
 | 
						|
 | 
						|
  (* delay stores to the real and imaginary parts of the same number *)
 | 
						|
  let x = 
 | 
						|
    if !Magic.reorder_stores then 
 | 
						|
      linearize(delay_friends store_to_same_class x) 
 | 
						|
    else
 | 
						|
      x
 | 
						|
  in
 | 
						|
 | 
						|
  (* move loads of the real and imaginary parts of the same number *)
 | 
						|
  let x = 
 | 
						|
    if !Magic.reorder_loads then 
 | 
						|
      linearize(anticipate_friends loads_from_same_class x) 
 | 
						|
    else 
 | 
						|
      x
 | 
						|
  in
 | 
						|
 | 
						|
  let x = collect_buddy_stores list_of_buddy_stores x in
 | 
						|
  let x = analyze [] x in
 | 
						|
  let res = rewrite_declarations true x in
 | 
						|
  let () = Util.info "end annotate" in
 | 
						|
  res
 | 
						|
 | 
						|
let rec dump print (Annotate (_, _, _, _, code)) =
 | 
						|
  dump_code print code
 | 
						|
and dump_code print = function
 | 
						|
  | ADone -> ()
 | 
						|
  | AInstr x -> print ((assignment_to_string x) ^ "\n")
 | 
						|
  | ASeq (a, b) -> dump print a; dump print b
 |