Files
lci/lib/miniImp/reduceRegisters.ml

424 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 =
let add_one = (fun x -> match x with None -> Some 1 | Some x -> Some (x + 1)) in
2025-01-11 20:32:11 +01:00
let helper (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
helper VariableMap.empty instr |> VariableMap.to_list
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 reduceregisters (n: int) (cfg: RISCCfg.t) : RISCCfg.t =
(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-11 20:32:11 +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
let all_variables =
match cfg.inputOutputVar with
| None -> all_variables
| Some (i, _o) -> (
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.inputOutputVar with
| None -> all_variables
| Some (_i, o) -> (
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
let replaceregisters
(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)]
| _ -> [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.")
)
| 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) -> (
(* we want to maybe store an address in memory (very confusing, don't
think can happen) *)
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.")
)
in
2025-01-11 20:32:11 +01:00
List.map aux code |> List.concat
in
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
let most_frequent = fst (List.split most_frequent) in
let least_frequent = fst (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*)
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
(fun x -> replaceregisters most_frequent_mapping least_frequent_mapping ["1"; "2"] x)
cfg.content}
in
match newcfg.inputOutputVar with
| None -> newcfg (* if no input or output variables we ignore *)
| Some (i, o) -> (
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 )
with (*we check if in and out are simply remapped or are put in memory*)
| Some i, Some o, _, _ ->
{ newcfg with inputOutputVar = Some (i, o) }
| Some i, None, _, Some mo -> ( (* since the output simbol is in memory
we need to first retrive it and then
put the result in a temporary
register *)
match newcfg.terminal with (* we check for the terminal node, if not
present we are very confused and dont
modify the out variable *)
| None -> { newcfg with inputOutputVar = Some (i, o)}
| Some n -> (
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
inputOutputVar = Some (i, "2");
content = content
}
)
)
| None, Some o, Some mi, _ -> ( (* the input simbol should be stored in
memory *)
match newcfg.initial with
| None -> { newcfg with inputOutputVar = Some (i, o) }
| Some n -> (
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
inputOutputVar = Some ("1", o);
content = content
}
)
)
| None, None, Some mi, Some mo -> ( (* both simbols should be in
memory *)
match newcfg.initial, newcfg.terminal with
| None, None -> { newcfg with inputOutputVar = Some (i, o) }
| None, Some n -> (
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
inputOutputVar = Some (i, "2");
content = content
}
)
| Some n, None -> (
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
inputOutputVar = Some ("1", o);
content = content
}
)
| Some ni, Some no -> (
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
)
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
inputOutputVar = Some ("1", "2");
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 )