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 |