Files
lci/lib/miniImp/reduceRegisters.ml

445 lines
17 KiB
OCaml
Raw Normal View History

open Analysis
module Variable = struct
type t = string
let _pp (ppf: out_channel) (v: t) : unit =
Printf.fprintf ppf "%s" v
let _pplist (ppf: out_channel) (vv: t list) : unit =
List.iter (Printf.fprintf ppf "%s, ") vv
let compare a b =
String.compare a b
end
module RISCCfg = CfgRISC.RISCCfg
module VariableMap = Map.Make(Variable)
let variables_frequency (instr : RISCCfg.elt) : (Variable.t * int) list =
2025-01-15 00:10:44 +01:00
let add_one = (fun x -> match x with None -> Some 1 | Some x -> Some (x + 1))
in
let aux (acc: int VariableMap.t) (instr: RISCCfg.elt) : int VariableMap.t =
match instr with
| Nop ->
acc
| BRegOp (_, r1, r2, r3) ->
VariableMap.update r1.index add_one acc |>
VariableMap.update r2.index add_one |>
VariableMap.update r3.index add_one
| BImmOp (_, r1, _, r3)
| URegOp (_, r1, r3)
| Load (r1, r3)
| Store (r1, r3) ->
VariableMap.update r1.index add_one acc |>
VariableMap.update r3.index add_one
| LoadI (_, r3) ->
VariableMap.update r3.index add_one acc
in
aux VariableMap.empty instr |> VariableMap.to_list
2025-01-15 00:10:44 +01:00
(* computes syntactic frequency of all variables in the code *)
let variables_all_frequency (instructions : RISCCfg.elt list)
: (Variable.t * int) list =
2025-01-11 20:32:11 +01:00
List.fold_left
( fun (acc: int VariableMap.t) (instr: RISCCfg.elt) ->
VariableMap.union
(fun _v x y -> Some (x + y))
acc (variables_frequency instr |> VariableMap.of_list) )
VariableMap.empty instructions |> VariableMap.to_list
let reduce_registers (n: int) (cfg: RISCCfg.t) : RISCCfg.t =
2025-01-15 00:10:44 +01:00
(if n < 4 then (
failwith "ReduceRegisters: number of registers too small"
) else ());
(* we get all the variables with associated frequency (only syntactic use) *)
let all_variables = List.fold_left
(fun acc (_, code) ->
2025-01-15 00:10:44 +01:00
Utility.unique_union_assoc
(fun _n x y -> x + y) acc (variables_all_frequency code))
[]
(Cfg.NodeMap.to_list cfg.content)
in
2025-01-11 20:32:11 +01:00
2025-01-15 00:10:44 +01:00
(* add one to in and out variables *)
let all_variables =
match cfg.input_output_var with
| None -> all_variables
| Some (i, _o) -> (
2025-01-27 01:17:53 +01:00
match List.assoc_opt i all_variables with
| None -> (i, 1) :: all_variables
| Some f -> (i, f+1) :: (List.remove_assoc i all_variables)
)
in
2025-01-11 20:32:11 +01:00
let all_variables =
match cfg.input_output_var with
| None -> all_variables
| Some (_i, o) -> (
2025-01-27 01:17:53 +01:00
match List.assoc_opt o all_variables with
| None -> (o, 1) :: all_variables
| Some f -> (o, f+1) :: (List.remove_assoc o all_variables)
)
in
2025-01-15 00:10:44 +01:00
(* replace each operation with a list of operations that have the new
registers or load from memory *)
let replace_registers
(remappedregisters: Variable.t VariableMap.t)
(memorymap: int VariableMap.t)
(temporaryregisters: Variable.t list)
(code: RISCCfg.elt list)
: RISCCfg.elt list =
let tmpreg1: CfgRISC.RISCSimpleStatements.register =
{index = List.nth temporaryregisters 0} in
let tmpreg2: CfgRISC.RISCSimpleStatements.register =
{index = List.nth temporaryregisters 1} in
let aux (instruction: RISCCfg.elt) : RISCCfg.elt list =
match instruction with
| Nop -> [Nop]
| BRegOp (brop, r1, r2, r3) -> (
match ( VariableMap.find_opt r1.index remappedregisters,
VariableMap.find_opt r2.index remappedregisters,
VariableMap.find_opt r3.index remappedregisters,
VariableMap.find_opt r1.index memorymap,
2025-01-11 20:32:11 +01:00
VariableMap.find_opt r2.index memorymap,
VariableMap.find_opt r3.index memorymap )
with
| Some r1, Some r2, Some r3, _, _, _ ->
[BRegOp (brop, {index = r1}, {index = r2}, {index = r3})]
| Some r1, None, Some r3, _, Some m2, _ ->
[LoadI (m2, tmpreg2);
Load (tmpreg2, tmpreg2);
BRegOp (brop, {index = r1}, tmpreg2, {index = r3})]
| None, Some r2, Some r3, Some m1, _, _ ->
[LoadI (m1, tmpreg1);
Load (tmpreg1, tmpreg1);
BRegOp (brop, tmpreg1, {index = r2}, {index = r3})]
| None, None, Some r3, Some m1, Some m2, _ ->
[LoadI (m1, tmpreg1);
Load (tmpreg1, tmpreg1);
LoadI (m2, tmpreg2);
Load (tmpreg2, tmpreg2);
BRegOp (brop, tmpreg1, tmpreg2, {index = r3})]
2025-01-11 20:32:11 +01:00
| Some r1, Some r2, None, _, _, Some m3 ->
[BRegOp (brop, {index = r1}, {index = r2}, tmpreg2);
LoadI (m3, tmpreg1);
Store (tmpreg2, tmpreg1)]
| Some r1, None, None, _, Some m2, Some m3 ->
[LoadI (m2, tmpreg2);
Load (tmpreg2, tmpreg2);
BRegOp (brop, {index = r1}, tmpreg2, tmpreg2);
LoadI (m3, tmpreg1);
Store (tmpreg2, tmpreg1)]
| None, Some r2, None, Some m1, _, Some m3 ->
[LoadI (m1, tmpreg1);
Load (tmpreg1, tmpreg1);
BRegOp (brop, tmpreg1, {index = r2}, tmpreg2);
LoadI (m3, tmpreg1);
Store (tmpreg2, tmpreg1)]
| None, None, None, Some m1, Some m2, Some m3 ->
[LoadI (m1, tmpreg1);
Load (tmpreg1, tmpreg1);
LoadI (m2, tmpreg2);
Load (tmpreg2, tmpreg2);
BRegOp (brop, tmpreg1, tmpreg2, tmpreg2);
LoadI (m3, tmpreg1);
Store (tmpreg2, tmpreg1)]
2025-01-15 00:10:44 +01:00
| _ -> [BRegOp (brop,
{index = r1.index},
{index = r2.index},
{index = r3.index})]
)
| BImmOp (biop, r1, i, r3) -> (
match ( VariableMap.find_opt r1.index remappedregisters,
VariableMap.find_opt r3.index remappedregisters,
VariableMap.find_opt r1.index memorymap,
VariableMap.find_opt r3.index memorymap )
with
| Some r1, Some r3, _, _ ->
[BImmOp (biop, {index = r1}, i, {index = r3})]
| Some r1, None, _, Some m3 ->
[BImmOp (biop, {index = r1}, i, tmpreg2);
LoadI (m3, tmpreg1);
Store (tmpreg2, tmpreg1)]
| None, Some r3, Some m1, _ ->
[LoadI (m1, tmpreg1);
Load (tmpreg1, tmpreg1);
BImmOp (biop, tmpreg1, i, {index = r3})]
| None, None, Some m1, Some m3 ->
[LoadI (m1, tmpreg1);
Load (tmpreg1, tmpreg1);
BImmOp (biop, tmpreg1, i, tmpreg2);
LoadI (m3, tmpreg1);
Store (tmpreg2, tmpreg1)]
| _ -> failwith ("ReduceRegisters: fail to partition a set, some" ^
" registers have no binding.")
2025-01-27 01:17:53 +01:00
)
| URegOp (urop, r1, r3) ->(
match ( VariableMap.find_opt r1.index remappedregisters,
VariableMap.find_opt r3.index remappedregisters,
VariableMap.find_opt r1.index memorymap,
VariableMap.find_opt r3.index memorymap )
with
| Some r1, Some r3, _, _ ->
[URegOp (urop, {index = r1}, {index = r3})]
| Some r1, None, _, Some m3 ->
[URegOp (urop, {index = r1}, tmpreg2);
LoadI (m3, tmpreg1);
Store (tmpreg2, tmpreg1)]
| None, Some r3, Some m1, _ ->
[LoadI (m1, tmpreg1);
Load (tmpreg1, tmpreg1);
URegOp (urop, tmpreg1, {index = r3})]
| None, None, Some m1, Some m3 ->
[LoadI (m1, tmpreg1);
Load (tmpreg1, tmpreg1);
URegOp (urop, tmpreg1, tmpreg2);
LoadI (m3, tmpreg1);
Store (tmpreg2, tmpreg1)]
| _ -> failwith ("ReduceRegisters: fail to partition a set, some" ^
" registers have no binding.")
)
| Load (r1, r3) -> (
match ( VariableMap.find_opt r1.index remappedregisters,
VariableMap.find_opt r3.index remappedregisters,
VariableMap.find_opt r1.index memorymap,
VariableMap.find_opt r3.index memorymap )
with
| Some r1, Some r3, _, _ ->
[Load ({index = r1}, {index = r3})]
| Some r1, None, _, Some m3 ->
[Load ({index = r1}, tmpreg2);
LoadI (m3, tmpreg1);
Store (tmpreg2, tmpreg1)]
| None, Some r3, Some m1, _ ->
[LoadI (m1, tmpreg1);
Load (tmpreg1, tmpreg1);
Load (tmpreg1, {index = r3})]
| None, None, Some m1, Some m3 ->
[LoadI (m1, tmpreg1);
Load (tmpreg1, tmpreg1);
Load (tmpreg1, tmpreg2);
LoadI (m3, tmpreg1);
Store (tmpreg2, tmpreg1)]
| _ -> failwith ("ReduceRegisters: fail to partition a set, some" ^
" registers have no binding.")
)
| LoadI (i, r3) -> (
(* we want to store an integer in memory immediately (strange, but
unless better heuristic to choose the variables to replace we are
stuck) *)
match ( VariableMap.find_opt r3.index remappedregisters,
VariableMap.find_opt r3.index memorymap )
with
| Some r3, _ ->
[LoadI (i, {index = r3})]
| None, Some m3 ->
[LoadI (i, tmpreg2);
LoadI (m3, tmpreg1);
Store (tmpreg2, tmpreg1)]
| _ -> failwith ("ReduceRegisters: fail to partition a set, some" ^
" registers have no binding.")
)
| Store (r1, r3) -> (
2025-01-15 00:10:44 +01:00
(* we want to maybe store an address in memory (very confusing,
but maybe possible) *)
match ( VariableMap.find_opt r1.index remappedregisters,
VariableMap.find_opt r3.index remappedregisters,
VariableMap.find_opt r1.index memorymap,
VariableMap.find_opt r3.index memorymap )
with
| Some r1, Some r3, _, _ ->
[Store ({index = r1}, {index = r3})]
| Some r1, None, _, Some m3 ->
[Store ({index = r1}, tmpreg2);
LoadI (m3, tmpreg1);
Store (tmpreg2, tmpreg1)]
| None, Some r3, Some m1, _ ->
[LoadI (m1, tmpreg1);
Load (tmpreg1, tmpreg1);
Store (tmpreg1, {index = r3})]
| None, None, Some m1, Some m3 ->
[LoadI (m1, tmpreg1);
Load (tmpreg1, tmpreg1);
Store (tmpreg1, tmpreg2);
LoadI (m3, tmpreg1);
Store (tmpreg2, tmpreg1)]
| _ -> failwith ("ReduceRegisters: fail to partition a set, some" ^
" registers have no binding.")
2025-01-27 01:17:53 +01:00
)
in
2025-01-11 20:32:11 +01:00
List.map aux code |> List.concat
in
2025-01-15 00:10:44 +01:00
(* partition the variables into two sets, most frequent and least frequent
then apply the transformation *)
2025-01-11 20:32:11 +01:00
let aux (cfg: RISCCfg.t) (all_variables: (string * int) list) =
(* we keep the first two variables free for immediate use *)
let most_frequent, least_frequent =
List.sort (fun (_a, fa) (_b, fb) -> Int.compare fb fa) all_variables
|> Utility.take (n-2)
in
2025-01-27 01:17:53 +01:00
let most_frequent, _frequencies = List.split most_frequent in
let least_frequent, _frequencies = List.split least_frequent in
(* we map the most frequent to new registers, so that the first two are
always free *)
let most_frequent_mapping = (* +3 because starts at 0, but we want to start
at 1*)
2025-01-27 01:17:53 +01:00
List.mapi
(fun n v -> (v, (string_of_int (n+3): Variable.t)))
most_frequent
|> VariableMap.of_list
in
(* we map the least to memory *)
let least_frequent_mapping =
List.mapi (fun n v -> (v, (n: int))) least_frequent
|> VariableMap.of_list
in
(* we need to replace both at the same time, because we might have mapped
some registers to already used registers, so a double pass might not
differentiate the two *)
(* special care must be taken for the in and out registers *)
let newcfg = {
cfg with
content = Cfg.NodeMap.map
2025-01-15 00:10:44 +01:00
( fun x ->
replace_registers
2025-01-15 00:10:44 +01:00
most_frequent_mapping
least_frequent_mapping
["1"; "2"]
x
) cfg.content}
in
if newcfg.input_output_var = None
2025-01-27 01:17:53 +01:00
then newcfg (* if no input or output variables we ignore *)
else
let i, o = Option.get newcfg.input_output_var in
2025-01-27 01:17:53 +01:00
match (VariableMap.find_opt i most_frequent_mapping,
VariableMap.find_opt o most_frequent_mapping,
VariableMap.find_opt i least_frequent_mapping,
VariableMap.find_opt o least_frequent_mapping,
newcfg.initial,
newcfg.terminal )
with (*we check if in and out are simply remapped or are put in memory*)
| Some i, Some o, _, _, _, _ ->
{ newcfg with input_output_var = Some (i, o) }
2025-01-27 01:17:53 +01:00
| Some i, None, _, Some _, _, None ->
(* we check for the terminal node, if not present we are very confused
and dont modify the out variable *)
{ newcfg with input_output_var = Some (i, o)}
2025-01-27 01:17:53 +01:00
| Some i, None, _, Some mo, _, Some n ->
(* since the output simbol is in memory we need to first retrive it
and then put the result in a temporary register *)
let terminal_content = (
2025-01-27 01:17:53 +01:00
match Cfg.NodeMap.find_opt n newcfg.content with
| None -> []
| Some x -> x
) @ [LoadI (mo, {index = "2"});
Load ({index = "2"}, {index = "2"})]
in
let content =
Cfg.NodeMap.add n terminal_content newcfg.content
2025-01-27 01:17:53 +01:00
in
{ newcfg with
input_output_var = Some (i, "2");
2025-01-27 01:17:53 +01:00
content = content
}
| None, Some o, Some _, _, _, None ->
{ newcfg with input_output_var = Some (i, o) }
2025-01-27 01:17:53 +01:00
| None, Some o, Some mi, _, _, Some n -> (
(* the input simbol should be stored in memory *)
let initialcontent =
[(LoadI (mi, {index = "2"}) : RISCCfg.elt);
Store ({index = "1"}, {index = "2"})] @ (
match Cfg.NodeMap.find_opt n newcfg.content with
| None -> []
| Some x -> x
)
in
let content = Cfg.NodeMap.add n initialcontent newcfg.content in
{ newcfg with
input_output_var = Some ("1", o);
2025-01-27 01:17:53 +01:00
content = content
}
)
| None, None, Some _, Some _, None, None ->
{ newcfg with input_output_var = Some (i, o) }
2025-01-27 01:17:53 +01:00
| None, None, Some _, Some mo, None, Some n ->
(* both simbols should be in memory *)
let terminalcontent = (
match Cfg.NodeMap.find_opt n newcfg.content with
| None -> []
| Some x -> x
) @ [LoadI (mo, {index = "2"});
Load ({index = "2"}, {index = "2"})]
in
let content =
Cfg.NodeMap.add n terminalcontent newcfg.content
in
{ newcfg with
input_output_var = Some (i, "2");
2025-01-27 01:17:53 +01:00
content = content
}
| None, None, Some mi, Some _, Some n, None ->
(* both simbols should be in memory *)
let initialcontent =
[(LoadI (mi, {index = "2"}) : RISCCfg.elt);
Store ({index = "1"}, {index = "2"})] @ (
match Cfg.NodeMap.find_opt n newcfg.content with
| None -> []
| Some x -> x
)
2025-01-27 01:17:53 +01:00
in
let content = Cfg.NodeMap.add n initialcontent newcfg.content in
{ newcfg with
input_output_var = Some ("1", o);
2025-01-27 01:17:53 +01:00
content = content
}
| None, None, Some mi, Some mo, Some ni, Some no ->
(* both simbols should be in memory *)
let initialcontent =
[(LoadI (mi, {index = "2"}) : RISCCfg.elt);
Store ({index = "1"}, {index = "2"})] @ (
match Cfg.NodeMap.find_opt ni newcfg.content with
| None -> []
| Some x -> x
)
2025-01-27 01:17:53 +01:00
in
let terminalcontent = (
match Cfg.NodeMap.find_opt no newcfg.content with
| None -> []
| Some x -> x
) @ [LoadI (mo, {index = "2"});
Load ({index = "2"}, {index = "2"})]
in
let content =
Cfg.NodeMap.add ni initialcontent newcfg.content
in
let content =
Cfg.NodeMap.add no terminalcontent content
in
{ newcfg with
input_output_var = Some ("1", "2");
2025-01-27 01:17:53 +01:00
content = content
}
| _ -> failwith ("ReduceRegisters: fail to partition a set, some" ^
" registers have no binding.")
in
2025-01-11 20:32:11 +01:00
( if List.length all_variables <= n
then cfg
else aux cfg all_variables )