Commit 75a2885f authored by Cyril SIX's avatar Cyril SIX
Browse files

Merge remote-tracking branch 'origin/kvx-work' into merge_master_8.13.1

parents 22504b61 c44fc24e
......@@ -315,6 +315,7 @@ compcert.ini: Makefile.config
echo "linker_options=$(CLINKER_OPTIONS)";\
echo "arch=$(ARCH)"; \
echo "model=$(MODEL)"; \
echo "os=$(OS)"; \
echo "abi=$(ABI)"; \
echo "endianness=$(ENDIANNESS)"; \
echo "system=$(SYSTEM)"; \
......
......@@ -52,7 +52,7 @@ INCLUDES=$(patsubst %,-I %, $(DIRS))
# Control of warnings:
# WARNINGS=-w +a-4-9-27-42 -strict-sequence -safe-string -warn-error +a #Deprication returns with ocaml 4.03
WARNINGS=-w +a-4-9-27-42
WARNINGS=-w +a-4-9-27-42-70-69 # 70,69 for OCaml 4.13
extraction/%.cmx: WARNINGS +=-w -20-27-32..34-39-41-44..45-60-67
extraction/%.cmo: WARNINGS +=-w -20-27-32..34-39-41-44..45-60-67
......
......@@ -158,7 +158,7 @@ Definition destroyed_by_builtin (ef: external_function): list mreg :=
match ef with
| EF_memcpy sz al => R15 :: R17 :: R29 :: nil
| EF_inline_asm txt sg clob => destroyed_by_clobber clob
| EF_profiling _ _ => R15 :: R17 :: nil
| EF_profiling _ _ => R15 :: R17 :: R29 :: nil
| _ => nil
end.
......
(* *********************************************************************)
(* *)
(* The Compcert verified compiler *)
(* *)
(* Xavier Leroy, INRIA Paris-Rocquencourt *)
(* *)
(* Copyright Institut National de Recherche en Informatique et en *)
(* Automatique. All rights reserved. This file is distributed *)
(* under the terms of the INRIA Non-Commercial License Agreement. *)
(* *)
(* *********************************************************************)
(** Auxiliary functions on machine registers *)
val is_scratch_register: string -> bool
val class_of_type: AST.typ -> int
......@@ -401,9 +401,8 @@ let pair_rep_inv insta =
* for one type of inst. Lists contains integers which
* are the indices of insts in the main array "insta". *)
init_symb_mem ();
for i = Array.length insta - 1 downto 1 do
for i = Array.length insta - 1 downto 0 do
let h0 = insta.(i) in
let h1 = insta.(i - 1) in
(* Here we need to update every symbolic memory according to the matched inst *)
update_pot_rep_basic h0 insta (Ldr P32) true;
update_pot_rep_basic h0 insta (Ldr P64) true;
......@@ -413,9 +412,9 @@ let pair_rep_inv insta =
update_pot_rep_basic h0 insta (Str P64) true;
update_pot_rep_basic h0 insta (Str P32f) true;
update_pot_rep_basic h0 insta (Str P64f) true;
match (h0, h1) with
match h0 with
(* Non-consecutive ldr *)
| PLoad (PLd_rd_a (ldi, rd1, ADimm (b1, n1))), _ ->
| PLoad (PLd_rd_a (ldi, rd1, ADimm (b1, n1))) ->
if is_compat_load ldi then (
(* Search a previous compatible load *)
let ld_t = get_load_pht ldi in
......@@ -445,7 +444,7 @@ let pair_rep_inv insta =
(trans_ldi ldi, rd1, r, chunk_load ldi, c, ADimm (b, n1)))));
Hashtbl.replace symb_mem ld_t pot_rep)
(* Non-consecutive str *)
| PStore (PSt_rs_a (sti, rd1, ADimm (b1, n1))), _ ->
| PStore (PSt_rs_a (sti, rd1, ADimm (b1, n1))) ->
if is_compat_store sti then (
(* Search a previous compatible store *)
let st_t = get_store_pht sti in
......@@ -469,7 +468,7 @@ let pair_rep_inv insta =
(trans_sti sti, rd1, r, chunk_store sti, c, ADimm (b, n1))));
Hashtbl.replace symb_mem st_t pot_rep
(* Any other inst *))
| i, _ -> (
| i -> (
(* Clear list of candidates if there is a non supported store *)
match i with PStore _ -> reset_str_symb_mem () | _ -> ())
done
......
......@@ -349,8 +349,8 @@ module Target(System: SYSTEM): TARGET =
fprintf oc "%s:\n" lbl;
fprintf oc " ldaxr x17, [x15]\n";
fprintf oc " add x17, x17, 1\n";
fprintf oc " stlxr w17, x17, [x15]\n";
fprintf oc " cbnz w17, %s\n" lbl;
fprintf oc " stlxr w29, x17, [x15]\n";
fprintf oc " cbnz w29, %s\n" lbl;
fprintf oc "%s end profiling %a %d\n" comment
Profilingaux.pp_id id kind;;
......
......@@ -24,6 +24,76 @@ open Maps
open Camlcoq
open DebugPrint
let stats_oc = ref None
let set_stats_oc () =
try
let name = Sys.getenv "COMPCERT_PREDICT_STATS" in
let oc = open_out_gen [Open_append; Open_creat; Open_text] 0o666 name in
stats_oc := Some oc
with Not_found -> ()
(* number of total CBs *)
let stats_nb_total = ref 0
(* we predicted the same thing as the profiling *)
let stats_nb_correct_predicts = ref 0
(* we predicted something (say Some true), but the profiling predicted the opposite (say Some false) *)
let stats_nb_mispredicts = ref 0
(* we did not predict anything (None) even though the profiling did predict something *)
let stats_nb_missed_opportunities = ref 0
(* we predicted something (say Some true) but the profiling preferred not to predict anything (None) *)
let stats_nb_overpredict = ref 0
(* heuristic specific counters *)
let wrong_opcode = ref 0
let wrong_return = ref 0
let wrong_loop2 = ref 0
let wrong_loop = ref 0
let wrong_call = ref 0
let right_opcode = ref 0
let right_return = ref 0
let right_loop2 = ref 0
let right_loop = ref 0
let right_call = ref 0
let reset_stats () = begin
stats_nb_total := 0;
stats_nb_correct_predicts := 0;
stats_nb_mispredicts := 0;
stats_nb_missed_opportunities := 0;
stats_nb_overpredict := 0;
wrong_opcode := 0;
wrong_return := 0;
wrong_loop2 := 0;
wrong_loop := 0;
wrong_call := 0;
right_opcode := 0;
right_return := 0;
right_loop2 := 0;
right_loop := 0;
right_call := 0;
end
let incr theref = theref := !theref + 1
let has_some o = match o with Some _ -> true | None -> false
let stats_oc_recording () = has_some !stats_oc
let write_stats_oc () =
match !stats_oc with
| None -> ()
| Some oc -> begin
Printf.fprintf oc "%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n" !stats_nb_total
!stats_nb_correct_predicts !stats_nb_mispredicts !stats_nb_missed_opportunities
!stats_nb_overpredict
!wrong_opcode !wrong_return !wrong_loop2 !wrong_loop !wrong_call
!right_opcode !right_return !right_loop2 !right_loop !right_call
;
close_out oc
end
let get_loop_headers = LICMaux.get_loop_headers
let get_some = LICMaux.get_some
let rtl_successors = LICMaux.rtl_successors
......@@ -270,120 +340,66 @@ let get_inner_loops f code is_loop_header =
) (PTree.elements loopmap)
end
let get_loop_bodies code entrypoint =
let predecessors = get_predecessors_rtl code in
(* Algorithm from Muchnik, Compiler Design & Implementation, Figure 7.21 page 192 *)
let natural_loop n m =
debug "Natural Loop from %d to %d\n" (P.to_int n) (P.to_int m);
let in_body = ref (PTree.map (fun n b -> false) code) in
let body = ref [] in
let add_to_body n = begin
in_body := PTree.set n true !in_body;
body := n :: !body
end
in let rec process_node p =
debug " Processing node %d\n" (P.to_int p);
List.iter (fun pred ->
debug " Looking at predecessor of %d: %d\n" (P.to_int p) (P.to_int pred);
let is_in_body = get_some @@ PTree.get pred !in_body in
if (not @@ is_in_body) then begin
debug " --> adding to body\n";
add_to_body pred;
process_node pred
end
) (get_some @@ PTree.get p predecessors)
in begin
add_to_body m;
add_to_body n;
(if (m != n) then process_node m);
!body
end
in let option_natural_loop n = function
| None -> None
| Some m -> Some (natural_loop n m)
in PTree.map option_natural_loop (LICMaux.get_loop_backedges code entrypoint)
(* Returns a PTree of either None or Some b where b determines the node following the loop, for a cb instruction *)
(* It uses the fact that loops in CompCert are done by a branch (backedge) instruction followed by a cb *)
(* Returns a PTree of either None or Some b where b determines the node in the loop body, for a cb instruction *)
let get_loop_info f is_loop_header bfs_order code =
debug "GET LOOP INFO\n";
debug "==================================\n";
let loop_info = ref (PTree.map (fun n i -> None) code) in
let mark_path n lbody =
let cb_info = ref PTree.empty in
let visited = ref (PTree.map (fun n i -> false) code) in
(* Returns true if there is a path from src to dest (not involving jumptables) *)
(* Mark nodes as visited along the way *)
let explore src dest =
debug "Trying to dive a path from %d to %d\n" (P.to_int src) (P.to_int dest);
(* Memoizing the results to avoid exponential blow-up *)
let memory = ref PTree.empty in
let rec explore_rec src =
debug "explore_rec %d vs %d... " (P.to_int src) (P.to_int dest);
if (P.to_int src) == (P.to_int dest) then (debug "FOUND\n"; true)
else if (get_some @@ PTree.get src !visited) then (debug "VISITED... :( \n"; false)
(* if we went out of the innermost loop *)
else if (not @@ List.mem src lbody) then (debug "Out of innermost...\n"; false)
else begin
let inst = get_some @@ PTree.get src code in
visited := PTree.set src true !visited;
match rtl_successors inst with
| [] -> false
| [s] -> explore_wrap s
| [s1; s2] -> let snapshot_visited = ref !visited in begin
debug "\t\tSplit at %d: either %d or %d\n" (P.to_int src) (P.to_int s1) (P.to_int s2);
(* Remembering that we tried the ifso node *)
cb_info := PTree.set src true !cb_info;
match explore_wrap s1 with
| true -> (
visited := !snapshot_visited;
match explore_wrap s2 with
| true -> begin
(* Both paths lead to a loop: we cannot predict the CB
* (but the explore still succeeds) *)
cb_info := PTree.remove src !cb_info;
true
end
| false -> true (* nothing to do, the explore succeeded *)
)
| false -> begin
cb_info := PTree.set src false !cb_info;
match explore_wrap s2 with
| true -> true
| false -> (cb_info := PTree.remove src !cb_info; false)
end
end
| _ -> false
let mark_body body =
List.iter (fun n ->
match get_some @@ PTree.get n code with
| Icond (_, _, ifso, ifnot, _) -> begin
match PTree.get n !loop_info with
| None -> ()
| Some _ ->
let b1 = List.mem ifso body in
let b2 = List.mem ifnot body in
if (b1 && b2) then ()
else if (b1 || b2) then begin
if b1 then loop_info := PTree.set n (Some true) !loop_info
else if b2 then loop_info := PTree.set n (Some false) !loop_info
end
end
and explore_wrap src = begin
match PTree.get src !memory with
| Some b -> b
| None ->
let result = explore_rec src in
(memory := PTree.set src result !memory; result)
end in explore_wrap src
(* Goes forward until a CB is encountered
* Returns None if no CB was found, or Some the_cb_node
* Marks nodes as visited along the way *)
in let rec advance_to_cb src =
if (get_some @@ PTree.get src !visited) then None
else begin
visited := PTree.set src true !visited;
match get_some @@ PTree.get src code with
| Inop s | Iop (_, _, _, s) | Iload (_,_,_,_,_,s) | Istore (_,_,_,_,s) | Icall (_,_,_,_,s)
| Ibuiltin (_,_,_,s) -> advance_to_cb s
| Icond _ -> Some src
| Ijumptable _ | Itailcall _ | Ireturn _ -> None
end
in begin
debug "Attempting to find natural loop from HEAD %d..\n" (P.to_int n);
match advance_to_cb n with
| None -> (debug "\tNo CB found\n")
| Some s -> ( debug "\tFound a CB! %d\n" (P.to_int s);
match get_some @@ PTree.get s !loop_info with
| None | Some _ -> begin
match get_some @@ PTree.get s code with
| Icond (_, _, n1, n2, _) -> (
let b1 = explore n1 n in
let b2 = explore n2 n in
if (b1 && b2) then
debug "\tBoth paths lead back to the head: NONE\n"
else if (b1 || b2) then begin
if b1 then begin
debug "\tTrue path leads to the head: TRUE\n";
loop_info := PTree.set s (Some true) !loop_info;
end else if b2 then begin
debug "\tFalse path leads to the head: FALSE\n";
loop_info := PTree.set s (Some false) !loop_info
end;
debug "\tSetting other CBs encountered..\n";
List.iter (fun (cb, dir) ->
debug "\t\t%d is %B\n" (P.to_int cb) dir;
loop_info := PTree.set cb (Some dir) !loop_info
) (PTree.elements !cb_info)
end else
debug "\tNo path leads back to the head: NONE\n"
)
| _ -> failwith "\tNot an Icond\n"
end
(* | Some _ -> ( debug "already loop info there\n" ) FIXME - we don't know yet whether a branch to a loop head is a backedge or not *)
)
end
in let iloops = get_inner_loops f code is_loop_header in
begin
List.iter (fun il -> mark_path il.head il.body) iloops;
(* List.iter mark_path @@ List.filter (fun n -> get_some @@ PTree.get n is_loop_header) bfs_order; *)
debug "==================================\n";
!loop_info
end
| _ -> ()
) body
in let bodymap = get_loop_bodies code f.fn_entrypoint in
List.iter (fun (_,obody) ->
match obody with
| None -> ()
| Some body -> mark_body body
) (PTree.elements bodymap);
!loop_info
(* Remark - compared to the original Branch Prediction for Free paper, we don't use the store heuristic *)
let get_directions f code entrypoint = begin
......@@ -397,28 +413,59 @@ let get_directions f code entrypoint = begin
(* debug "\n"; *)
List.iter (fun n ->
match (get_some @@ PTree.get n code) with
| Icond (cond, lr, ifso, ifnot, pred) ->
(match pred with Some _ -> debug "RTL node %d already has prediction information\n" (P.to_int n)
| None ->
(* debug "Analyzing %d.." (P.to_int n); *)
let heuristics = [ do_opcode_heuristic;
do_return_heuristic; do_loop2_heuristic loop_info n; do_loop_heuristic; do_call_heuristic;
(* do_store_heuristic *) ] in
let preferred = ref None in
begin
debug "Deciding condition for RTL node %d\n" (P.to_int n);
List.iter (fun do_heur ->
match !preferred with
| None -> preferred := do_heur code cond ifso ifnot is_loop_header
| Some _ -> ()
) heuristics;
directions := PTree.set n !preferred !directions;
(match !preferred with | Some false -> debug "\tFALLTHROUGH\n"
| Some true -> debug "\tBRANCH\n"
| None -> debug "\tUNSURE\n");
debug "---------------------------------------\n"
end
)
| Icond (cond, lr, ifso, ifnot, pred) -> begin
if stats_oc_recording () || not @@ has_some pred then
(* debug "Analyzing %d.." (P.to_int n); *)
let heuristics = [ do_opcode_heuristic;
do_return_heuristic; do_loop2_heuristic loop_info n; do_loop_heuristic; do_call_heuristic;
(* do_store_heuristic *) ] in
let preferred = ref None in
let current_heuristic = ref 0 in
begin
debug "Deciding condition for RTL node %d\n" (P.to_int n);
List.iter (fun do_heur ->
match !preferred with
| None -> begin
preferred := do_heur code cond ifso ifnot is_loop_header;
if stats_oc_recording () then begin
(* Getting stats about mispredictions from each heuristic *)
(match !preferred, pred with
| Some false, Some true
| Some true, Some false
(* | Some _, None *) (* Uncomment for overpredicts *)
-> begin
match !current_heuristic with
| 0 -> incr wrong_opcode
| 1 -> incr wrong_return
| 2 -> incr wrong_loop2
| 3 -> incr wrong_loop
| 4 -> incr wrong_call
| _ -> failwith "Shouldn't happen"
end
| Some false, Some false
| Some true, Some true -> begin
match !current_heuristic with
| 0 -> incr right_opcode
| 1 -> incr right_return
| 2 -> incr right_loop2
| 3 -> incr right_loop
| 4 -> incr right_call
| _ -> failwith "Shouldn't happen"
end
| _ -> ()
);
incr current_heuristic
end
end
| Some _ -> ()
) heuristics;
directions := PTree.set n !preferred !directions;
(match !preferred with | Some false -> debug "\tFALLTHROUGH\n"
| Some true -> debug "\tBRANCH\n"
| None -> debug "\tUNSURE\n");
debug "---------------------------------------\n"
end
end
| _ -> ()
) bfs_order;
!directions
......@@ -426,11 +473,28 @@ let get_directions f code entrypoint = begin
end
let update_direction direction = function
| Icond (cond, lr, n, n', pred) ->
| Icond (cond, lr, n, n', pred) -> begin
(* Counting stats from profiling *)
if stats_oc_recording () then begin
incr stats_nb_total;
match pred, direction with
| None, None -> incr stats_nb_correct_predicts
| None, Some _ -> incr stats_nb_overpredict
| Some _, None -> incr stats_nb_missed_opportunities
| Some false, Some false -> incr stats_nb_correct_predicts
| Some false, Some true -> incr stats_nb_mispredicts
| Some true, Some false -> incr stats_nb_mispredicts
| Some true, Some true -> incr stats_nb_correct_predicts
end;
(* only update if there is no prior existing branch prediction *)
(match pred with
| None -> Icond (cond, lr, n, n', direction)
| Some _ -> Icond (cond, lr, n, n', pred) )
| Some _ -> begin
Icond (cond, lr, n, n', pred)
end
)
end
| i -> i
(* Uses branch prediction to write prediction annotations in Icond *)
......@@ -1026,15 +1090,20 @@ let static_predict f =
let entrypoint = f.fn_entrypoint in
let code = f.fn_code in
let revmap = make_identity_ptree code in
let code =
if !Clflags.option_fpredict then
update_directions f code entrypoint
else code in
let code =
if !Clflags.option_fpredict then
invert_iconds code
else code in
((code, entrypoint), revmap)
begin
reset_stats ();
set_stats_oc ();
let code =
if !Clflags.option_fpredict then
update_directions f code entrypoint
else code in
write_stats_oc ();
let code =
if !Clflags.option_fpredict then
invert_iconds code
else code in
((code, entrypoint), revmap)
end
let unroll_single f =
let entrypoint = f.fn_entrypoint in
......
......@@ -41,24 +41,25 @@ let rtl_successors = function
*
* If we come accross an edge to a Processed node, it's a loop!
*)
let get_loop_headers code entrypoint = begin
debug "get_loop_headers\n";
let get_loop_backedges code entrypoint = begin
debug "get_loop_backedges\n";
let visited = ref (PTree.map (fun n i -> Unvisited) code)
and is_loop_header = ref (PTree.map (fun n i -> false) code)
in let rec dfs_visit code = function
and loop_backedge = ref (PTree.map (fun n i -> None) code)
in let rec dfs_visit code origin = function
| [] -> ()
| node :: ln ->
debug "ENTERING node %d, REM are %a\n" (P.to_int node) print_intlist ln;
match (get_some @@ PTree.get node !visited) with
| Visited -> begin
debug "\tNode %d is already Visited, skipping\n" (P.to_int node);
dfs_visit code ln
dfs_visit code origin ln
end
| Processed -> begin
debug "Node %d is a loop header\n" (P.to_int node);
is_loop_header := PTree.set node true !is_loop_header;
debug "The backedge is from %d\n" (P.to_int @@ get_some origin);
loop_backedge := PTree.set node origin !loop_backedge;
visited := PTree.set node Visited !visited;
dfs_visit code ln
dfs_visit code origin ln
end
| Unvisited -> begin
visited := PTree.set node Processed !visited;
......@@ -67,19 +68,26 @@ let get_loop_headers code entrypoint = begin
| None -> failwith "No such node"
| Some i -> let next_visits = rtl_successors i in begin
debug "About to visit: %a\n" print_intlist next_visits;
dfs_visit code next_visits
dfs_visit code (Some node) next_visits
end);
debug "Node %d is Visited!\n" (P.to_int node);
visited := PTree.set node Visited !visited;
dfs_visit code ln
dfs_visit code origin ln
end
in begin
dfs_visit code [entrypoint];
debug "LOOP HEADERS: %a\n" print_ptree_bool !is_loop_header;
!is_loop_header
dfs_visit code None [entrypoint];
debug "LOOP BACKEDGES: %a\n" print_ptree_opint !loop_backedge;
!loop_backedge
end
end
let get_loop_headers code entrypoint =
let backedges = get_loop_backedges code entrypoint in
PTree.map (fun _ ob ->
match ob with
| None -> false
| Some _ -> true
) backedges
module Dominator =
struct
......
......@@ -126,400 +126,64 @@ let enumerate_aux_flat f reach =
* This is a slight alteration to the above heuristic, ensuring that any
* superblock will be contiguous in memory, while still following the original
* heuristic
*
* Slight change: instead of taking the minimum pc of the superblock, we just take
* the pc of the first block.
* (experimentally this leads to slightly better performance..)
*)
let get_some = function
| None -> failwith "Did not get some"
| Some thing -> thing
exception EmptyList
let rec last_element = function
| [] -> raise EmptyList
| e :: [] -> e
| e' :: e :: l -> last_element (e::l)
let print_plist l =
let rec f = function
| [] -> ()
| n :: l -> Printf.printf "%d, " (P.to_int n); f l
in begin
if !debug_flag then begin
Printf.printf "[";
f l;