From 4e9f08347bb4cae6f35dc2e1ddd578e3c42c54f4 Mon Sep 17 00:00:00 2001 From: elvis Date: Thu, 21 Nov 2024 18:30:19 +0100 Subject: [PATCH 01/29] Changed from type to module for cfg and moved to separate lib --- bin/main.ml | 4 +- dune-project | 4 + lib/cfg/Cfg.ml | 199 ++++++++++++++++++++++++++ lib/cfg/Cfg.mli | 38 +++++ lib/cfg/dune | 5 + lib/miniImp/Cfg.ml | 316 ----------------------------------------- lib/miniImp/Cfg.mli | 40 ------ lib/miniImp/CfgImp.ml | 182 ++++++++++++++++++++++++ lib/miniImp/CfgImp.mli | 34 +++++ lib/miniImp/dune | 4 +- 10 files changed, 466 insertions(+), 360 deletions(-) create mode 100644 lib/cfg/Cfg.ml create mode 100644 lib/cfg/Cfg.mli create mode 100644 lib/cfg/dune delete mode 100644 lib/miniImp/Cfg.ml delete mode 100644 lib/miniImp/Cfg.mli create mode 100644 lib/miniImp/CfgImp.ml create mode 100644 lib/miniImp/CfgImp.mli diff --git a/bin/main.ml b/bin/main.ml index 9d63d55..832d583 100644 --- a/bin/main.ml +++ b/bin/main.ml @@ -1,5 +1,5 @@ open MiniImp -open MiniImp.Cfg +open MiniImp.CfgImp let () = let program = "def main with input x output y as @@ -17,4 +17,4 @@ let () = let converted = convert p in - Printf.printf "%a" Cfg.pp converted + Printf.printf "%a" SSCfg.pp converted diff --git a/dune-project b/dune-project index 900aba8..bbe1b2e 100644 --- a/dune-project +++ b/dune-project @@ -10,6 +10,10 @@ (name utility) (depends ocaml dune)) +(package + (name cfg) + (depends ocaml dune)) + (package (name miniImp) (depends ocaml dune utility)) diff --git a/lib/cfg/Cfg.ml b/lib/cfg/Cfg.ml new file mode 100644 index 0000000..b0b61e2 --- /dev/null +++ b/lib/cfg/Cfg.ml @@ -0,0 +1,199 @@ +module type PrintableType = sig + type t + val pp : out_channel -> t -> unit + val pplist : out_channel -> t list -> unit +end + +let globalIdNode = ref 0; + +module Node = struct + type t = { + id: int + } + let compare a b = compare a.id b.id + + let create () = + globalIdNode := !globalIdNode + 1; + {id = !globalIdNode} +end +;; + +module NodeMap = Map.Make(Node) +module NodeSet = Set.Make(Node) + +module type C = sig + type elt + type t = { + empty: bool; + nodes: NodeSet.t; + edges: (Node.t * (Node.t option)) NodeMap.t; + reverseEdges: (Node.t list) NodeMap.t; + inputVal: elt option; + outputVal: elt option; + initial: Node.t option; + terminal: Node.t option; + content: elt list NodeMap.t + } + + val create : unit -> t + val merge : t -> t -> Node.t -> Node.t -> t + val concat : t -> t -> t + val addToLastNode : elt -> t -> t + + val pp : out_channel -> t -> unit +end + +module Make(M: PrintableType) = struct + type elt = M.t + type t = { + empty: bool; + nodes: NodeSet.t; + edges: (Node.t * (Node.t option)) NodeMap.t; + reverseEdges: (Node.t list) NodeMap.t; + inputVal: elt option; + outputVal: elt option; + initial: Node.t option; + terminal: Node.t option; + content: elt list NodeMap.t + } + + let create () : t = + { empty = true; + nodes = NodeSet.empty; + edges = NodeMap.empty; + reverseEdges = NodeMap.empty; + inputVal = None; + outputVal = None; + initial = None; + terminal = None; + content = NodeMap.empty } + + let merge (cfg1: t) (cfg2: t) (entryNode: Node.t) (exitNode: Node.t) : t = + match (cfg1.empty, cfg2.empty) with + true, _ -> cfg2 + | _, true -> cfg1 + | false, false -> + let cfg1initial = Option.get cfg1.initial in + let cfg2initial = Option.get cfg2.initial in + let cfg1terminal = Option.get cfg1.terminal in + let cfg2terminal = Option.get cfg2.terminal in + { empty = false; + nodes = NodeSet.union cfg1.nodes cfg2.nodes |> + NodeSet.add entryNode |> + NodeSet.add exitNode; + edges = NodeMap.union (fun _ -> failwith "Failed merging edges of cfg.") + cfg1.edges cfg2.edges |> + NodeMap.add entryNode (cfg1initial, Some cfg2initial) |> + NodeMap.add cfg1terminal (exitNode, None) |> + NodeMap.add cfg2terminal (exitNode, None); + reverseEdges = NodeMap.union (fun _ -> failwith "Failed merging edges of cfg.") + cfg1.reverseEdges cfg2.reverseEdges |> + NodeMap.add_to_list cfg1initial entryNode |> + NodeMap.add_to_list cfg2initial entryNode |> + NodeMap.add_to_list exitNode cfg1terminal |> + NodeMap.add_to_list exitNode cfg2terminal; + inputVal = cfg1.inputVal; + outputVal = cfg1.outputVal; + initial = Some entryNode; + terminal = Some exitNode; + content = NodeMap.union (fun _ -> failwith "Failed merging code of cfg.") + cfg1.content cfg2.content + } + + let concat (cfg1: t) (cfg2: t) : t = + match (cfg1.empty, cfg2.empty) with + true, _ -> cfg2 + | _, true -> cfg1 + | false, false -> + let cfg1initial = Option.get cfg1.initial in + let cfg2initial = Option.get cfg2.initial in + let cfg1terminal = Option.get cfg1.terminal in + let cfg2terminal = Option.get cfg2.terminal in + { empty = false; + nodes = NodeSet.union cfg1.nodes cfg2.nodes; + edges = NodeMap.union (fun _ -> failwith "Failed merging edges of cfg.") + cfg1.edges cfg2.edges |> + NodeMap.add cfg1terminal (cfg2initial, None); + reverseEdges = NodeMap.union (fun _ -> failwith "Failed merging edges of cfg.") + cfg1.reverseEdges cfg2.reverseEdges |> + NodeMap.add_to_list cfg2initial cfg1terminal; + inputVal = cfg1.inputVal; + outputVal = cfg1.outputVal; + initial = Some cfg1initial; + terminal = Some cfg2terminal; + content = NodeMap.union (fun _ -> failwith "Failed merging code of cfg.") + cfg1.content cfg2.content + } + + let addToLastNode (newcontent: elt) (cfg: t) : t = + match cfg.empty with + | true -> let newnode = Node.create () in + { empty = false; + nodes = NodeSet.singleton newnode; + edges = NodeMap.empty; + reverseEdges = NodeMap.empty; + inputVal = None; + outputVal = None; + initial = Some newnode; + terminal = Some newnode; + content = NodeMap.singleton newnode [newcontent] + } + | false -> + let prevcfgterminal = Option.get cfg.terminal in + { cfg with + content = (NodeMap.add_to_list + prevcfgterminal + newcontent + cfg.content) } + + let pp (ppf) (c: t) : unit = + Printf.fprintf ppf "Nodes' ids: "; + List.iter (fun (x : Node.t) -> Printf.fprintf ppf "%d " x.id) (NodeSet.to_list c.nodes); + Printf.fprintf ppf "\n"; + + Printf.fprintf ppf "Nodes' edges:\n"; + List.iter (fun ((n, (a, b)) : (Node.t * (Node.t * Node.t option))) : unit -> + match b with None -> Printf.fprintf ppf "\t%d -> %d\n" n.id a.id + | Some b -> Printf.fprintf ppf "\t%d -> %d, %d\n" n.id a.id b.id + ) (NodeMap.to_list c.edges); + Printf.fprintf ppf "\n"; + + Printf.fprintf ppf "Nodes' back edges:\n"; + List.iter (fun ((n, xs) : (Node.t * (Node.t list))) : unit -> + Printf.fprintf ppf "\t%d -> " n.id; + List.iter (fun (x: Node.t) -> Printf.fprintf ppf "%d, " x.id) xs; + Printf.fprintf ppf "\n" + ) (NodeMap.to_list c.reverseEdges); + Printf.fprintf ppf "\n"; + + Printf.fprintf ppf "Input Value: "; + (match c.inputVal with + Some i -> Printf.fprintf ppf "%a" M.pp (i); + | None -> Printf.fprintf ppf "None";); + Printf.fprintf ppf "\n"; + + Printf.fprintf ppf "Output Value: "; + (match c.outputVal with + Some i -> Printf.fprintf ppf "%a" M.pp (i); + | None -> Printf.fprintf ppf "None";); + Printf.fprintf ppf "\n"; + + Printf.fprintf ppf "Initial node's id: "; + (match c.initial with + Some i -> Printf.fprintf ppf "%d" (i.id); + | None -> Printf.fprintf ppf "None";); + Printf.fprintf ppf "\n"; + + Printf.fprintf ppf "Terminal node's id: "; + (match c.terminal with + Some i -> Printf.fprintf ppf "%d" (i.id); + | None -> Printf.fprintf ppf "None";); + Printf.fprintf ppf "\n"; + + Printf.fprintf ppf "Code:\n"; + List.iter (fun ((n, stms) : Node.t * elt list) : unit -> + Printf.fprintf ppf "\tid %d --> %a\n%!" n.id M.pplist (List.rev stms) + ) (NodeMap.to_list c.content); + Printf.fprintf ppf "\n"; +end +;; diff --git a/lib/cfg/Cfg.mli b/lib/cfg/Cfg.mli new file mode 100644 index 0000000..6a61e03 --- /dev/null +++ b/lib/cfg/Cfg.mli @@ -0,0 +1,38 @@ +module type PrintableType = sig + type t + val pp : out_channel -> t -> unit + val pplist : out_channel -> t list -> unit +end + +module Node : sig + type t + val compare : t -> t -> int + val create : unit -> t +end + +module NodeMap : Map.S with type key = Node.t +module NodeSet : Set.S with type elt = Node.t + +module type C = sig + type elt + type t = { + empty: bool; + nodes: NodeSet.t; + edges: (Node.t * (Node.t option)) NodeMap.t; + reverseEdges: (Node.t list) NodeMap.t; + inputVal: elt option; + outputVal: elt option; + initial: Node.t option; + terminal: Node.t option; + content: elt list NodeMap.t + } + + val create : unit -> t + val merge : t -> t -> Node.t -> Node.t -> t + val concat : t -> t -> t + val addToLastNode : elt -> t -> t + + val pp : out_channel -> t -> unit +end + +module Make (M: PrintableType) : C with type elt = M.t diff --git a/lib/cfg/dune b/lib/cfg/dune new file mode 100644 index 0000000..772e4a7 --- /dev/null +++ b/lib/cfg/dune @@ -0,0 +1,5 @@ +(library + (name cfg) + (public_name cfg)) + +(include_subdirs qualified) diff --git a/lib/miniImp/Cfg.ml b/lib/miniImp/Cfg.ml deleted file mode 100644 index 7e92de6..0000000 --- a/lib/miniImp/Cfg.ml +++ /dev/null @@ -1,316 +0,0 @@ -type simpleStatements = - | SimpleSkip - | SimpleAssignment of Types.variable * simpleArithmetic - | SimpleGuard of simpleBoolean -and simpleBoolean = - | SimpleBoolean of bool - | SimpleBAnd of simpleBoolean * simpleBoolean - | SimpleBOr of simpleBoolean * simpleBoolean - | SimpleBNot of simpleBoolean - | SimpleBCmp of simpleArithmetic * simpleArithmetic - | SimpleBCmpLess of simpleArithmetic * simpleArithmetic - | SimpleBCmpLessEq of simpleArithmetic * simpleArithmetic - | SimpleBCmpGreater of simpleArithmetic * simpleArithmetic - | SimpleBCmpGreaterEq of simpleArithmetic * simpleArithmetic -and simpleArithmetic = - | SimpleVariable of Types.variable - | SimpleInteger of int - | SimplePlus of simpleArithmetic * simpleArithmetic - | SimpleMinus of simpleArithmetic * simpleArithmetic - | SimpleTimes of simpleArithmetic * simpleArithmetic - | SimpleDivision of simpleArithmetic * simpleArithmetic - | SimpleModulo of simpleArithmetic * simpleArithmetic - | SimplePower of simpleArithmetic * simpleArithmetic - | SimplePowerMod of simpleArithmetic * simpleArithmetic * simpleArithmetic - | SimpleRand of simpleArithmetic - -let printSingleStatement (ppf) (c: simpleStatements) : unit = - let rec helper_c (ppf) (c: simpleStatements) : unit = - match c with - | SimpleSkip -> Printf.fprintf ppf "Skip" - | SimpleAssignment (v, a) -> Printf.fprintf ppf "Assignment {%s, %a}" v helper_a a - | SimpleGuard (b) -> Printf.fprintf ppf "Guard {%a}" helper_b b - and helper_b (ppf) (c: simpleBoolean) : unit = - match c with - | SimpleBoolean b -> Printf.fprintf ppf "%b" b - | SimpleBAnd (b1, b2) -> Printf.fprintf ppf "{%a && %a}" helper_b b1 helper_b b2 - | SimpleBOr (b1, b2) -> Printf.fprintf ppf "{%a || %a}" helper_b b1 helper_b b2 - | SimpleBNot b -> Printf.fprintf ppf "{not %a}" helper_b b - | SimpleBCmp (a1, a2) -> Printf.fprintf ppf "{%a == %a}" helper_a a1 helper_a a2 - | SimpleBCmpLess (a1, a2) -> Printf.fprintf ppf "{%a < %a}" helper_a a1 helper_a a2 - | SimpleBCmpLessEq (a1, a2) -> Printf.fprintf ppf "{%a <= %a}" helper_a a1 helper_a a2 - | SimpleBCmpGreater (a1, a2) -> Printf.fprintf ppf "{%a > %a}" helper_a a1 helper_a a2 - | SimpleBCmpGreaterEq (a1, a2) -> Printf.fprintf ppf "{%a >= %a}" helper_a a1 helper_a a2 - and helper_a (ppf) (c: simpleArithmetic) : unit = - match c with - | SimpleVariable (v) -> Printf.fprintf ppf "%s" v - | SimpleInteger (i) -> Printf.fprintf ppf "%d" i - | SimplePlus (a1, a2) -> Printf.fprintf ppf "{%a + %a}" helper_a a1 helper_a a2 - | SimpleMinus (a1, a2) -> Printf.fprintf ppf "{%a - %a}" helper_a a1 helper_a a2 - | SimpleTimes (a1, a2) -> Printf.fprintf ppf "{%a * %a}" helper_a a1 helper_a a2 - | SimpleDivision (a1, a2) -> Printf.fprintf ppf "{%a / %a}" helper_a a1 helper_a a2 - | SimpleModulo (a1, a2) -> Printf.fprintf ppf "{%a %% %a}" helper_a a1 helper_a a2 - | SimplePower (a1, a2) -> Printf.fprintf ppf "{%a ^ %a}" helper_a a1 helper_a a2 - | SimplePowerMod (a1, a2, a3) -> Printf.fprintf ppf "{powmod %a %a %a}" helper_a a1 helper_a a2 helper_a a3 - | SimpleRand (a) -> Printf.fprintf ppf "{rand %a}" helper_a a - in - helper_c ppf c - -let printSimpleStatements (ppf) (c: simpleStatements list) : unit = - List.iter (fun x -> printSingleStatement ppf x; Printf.printf "; ") c - - - -let globalIdNode = ref 0; - -module Node = struct - type t = { - id: int - } - let compare a b = compare a.id b.id - - let newNode () = - globalIdNode := !globalIdNode + 1; - {id = !globalIdNode} -end -;; - -module NodeMap = Map.Make(Node) -module NodeSet = Set.Make(Node) - -module Cfg = struct - type t = { - empty: bool; - nodes: NodeSet.t; - edges: (Node.t * (Node.t option)) NodeMap.t; - reverseedges: (Node.t list) NodeMap.t; - initial: Node.t option; - terminal: Node.t option; - code: (simpleStatements list) NodeMap.t - } - - let newCfg () = - { empty = true; - nodes = NodeSet.empty; - edges = NodeMap.empty; - reverseedges = NodeMap.empty; - initial = None; - terminal = None; - code = NodeMap.empty } - - let mergeCfg (cfg1: t) (cfg2: t) (entryNode: Node.t) (exitNode: Node.t) : t = - match (cfg1.empty, cfg2.empty) with - true, _ -> cfg2 - | _, true -> cfg1 - | false, false -> - let cfg1initial = Option.get cfg1.initial in - let cfg2initial = Option.get cfg2.initial in - let cfg1terminal = Option.get cfg1.terminal in - let cfg2terminal = Option.get cfg2.terminal in - { empty = false; - nodes = NodeSet.union cfg1.nodes cfg2.nodes |> - NodeSet.add entryNode |> - NodeSet.add exitNode; - edges = NodeMap.union (fun _ -> failwith "Failed merging edges of cfg.") - cfg1.edges cfg2.edges |> - NodeMap.add entryNode (cfg1initial, Some cfg2initial) |> - NodeMap.add cfg1terminal (exitNode, None) |> - NodeMap.add cfg2terminal (exitNode, None); - reverseedges = NodeMap.union (fun _ -> failwith "Failed merging edges of cfg.") - cfg1.reverseedges cfg2.reverseedges |> - NodeMap.add_to_list cfg1initial entryNode |> - NodeMap.add_to_list cfg2initial entryNode |> - NodeMap.add_to_list exitNode cfg1terminal |> - NodeMap.add_to_list exitNode cfg2terminal; - initial = Some entryNode; - terminal = Some exitNode; - code = NodeMap.union (fun _ -> failwith "Failed merging code of cfg.") - cfg1.code cfg2.code - } - - let concatCfg (cfg1: t) (cfg2: t) : t = - match (cfg1.empty, cfg2.empty) with - true, _ -> cfg2 - | _, true -> cfg1 - | false, false -> - let cfg1initial = Option.get cfg1.initial in - let cfg2initial = Option.get cfg2.initial in - let cfg1terminal = Option.get cfg1.terminal in - let cfg2terminal = Option.get cfg2.terminal in - { empty = false; - nodes = NodeSet.union cfg1.nodes cfg2.nodes; - edges = NodeMap.union (fun _ -> failwith "Failed merging edges of cfg.") - cfg1.edges cfg2.edges |> - NodeMap.add cfg1terminal (cfg2initial, None); - reverseedges = NodeMap.union (fun _ -> failwith "Failed merging edges of cfg.") - cfg1.reverseedges cfg2.reverseedges |> - NodeMap.add_to_list cfg2initial cfg1terminal; - initial = Some cfg1initial; - terminal = Some cfg2terminal; - code = NodeMap.union (fun _ -> failwith "Failed merging code of cfg.") - cfg1.code cfg2.code - } - - let addToLastNode (newcode: simpleStatements) (cfg: t) : t = - match cfg.empty with - | true -> let newnode = Node.newNode () in - { empty = false; - nodes = NodeSet.singleton newnode; - edges = NodeMap.empty; - reverseedges = NodeMap.empty; - initial = Some newnode; - terminal = Some newnode; - code = NodeMap.singleton newnode [newcode] - } - | false -> - let prevcfgterminal = Option.get cfg.terminal in - { cfg with - code = (NodeMap.add_to_list - prevcfgterminal - newcode - cfg.code) } - - let pp (ppf) (c: t) : unit = - Printf.fprintf ppf "Nodes' ids: "; - List.iter (fun (x : Node.t) -> Printf.fprintf ppf "%d " x.id) (NodeSet.to_list c.nodes); - Printf.fprintf ppf "\n"; - - Printf.fprintf ppf "Nodes' edges:\n"; - List.iter (fun ((n, (a, b)) : (Node.t * (Node.t * Node.t option))) : unit -> - match b with None -> Printf.fprintf ppf "\t%d -> %d\n" n.id a.id - | Some b -> Printf.fprintf ppf "\t%d -> %d, %d\n" n.id a.id b.id - ) (NodeMap.to_list c.edges); - Printf.fprintf ppf "\n"; - - Printf.fprintf ppf "Nodes' back edges:\n"; - List.iter (fun ((n, xs) : (Node.t * (Node.t list))) : unit -> - Printf.fprintf ppf "\t%d -> " n.id; - List.iter (fun (x: Node.t) -> Printf.fprintf ppf "%d, " x.id) xs; - Printf.fprintf ppf "\n" - ) (NodeMap.to_list c.reverseedges); - Printf.fprintf ppf "\n"; - - Printf.fprintf ppf "Initial node's id: "; - Printf.fprintf ppf "%d" ((Option.get c.initial).id); - Printf.fprintf ppf "\n"; - - Printf.fprintf ppf "Terminal node's id: "; - Printf.fprintf ppf "%d" ((Option.get c.terminal).id); - Printf.fprintf ppf "\n"; - - Printf.fprintf ppf "Code:\n"; - List.iter (fun ((n, stms) : Node.t * simpleStatements list) : unit -> - Printf.fprintf ppf "\tid %d --> %a\n%!" n.id printSimpleStatements (List.rev stms) - ) (NodeMap.to_list c.code); - Printf.fprintf ppf "\n"; -end -;; - - -let rec convert_c (prevcfg: Cfg.t) (prg: Types.c_exp) : Cfg.t = - match prg with - | Skip -> prevcfg |> Cfg.addToLastNode SimpleSkip - | Assignment (x, a) -> prevcfg |> Cfg.addToLastNode (SimpleAssignment (x, convert_a a)) - | Sequence (c1, c2) -> - let cfg1 = convert_c prevcfg c1 in - let cfg2 = convert_c cfg1 c2 in - cfg2 - | If (b, c1, c2) -> - let convertedb = convert_b b in - let cfg1 = convert_c (Cfg.newCfg ()) c1 in - let cfg2 = convert_c (Cfg.newCfg ()) c2 in - let entrynode = Node.newNode () in - let exitnode = Node.newNode () in - let newcfg = Cfg.mergeCfg cfg1 cfg2 entrynode exitnode in - let mergedcfg = Cfg.concatCfg prevcfg newcfg in - { mergedcfg with - code = mergedcfg.code |> - NodeMap.add_to_list entrynode (SimpleGuard convertedb) |> - NodeMap.add_to_list exitnode (SimpleSkip) } - | While (b, c) -> - let convertedb = convert_b b in - let cfg = convert_c (Cfg.newCfg ()) c in - let cfginitial = Option.get cfg.initial in - let cfgterminal = Option.get cfg.terminal in - let entrynode = Node.newNode () in - let guardnode = Node.newNode () in - let exitnode = Node.newNode () in - { empty = false; - nodes = cfg.nodes |> - NodeSet.add entrynode |> - NodeSet.add guardnode |> - NodeSet.add exitnode; - edges = cfg.edges |> - NodeMap.add entrynode (guardnode, None) |> - NodeMap.add guardnode (cfginitial, Some exitnode) |> - NodeMap.add cfgterminal (guardnode, None); - reverseedges = cfg.reverseedges |> - NodeMap.add_to_list guardnode entrynode |> - NodeMap.add_to_list cfginitial guardnode |> - NodeMap.add_to_list exitnode guardnode |> - NodeMap.add_to_list guardnode cfgterminal; - initial = Some entrynode; - terminal = Some exitnode; - code = NodeMap.add_to_list guardnode (SimpleGuard (convertedb)) cfg.code |> - NodeMap.add_to_list exitnode (SimpleSkip) - } |> Cfg.concatCfg prevcfg - | For (assignment, guard, increment, body) -> - let cfgassignment = convert_c (Cfg.newCfg ()) assignment in - let convertedguard = convert_b guard in - let cfgincrement = convert_c (Cfg.newCfg ()) increment in - let cfgbody = convert_c (Cfg.newCfg ()) body in - - let prevassignment = Cfg.concatCfg prevcfg cfgassignment in - let bodyincrement = Cfg.concatCfg cfgbody cfgincrement in - - let cfginitial = Option.get bodyincrement.initial in - let cfgterminal = Option.get bodyincrement.terminal in - - let guardnode = Node.newNode () in - let exitnode = Node.newNode () in - { empty = false; - nodes = bodyincrement.nodes |> - NodeSet.add guardnode |> - NodeSet.add exitnode; - edges = bodyincrement.edges |> - NodeMap.add guardnode (cfginitial, Some exitnode) |> - NodeMap.add cfgterminal (guardnode, None); - reverseedges = bodyincrement.reverseedges |> - NodeMap.add_to_list cfginitial guardnode |> - NodeMap.add_to_list exitnode guardnode |> - NodeMap.add_to_list guardnode cfgterminal; - initial = Some guardnode; - terminal = Some exitnode; - code = NodeMap.add_to_list guardnode (SimpleGuard (convertedguard)) bodyincrement.code |> - NodeMap.add_to_list exitnode (SimpleSkip) - } |> Cfg.concatCfg prevassignment - -and convert_b (prg: Types.b_exp) : simpleBoolean = - match prg with - | Boolean (b) -> SimpleBoolean b - | BAnd (b1, b2) -> SimpleBAnd (convert_b b1, convert_b b2) - | BOr (b1, b2) -> SimpleBOr (convert_b b1, convert_b b2) - | BNot (b) -> SimpleBNot (convert_b b) - | BCmp (a1, a2) -> SimpleBCmp (convert_a a1, convert_a a2) - | BCmpLess (a1, a2) -> SimpleBCmpLess (convert_a a1, convert_a a2) - | BCmpLessEq (a1, a2) -> SimpleBCmpLessEq (convert_a a1, convert_a a2) - | BCmpGreater (a1, a2) -> SimpleBCmpGreater (convert_a a1, convert_a a2) - | BCmpGreaterEq (a1, a2) -> SimpleBCmpGreaterEq (convert_a a1, convert_a a2) - -and convert_a (prg: Types.a_exp) : simpleArithmetic = - match prg with - | Variable x -> SimpleVariable x - | Integer n -> SimpleInteger n - | Plus (a1, a2) -> SimplePlus (convert_a a1, convert_a a2) - | Minus (a1, a2) -> SimpleMinus (convert_a a1, convert_a a2) - | Times (a1, a2) -> SimpleTimes (convert_a a1, convert_a a2) - | Division (a1, a2) -> SimpleDivision (convert_a a1, convert_a a2) - | Modulo (a1, a2) -> SimpleModulo (convert_a a1, convert_a a2) - | Power (a1, a2) -> SimplePower (convert_a a1, convert_a a2) - | PowerMod (a1, a2, a3) -> SimplePowerMod (convert_a a1, convert_a a2, convert_a a3) - | Rand (a) -> SimpleRand (convert_a a) - -let convert (prg: Types.p_exp) : Cfg.t = - match prg with - | Main (_, _, exp) -> - convert_c (Cfg.newCfg ()) exp diff --git a/lib/miniImp/Cfg.mli b/lib/miniImp/Cfg.mli deleted file mode 100644 index 46a0f7a..0000000 --- a/lib/miniImp/Cfg.mli +++ /dev/null @@ -1,40 +0,0 @@ -type simpleStatements = - | SimpleSkip - | SimpleAssignment of Types.variable * simpleArithmetic - | SimpleGuard of simpleBoolean -and simpleBoolean = - | SimpleBoolean of bool - | SimpleBAnd of simpleBoolean * simpleBoolean - | SimpleBOr of simpleBoolean * simpleBoolean - | SimpleBNot of simpleBoolean - | SimpleBCmp of simpleArithmetic * simpleArithmetic - | SimpleBCmpLess of simpleArithmetic * simpleArithmetic - | SimpleBCmpLessEq of simpleArithmetic * simpleArithmetic - | SimpleBCmpGreater of simpleArithmetic * simpleArithmetic - | SimpleBCmpGreaterEq of simpleArithmetic * simpleArithmetic -and simpleArithmetic = - | SimpleVariable of Types.variable - | SimpleInteger of int - | SimplePlus of simpleArithmetic * simpleArithmetic - | SimpleMinus of simpleArithmetic * simpleArithmetic - | SimpleTimes of simpleArithmetic * simpleArithmetic - | SimpleDivision of simpleArithmetic * simpleArithmetic - | SimpleModulo of simpleArithmetic * simpleArithmetic - | SimplePower of simpleArithmetic * simpleArithmetic - | SimplePowerMod of simpleArithmetic * simpleArithmetic * simpleArithmetic - | SimpleRand of simpleArithmetic - -module Node : sig - type t - val compare : t -> t -> int -end - -module NodeMap : Map.S with type key = Node.t -module NodeSet : Set.S with type elt = Node.t - -module Cfg : sig - type t - val pp : out_channel -> t -> unit -end - -val convert : Types.p_exp -> Cfg.t diff --git a/lib/miniImp/CfgImp.ml b/lib/miniImp/CfgImp.ml new file mode 100644 index 0000000..be8db0e --- /dev/null +++ b/lib/miniImp/CfgImp.ml @@ -0,0 +1,182 @@ +open Cfg + +module SimpleStatements = struct + type t = + | SimpleSkip + | SimpleAssignment of Types.variable * simpleArithmetic + | SimpleGuard of simpleBoolean + and simpleBoolean = + | SimpleBoolean of bool + | SimpleBAnd of simpleBoolean * simpleBoolean + | SimpleBOr of simpleBoolean * simpleBoolean + | SimpleBNot of simpleBoolean + | SimpleBCmp of simpleArithmetic * simpleArithmetic + | SimpleBCmpLess of simpleArithmetic * simpleArithmetic + | SimpleBCmpLessEq of simpleArithmetic * simpleArithmetic + | SimpleBCmpGreater of simpleArithmetic * simpleArithmetic + | SimpleBCmpGreaterEq of simpleArithmetic * simpleArithmetic + and simpleArithmetic = + | SimpleVariable of Types.variable + | SimpleInteger of int + | SimplePlus of simpleArithmetic * simpleArithmetic + | SimpleMinus of simpleArithmetic * simpleArithmetic + | SimpleTimes of simpleArithmetic * simpleArithmetic + | SimpleDivision of simpleArithmetic * simpleArithmetic + | SimpleModulo of simpleArithmetic * simpleArithmetic + | SimplePower of simpleArithmetic * simpleArithmetic + | SimplePowerMod of simpleArithmetic * simpleArithmetic * simpleArithmetic + | SimpleRand of simpleArithmetic + + let pp (ppf: out_channel) (c: t) : unit = + let rec helper_c (ppf) (c: t) : unit = + match c with + | SimpleSkip -> Printf.fprintf ppf "Skip" + | SimpleAssignment (v, a) -> Printf.fprintf ppf "Assignment {%s, %a}" v helper_a a + | SimpleGuard (b) -> Printf.fprintf ppf "Guard {%a}" helper_b b + and helper_b (ppf) (c: simpleBoolean) : unit = + match c with + | SimpleBoolean b -> Printf.fprintf ppf "%b" b + | SimpleBAnd (b1, b2) -> Printf.fprintf ppf "{%a && %a}" helper_b b1 helper_b b2 + | SimpleBOr (b1, b2) -> Printf.fprintf ppf "{%a || %a}" helper_b b1 helper_b b2 + | SimpleBNot b -> Printf.fprintf ppf "{not %a}" helper_b b + | SimpleBCmp (a1, a2) -> Printf.fprintf ppf "{%a == %a}" helper_a a1 helper_a a2 + | SimpleBCmpLess (a1, a2) -> Printf.fprintf ppf "{%a < %a}" helper_a a1 helper_a a2 + | SimpleBCmpLessEq (a1, a2) -> Printf.fprintf ppf "{%a <= %a}" helper_a a1 helper_a a2 + | SimpleBCmpGreater (a1, a2) -> Printf.fprintf ppf "{%a > %a}" helper_a a1 helper_a a2 + | SimpleBCmpGreaterEq (a1, a2) -> Printf.fprintf ppf "{%a >= %a}" helper_a a1 helper_a a2 + and helper_a (ppf) (c: simpleArithmetic) : unit = + match c with + | SimpleVariable (v) -> Printf.fprintf ppf "%s" v + | SimpleInteger (i) -> Printf.fprintf ppf "%d" i + | SimplePlus (a1, a2) -> Printf.fprintf ppf "{%a + %a}" helper_a a1 helper_a a2 + | SimpleMinus (a1, a2) -> Printf.fprintf ppf "{%a - %a}" helper_a a1 helper_a a2 + | SimpleTimes (a1, a2) -> Printf.fprintf ppf "{%a * %a}" helper_a a1 helper_a a2 + | SimpleDivision (a1, a2) -> Printf.fprintf ppf "{%a / %a}" helper_a a1 helper_a a2 + | SimpleModulo (a1, a2) -> Printf.fprintf ppf "{%a %% %a}" helper_a a1 helper_a a2 + | SimplePower (a1, a2) -> Printf.fprintf ppf "{%a ^ %a}" helper_a a1 helper_a a2 + | SimplePowerMod (a1, a2, a3) -> Printf.fprintf ppf "{powmod %a %a %a}" helper_a a1 helper_a a2 helper_a a3 + | SimpleRand (a) -> Printf.fprintf ppf "{rand %a}" helper_a a + in + helper_c ppf c + + let pplist (ppf: out_channel) (c: t list) : unit = + List.iter (fun x -> pp ppf x; Printf.printf "; ") c +end + +module SSCfg = Cfg.Make(SimpleStatements) + +let rec convert_c (prevcfg: SSCfg.t) (prg: Types.c_exp) : SSCfg.t = + let open SimpleStatements in + match prg with + | Skip -> prevcfg |> SSCfg.addToLastNode SimpleSkip + | Assignment (x, a) -> prevcfg |> SSCfg.addToLastNode (SimpleAssignment (x, convert_a a)) + | Sequence (c1, c2) -> + let cfg1 = convert_c prevcfg c1 in + let cfg2 = convert_c cfg1 c2 in + cfg2 + | If (b, c1, c2) -> + let convertedb = convert_b b in + let cfg1 = convert_c (SSCfg.create ()) c1 in + let cfg2 = convert_c (SSCfg.create ()) c2 in + let entrynode = Node.create () in + let exitnode = Node.create () in + let newcfg = SSCfg.merge cfg1 cfg2 entrynode exitnode in + let mergedcfg = SSCfg.concat prevcfg newcfg in + { mergedcfg with + content = mergedcfg.content |> + NodeMap.add_to_list entrynode (SimpleGuard convertedb) |> + NodeMap.add_to_list exitnode (SimpleSkip) } + | While (b, c) -> + let convertedb = convert_b b in + let cfg = convert_c (SSCfg.create ()) c in + let cfginitial = Option.get cfg.initial in + let cfgterminal = Option.get cfg.terminal in + let entrynode = Node.create () in + let guardnode = Node.create () in + let exitnode = Node.create () in + { empty = false; + nodes = cfg.nodes |> + NodeSet.add entrynode |> + NodeSet.add guardnode |> + NodeSet.add exitnode; + edges = cfg.edges |> + NodeMap.add entrynode (guardnode, None) |> + NodeMap.add guardnode (cfginitial, Some exitnode) |> + NodeMap.add cfgterminal (guardnode, None); + reverseEdges = cfg.reverseEdges |> + NodeMap.add_to_list guardnode entrynode |> + NodeMap.add_to_list cfginitial guardnode |> + NodeMap.add_to_list exitnode guardnode |> + NodeMap.add_to_list guardnode cfgterminal; + inputVal = prevcfg.inputVal; + outputVal = prevcfg.outputVal; + initial = Some entrynode; + terminal = Some exitnode; + content = NodeMap.add_to_list guardnode (SimpleGuard (convertedb)) cfg.content |> + NodeMap.add_to_list exitnode (SimpleSkip) + } |> SSCfg.concat prevcfg + | For (assignment, guard, increment, body) -> + let cfgassignment = convert_c (SSCfg.create ()) assignment in + let convertedguard = convert_b guard in + let cfgincrement = convert_c (SSCfg.create ()) increment in + let cfgbody = convert_c (SSCfg.create ()) body in + + let prevassignment = SSCfg.concat prevcfg cfgassignment in + let bodyincrement = SSCfg.concat cfgbody cfgincrement in + + let cfginitial = Option.get bodyincrement.initial in + let cfgterminal = Option.get bodyincrement.terminal in + + let guardnode = Node.create () in + let exitnode = Node.create () in + { empty = false; + nodes = bodyincrement.nodes |> + NodeSet.add guardnode |> + NodeSet.add exitnode; + edges = bodyincrement.edges |> + NodeMap.add guardnode (cfginitial, Some exitnode) |> + NodeMap.add cfgterminal (guardnode, None); + reverseEdges = bodyincrement.reverseEdges |> + NodeMap.add_to_list cfginitial guardnode |> + NodeMap.add_to_list exitnode guardnode |> + NodeMap.add_to_list guardnode cfgterminal; + inputVal = prevcfg.inputVal; + outputVal = prevcfg.outputVal; + initial = Some guardnode; + terminal = Some exitnode; + content = NodeMap.add_to_list guardnode (SimpleGuard (convertedguard)) bodyincrement.content |> + NodeMap.add_to_list exitnode (SimpleSkip) + } |> SSCfg.concat prevassignment + +and convert_b (prg: Types.b_exp) : SimpleStatements.simpleBoolean = + match prg with + | Boolean (b) -> SimpleBoolean b + | BAnd (b1, b2) -> SimpleBAnd (convert_b b1, convert_b b2) + | BOr (b1, b2) -> SimpleBOr (convert_b b1, convert_b b2) + | BNot (b) -> SimpleBNot (convert_b b) + | BCmp (a1, a2) -> SimpleBCmp (convert_a a1, convert_a a2) + | BCmpLess (a1, a2) -> SimpleBCmpLess (convert_a a1, convert_a a2) + | BCmpLessEq (a1, a2) -> SimpleBCmpLessEq (convert_a a1, convert_a a2) + | BCmpGreater (a1, a2) -> SimpleBCmpGreater (convert_a a1, convert_a a2) + | BCmpGreaterEq (a1, a2) -> SimpleBCmpGreaterEq (convert_a a1, convert_a a2) + +and convert_a (prg: Types.a_exp) : SimpleStatements.simpleArithmetic = + match prg with + | Variable x -> SimpleVariable x + | Integer n -> SimpleInteger n + | Plus (a1, a2) -> SimplePlus (convert_a a1, convert_a a2) + | Minus (a1, a2) -> SimpleMinus (convert_a a1, convert_a a2) + | Times (a1, a2) -> SimpleTimes (convert_a a1, convert_a a2) + | Division (a1, a2) -> SimpleDivision (convert_a a1, convert_a a2) + | Modulo (a1, a2) -> SimpleModulo (convert_a a1, convert_a a2) + | Power (a1, a2) -> SimplePower (convert_a a1, convert_a a2) + | PowerMod (a1, a2, a3) -> SimplePowerMod (convert_a a1, convert_a a2, convert_a a3) + | Rand (a) -> SimpleRand (convert_a a) + +let convert (prg: Types.p_exp) : SSCfg.t = + let result = + match prg with + | Main (_, _, exp) -> + convert_c (SSCfg.create ()) exp + in + {result with inputVal = None; outputVal = None} diff --git a/lib/miniImp/CfgImp.mli b/lib/miniImp/CfgImp.mli new file mode 100644 index 0000000..6f73010 --- /dev/null +++ b/lib/miniImp/CfgImp.mli @@ -0,0 +1,34 @@ +module SimpleStatements : sig + type t = + | SimpleSkip + | SimpleAssignment of Types.variable * simpleArithmetic + | SimpleGuard of simpleBoolean + and simpleBoolean = + | SimpleBoolean of bool + | SimpleBAnd of simpleBoolean * simpleBoolean + | SimpleBOr of simpleBoolean * simpleBoolean + | SimpleBNot of simpleBoolean + | SimpleBCmp of simpleArithmetic * simpleArithmetic + | SimpleBCmpLess of simpleArithmetic * simpleArithmetic + | SimpleBCmpLessEq of simpleArithmetic * simpleArithmetic + | SimpleBCmpGreater of simpleArithmetic * simpleArithmetic + | SimpleBCmpGreaterEq of simpleArithmetic * simpleArithmetic + and simpleArithmetic = + | SimpleVariable of Types.variable + | SimpleInteger of int + | SimplePlus of simpleArithmetic * simpleArithmetic + | SimpleMinus of simpleArithmetic * simpleArithmetic + | SimpleTimes of simpleArithmetic * simpleArithmetic + | SimpleDivision of simpleArithmetic * simpleArithmetic + | SimpleModulo of simpleArithmetic * simpleArithmetic + | SimplePower of simpleArithmetic * simpleArithmetic + | SimplePowerMod of simpleArithmetic * simpleArithmetic * simpleArithmetic + | SimpleRand of simpleArithmetic + + val pp : out_channel -> t -> unit + val pplist : out_channel -> t list -> unit +end + +module SSCfg : Cfg.C with type elt = SimpleStatements.t + +val convert : Types.p_exp -> SSCfg.t diff --git a/lib/miniImp/dune b/lib/miniImp/dune index 37cb1c9..ffeaddc 100644 --- a/lib/miniImp/dune +++ b/lib/miniImp/dune @@ -10,7 +10,7 @@ (library (name miniImp) (public_name miniImp) - (modules Lexer Parser Types Semantics Cfg) - (libraries utility menhirLib)) + (modules Lexer Parser Types Semantics CfgImp) + (libraries cfg utility menhirLib)) (include_subdirs qualified) From 99287d79c5508103df7854de62003690498ce3b6 Mon Sep 17 00:00:00 2001 From: elvis Date: Wed, 27 Nov 2024 20:18:30 +0100 Subject: [PATCH 02/29] Implementing cfg for risc --- bin/main.ml | 10 +- lib/cfg/Cfg.ml | 28 +-- lib/cfg/Cfg.mli | 10 +- lib/miniImp/CfgImp.ml | 28 +-- lib/miniImp/CfgImp.mli | 1 + lib/miniImp/CfgRISC.ml | 459 ++++++++++++++++++++++++++++++++++++++++ lib/miniImp/CfgRISC.mli | 53 +++++ lib/miniImp/dune | 2 +- 8 files changed, 554 insertions(+), 37 deletions(-) create mode 100644 lib/miniImp/CfgRISC.ml create mode 100644 lib/miniImp/CfgRISC.mli diff --git a/bin/main.ml b/bin/main.ml index 832d583..4b5bd64 100644 --- a/bin/main.ml +++ b/bin/main.ml @@ -1,5 +1,4 @@ open MiniImp -open MiniImp.CfgImp let () = let program = "def main with input x output y as @@ -10,11 +9,14 @@ let () = ) else x := 1 - y;" in - let get_result x = Lexing.from_string x |> Parser.prg Lexer.lex in let p = get_result program in - let converted = convert p in + let convertedcfg = CfgImp.convert_io p 3 in - Printf.printf "%a" SSCfg.pp converted + Printf.printf "%a" CfgImp.SSCfg.pp convertedcfg; + + let convertedrisccfg = CfgRISC.convert convertedcfg in + + Printf.printf "%a" CfgRISC.RISCCfg.pp convertedrisccfg diff --git a/lib/cfg/Cfg.ml b/lib/cfg/Cfg.ml index b0b61e2..70e26a9 100644 --- a/lib/cfg/Cfg.ml +++ b/lib/cfg/Cfg.ml @@ -28,14 +28,14 @@ module type C = sig nodes: NodeSet.t; edges: (Node.t * (Node.t option)) NodeMap.t; reverseEdges: (Node.t list) NodeMap.t; - inputVal: elt option; - outputVal: elt option; + inputVal: int option; + inputOutputVar: (string * string) option; initial: Node.t option; terminal: Node.t option; content: elt list NodeMap.t } - val create : unit -> t + val empty : t val merge : t -> t -> Node.t -> Node.t -> t val concat : t -> t -> t val addToLastNode : elt -> t -> t @@ -50,20 +50,20 @@ module Make(M: PrintableType) = struct nodes: NodeSet.t; edges: (Node.t * (Node.t option)) NodeMap.t; reverseEdges: (Node.t list) NodeMap.t; - inputVal: elt option; - outputVal: elt option; + inputVal: int option; + inputOutputVar: (string * string) option; initial: Node.t option; terminal: Node.t option; content: elt list NodeMap.t } - let create () : t = + let empty : t = { empty = true; nodes = NodeSet.empty; edges = NodeMap.empty; reverseEdges = NodeMap.empty; inputVal = None; - outputVal = None; + inputOutputVar = None; initial = None; terminal = None; content = NodeMap.empty } @@ -93,7 +93,7 @@ module Make(M: PrintableType) = struct NodeMap.add_to_list exitNode cfg1terminal |> NodeMap.add_to_list exitNode cfg2terminal; inputVal = cfg1.inputVal; - outputVal = cfg1.outputVal; + inputOutputVar = cfg1.inputOutputVar; initial = Some entryNode; terminal = Some exitNode; content = NodeMap.union (fun _ -> failwith "Failed merging code of cfg.") @@ -118,7 +118,7 @@ module Make(M: PrintableType) = struct cfg1.reverseEdges cfg2.reverseEdges |> NodeMap.add_to_list cfg2initial cfg1terminal; inputVal = cfg1.inputVal; - outputVal = cfg1.outputVal; + inputOutputVar = cfg1.inputOutputVar; initial = Some cfg1initial; terminal = Some cfg2terminal; content = NodeMap.union (fun _ -> failwith "Failed merging code of cfg.") @@ -133,7 +133,7 @@ module Make(M: PrintableType) = struct edges = NodeMap.empty; reverseEdges = NodeMap.empty; inputVal = None; - outputVal = None; + inputOutputVar = None; initial = Some newnode; terminal = Some newnode; content = NodeMap.singleton newnode [newcontent] @@ -168,13 +168,13 @@ module Make(M: PrintableType) = struct Printf.fprintf ppf "Input Value: "; (match c.inputVal with - Some i -> Printf.fprintf ppf "%a" M.pp (i); + Some i -> Printf.fprintf ppf "%d" i; | None -> Printf.fprintf ppf "None";); Printf.fprintf ppf "\n"; - Printf.fprintf ppf "Output Value: "; - (match c.outputVal with - Some i -> Printf.fprintf ppf "%a" M.pp (i); + Printf.fprintf ppf "Input and Output Vars: "; + (match c.inputOutputVar with + Some (i, o) -> Printf.fprintf ppf "(in: %s, out: %s)" i o; | None -> Printf.fprintf ppf "None";); Printf.fprintf ppf "\n"; diff --git a/lib/cfg/Cfg.mli b/lib/cfg/Cfg.mli index 6a61e03..1c23d2d 100644 --- a/lib/cfg/Cfg.mli +++ b/lib/cfg/Cfg.mli @@ -5,7 +5,9 @@ module type PrintableType = sig end module Node : sig - type t + type t = { + id: int + } val compare : t -> t -> int val create : unit -> t end @@ -20,14 +22,14 @@ module type C = sig nodes: NodeSet.t; edges: (Node.t * (Node.t option)) NodeMap.t; reverseEdges: (Node.t list) NodeMap.t; - inputVal: elt option; - outputVal: elt option; + inputVal: int option; + inputOutputVar: (string * string) option; initial: Node.t option; terminal: Node.t option; content: elt list NodeMap.t } - val create : unit -> t + val empty : t val merge : t -> t -> Node.t -> Node.t -> t val concat : t -> t -> t val addToLastNode : elt -> t -> t diff --git a/lib/miniImp/CfgImp.ml b/lib/miniImp/CfgImp.ml index be8db0e..6655d30 100644 --- a/lib/miniImp/CfgImp.ml +++ b/lib/miniImp/CfgImp.ml @@ -76,8 +76,8 @@ let rec convert_c (prevcfg: SSCfg.t) (prg: Types.c_exp) : SSCfg.t = cfg2 | If (b, c1, c2) -> let convertedb = convert_b b in - let cfg1 = convert_c (SSCfg.create ()) c1 in - let cfg2 = convert_c (SSCfg.create ()) c2 in + let cfg1 = convert_c SSCfg.empty c1 in + let cfg2 = convert_c SSCfg.empty c2 in let entrynode = Node.create () in let exitnode = Node.create () in let newcfg = SSCfg.merge cfg1 cfg2 entrynode exitnode in @@ -88,7 +88,7 @@ let rec convert_c (prevcfg: SSCfg.t) (prg: Types.c_exp) : SSCfg.t = NodeMap.add_to_list exitnode (SimpleSkip) } | While (b, c) -> let convertedb = convert_b b in - let cfg = convert_c (SSCfg.create ()) c in + let cfg = convert_c SSCfg.empty c in let cfginitial = Option.get cfg.initial in let cfgterminal = Option.get cfg.terminal in let entrynode = Node.create () in @@ -109,17 +109,17 @@ let rec convert_c (prevcfg: SSCfg.t) (prg: Types.c_exp) : SSCfg.t = NodeMap.add_to_list exitnode guardnode |> NodeMap.add_to_list guardnode cfgterminal; inputVal = prevcfg.inputVal; - outputVal = prevcfg.outputVal; + inputOutputVar = prevcfg.inputOutputVar; initial = Some entrynode; terminal = Some exitnode; content = NodeMap.add_to_list guardnode (SimpleGuard (convertedb)) cfg.content |> NodeMap.add_to_list exitnode (SimpleSkip) } |> SSCfg.concat prevcfg | For (assignment, guard, increment, body) -> - let cfgassignment = convert_c (SSCfg.create ()) assignment in + let cfgassignment = convert_c SSCfg.empty assignment in let convertedguard = convert_b guard in - let cfgincrement = convert_c (SSCfg.create ()) increment in - let cfgbody = convert_c (SSCfg.create ()) body in + let cfgincrement = convert_c SSCfg.empty increment in + let cfgbody = convert_c SSCfg.empty body in let prevassignment = SSCfg.concat prevcfg cfgassignment in let bodyincrement = SSCfg.concat cfgbody cfgincrement in @@ -141,7 +141,7 @@ let rec convert_c (prevcfg: SSCfg.t) (prg: Types.c_exp) : SSCfg.t = NodeMap.add_to_list exitnode guardnode |> NodeMap.add_to_list guardnode cfgterminal; inputVal = prevcfg.inputVal; - outputVal = prevcfg.outputVal; + inputOutputVar = prevcfg.inputOutputVar; initial = Some guardnode; terminal = Some exitnode; content = NodeMap.add_to_list guardnode (SimpleGuard (convertedguard)) bodyincrement.content |> @@ -174,9 +174,9 @@ and convert_a (prg: Types.a_exp) : SimpleStatements.simpleArithmetic = | Rand (a) -> SimpleRand (convert_a a) let convert (prg: Types.p_exp) : SSCfg.t = - let result = - match prg with - | Main (_, _, exp) -> - convert_c (SSCfg.create ()) exp - in - {result with inputVal = None; outputVal = None} + match prg with + | Main (i, o, exp) -> + {(convert_c SSCfg.empty exp) with inputOutputVar = Some (i, o)} + +let convert_io (prg: Types.p_exp) (i: int) : SSCfg.t = + {(convert prg) with inputVal = Some i} diff --git a/lib/miniImp/CfgImp.mli b/lib/miniImp/CfgImp.mli index 6f73010..b0f7035 100644 --- a/lib/miniImp/CfgImp.mli +++ b/lib/miniImp/CfgImp.mli @@ -32,3 +32,4 @@ end module SSCfg : Cfg.C with type elt = SimpleStatements.t val convert : Types.p_exp -> SSCfg.t +val convert_io : Types.p_exp -> int -> SSCfg.t diff --git a/lib/miniImp/CfgRISC.ml b/lib/miniImp/CfgRISC.ml new file mode 100644 index 0000000..7c6d42e --- /dev/null +++ b/lib/miniImp/CfgRISC.ml @@ -0,0 +1,459 @@ +module RISCSimpleStatements = struct + type register = { + index: int + } + + type t = + | Nop + | BRegOp of brop * register * register * register + | BImmOp of biop * register * int * register + | URegOp of urop * register * register + | Load of register * register + | LoadI of register * int + | Store of register * register + and brop = + | Add + | Sub + | Mult + | Div + | Mod + | Pow + | And + | Or + | Eq + | Less + | LessEq + | More + | MoreEq + and biop = + | AddI + | SubI + | MultI + | DivI + | ModI + | PowI + | AndI + | OrI + | EqI + | LessI + | LessEqI + | MoreI + | MoreEqI + and urop = + | Not + | Copy + | Rand + + let pp (ppf: out_channel) (v: t) : unit = + let rec pp_t (ppf: out_channel) (v: t) : unit = + match v with + Nop -> Printf.fprintf ppf "nop" + | BRegOp (b, r1, r2, r3) -> Printf.fprintf ppf "%a r%d r%d => r%d" pp_brop b r1.index r2.index r3.index + | BImmOp (b, r1, i, r3) -> Printf.fprintf ppf "%a r%d %d => r%d" pp_biop b r1.index i r3.index + | URegOp (u, r1, r2) -> Printf.fprintf ppf "%a r%d => r%d" pp_urop u r1.index r2.index + | Load (r1, r2) -> Printf.fprintf ppf "load r%d => r%d" r1.index r2.index + | LoadI (r2, i) -> Printf.fprintf ppf "loadi %d => r%d" i r2.index + | Store (r1, r2) -> Printf.fprintf ppf "store r%d => r%d" r1.index r2.index + and pp_brop (ppf: out_channel) (v: brop) : unit = + match v with + Add -> Printf.fprintf ppf "Add" + | Sub -> Printf.fprintf ppf "Sub" + | Mult -> Printf.fprintf ppf "Mult" + | Div -> Printf.fprintf ppf "Div" + | Mod -> Printf.fprintf ppf "Mod" + | Pow -> Printf.fprintf ppf "Pow" + | And -> Printf.fprintf ppf "And" + | Or -> Printf.fprintf ppf "Or" + | Eq -> Printf.fprintf ppf "Eq" + | Less -> Printf.fprintf ppf "Less" + | LessEq -> Printf.fprintf ppf "LessEq" + | More -> Printf.fprintf ppf "More" + | MoreEq -> Printf.fprintf ppf "MoreEq" + and pp_biop (ppf: out_channel) (v: biop) : unit = + match v with + AddI -> Printf.fprintf ppf "AddI" + | SubI -> Printf.fprintf ppf "SubI" + | MultI -> Printf.fprintf ppf "MultI" + | DivI -> Printf.fprintf ppf "DivI" + | ModI -> Printf.fprintf ppf "ModI" + | PowI -> Printf.fprintf ppf "PowI" + | AndI -> Printf.fprintf ppf "AndI" + | OrI -> Printf.fprintf ppf "OrI" + | EqI -> Printf.fprintf ppf "EqI" + | LessI -> Printf.fprintf ppf "LessI" + | LessEqI -> Printf.fprintf ppf "LessEqI" + | MoreI -> Printf.fprintf ppf "MoreI" + | MoreEqI -> Printf.fprintf ppf "MoreEqI" + and pp_urop (ppf: out_channel) (v: urop) : unit = + match v with + Not -> Printf.fprintf ppf "Nop" + | Copy -> Printf.fprintf ppf "Copy" + | Rand -> Printf.fprintf ppf "Rand" + in + pp_t ppf v + + let pplist (ppf: out_channel) (l: t list) : unit = + List.iter (fun x -> pp ppf x; Printf.printf "; ") l +end + +module RISCCfg = Cfg.Make(RISCSimpleStatements) + +let globalcounter = ref 0 +module RegisterMap = struct + type m = { + assignments: int Types.VariableMap.t + } + let _get_opt_register (x: Types.variable) (m: m) : RISCSimpleStatements.register option = + Option.bind + (Types.VariableMap.find_opt x m.assignments) + (fun (x: int) : RISCSimpleStatements.register option -> Some {index = x}) + + let get_or_set_register (x: Types.variable) (m: m) : RISCSimpleStatements.register * m = + match Types.VariableMap.find_opt x m.assignments with + None -> + ( globalcounter := !globalcounter + 1; + ({index = !globalcounter}, {assignments = Types.VariableMap.add x !globalcounter m.assignments}) ) + | Some i -> ({index = i}, m) + + let get_fresh_register (m: m) : RISCSimpleStatements.register * m * Types.variable = + globalcounter := !globalcounter + 1; + let freshvariable = string_of_int !globalcounter in + ({index = !globalcounter}, + {assignments = Types.VariableMap.add freshvariable !globalcounter m.assignments}, + freshvariable) + + let empty : m = + {assignments = Types.VariableMap.empty} + + (* let pp (ppx) (m: m) : unit = *) + (* Printf.fprintf ppx "RegisterMap contents: "; *) + (* List.iter (fun (n, v) -> Printf.fprintf ppx "%s -> %d, " n v) (Types.VariableMap.to_list m.assignments); *) + (* Printf.fprintf ppx "\n"; *) +end + + +let rec c_ss_t + (ss: CfgImp.SimpleStatements.t) + (m: RegisterMap.m) + (convertedcode: RISCSimpleStatements.t list) + : RISCSimpleStatements.t list * RegisterMap.m = + match ss with + SimpleSkip -> (Nop :: convertedcode, m) + | SimpleAssignment (v, sa) -> ( + let r1, m = RegisterMap.get_or_set_register v m in + c_ss_sa sa m convertedcode r1 + ) + | SimpleGuard (b) -> ( + let returnreg, m, _returnregvar = RegisterMap.get_fresh_register m in + c_ss_sb b m convertedcode returnreg + ) +and c_ss_sb + (ss: CfgImp.SimpleStatements.simpleBoolean) + (m: RegisterMap.m) + (convertedcode: RISCSimpleStatements.t list) + (register: RISCSimpleStatements.register) + : RISCSimpleStatements.t list * RegisterMap.m = + match ss with + SimpleBoolean (b) -> ( + let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + if b then + (LoadI (partialresreg, 1) :: convertedcode, m) + else + (LoadI (partialresreg, 0) :: convertedcode, m) + ) + | SimpleBAnd (b1, b2) -> ( + match (b1, b2) with + | (SimpleBoolean (true), b) + | (b, SimpleBoolean (true)) -> ( + c_ss_sb b m convertedcode register + ) + | (SimpleBoolean (false), _) + | (_, SimpleBoolean (false)) -> ( + (LoadI (register, 0) :: convertedcode, m) + ) + | (_, _) -> ( + let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in + let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sb b1 m convertedcode partialresreg1 in + let convertedcode, m = c_ss_sb b2 m convertedcode partialresreg2 in + (BRegOp (And, partialresreg1, partialresreg2, register) :: convertedcode, m) + ) + ) + | SimpleBOr (b1, b2) -> ( + match (b1, b2) with + | (SimpleBoolean (false), b) + | (b, SimpleBoolean (false)) -> ( + c_ss_sb b m convertedcode register + ) + | (SimpleBoolean (true), _) + | (_, SimpleBoolean (true)) -> ( + (LoadI (register, 1) :: convertedcode, m) + ) + | (_, _) -> ( + let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in + let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sb b1 m convertedcode partialresreg1 in + let convertedcode, m = c_ss_sb b2 m convertedcode partialresreg2 in + (BRegOp (Or, partialresreg1, partialresreg2, register) :: convertedcode, m) + ) + ) + | SimpleBNot (b) -> ( + match (b) with + | SimpleBoolean (b) ->( + if b then + (LoadI (register, 0) :: convertedcode, m) + else + (LoadI (register, 1) :: convertedcode, m) + ) + | _ -> ( + let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sb b m convertedcode partialresreg in + (URegOp (Not, partialresreg, register) :: convertedcode, m) + ) + ) + | SimpleBCmp (a1, a2) -> ( + match (a1, a2) with + | (SimpleInteger (i), a) + | (a, SimpleInteger (i)) -> ( + let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a m convertedcode partialresreg in + (BImmOp (EqI, partialresreg, i, register) :: convertedcode, m) + ) + | (_, _) -> ( + let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in + let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a1 m convertedcode partialresreg1 in + let convertedcode, m = c_ss_sa a2 m convertedcode partialresreg2 in + (BRegOp (Eq, partialresreg1, partialresreg2, register) :: convertedcode, m) + ) + ) + | SimpleBCmpLess (a1, a2) -> ( + match (a1, a2) with + | (SimpleInteger (i), a) -> ( + let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a m convertedcode partialresreg in + (BImmOp (MoreI, partialresreg, i, register) :: convertedcode, m) + ) + | (a, SimpleInteger (i)) -> ( + let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a m convertedcode partialresreg in + (BImmOp (LessI, partialresreg, i, register) :: convertedcode, m) + ) + | (_, _) -> ( + let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in + let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a1 m convertedcode partialresreg1 in + let convertedcode, m = c_ss_sa a2 m convertedcode partialresreg2 in + (BRegOp (Less, partialresreg1, partialresreg2, register) :: convertedcode, m) + ) + ) + | SimpleBCmpLessEq (a1, a2) -> ( + match (a1, a2) with + | (SimpleInteger (i), a) -> ( + let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a m convertedcode partialresreg in + (BImmOp (MoreEqI, partialresreg, i, register) :: convertedcode, m) + ) + | (a, SimpleInteger (i)) -> ( + let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a m convertedcode partialresreg in + (BImmOp (LessEqI, partialresreg, i, register) :: convertedcode, m) + ) + | (_, _) -> ( + let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in + let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a1 m convertedcode partialresreg1 in + let convertedcode, m = c_ss_sa a2 m convertedcode partialresreg2 in + (BRegOp (LessEq, partialresreg1, partialresreg2, register) :: convertedcode, m) + ) + ) + | SimpleBCmpGreater (a1, a2) -> ( + match (a1, a2) with + | (SimpleInteger (i), a) -> ( + let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a m convertedcode partialresreg in + (BImmOp (LessI, partialresreg, i, register) :: convertedcode, m) + ) + | (a, SimpleInteger (i)) -> ( + let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a m convertedcode partialresreg in + (BImmOp (MoreI, partialresreg, i, register) :: convertedcode, m) + ) + | (_, _) -> ( + let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in + let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a1 m convertedcode partialresreg1 in + let convertedcode, m = c_ss_sa a2 m convertedcode partialresreg2 in + (BRegOp (More, partialresreg1, partialresreg2, register) :: convertedcode, m) + ) + ) + | SimpleBCmpGreaterEq (a1, a2) -> ( + match (a1, a2) with + | (SimpleInteger (i), a) -> ( + let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a m convertedcode partialresreg in + (BImmOp (LessEqI, partialresreg, i, register) :: convertedcode, m) + ) + | (a, SimpleInteger (i)) -> ( + let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a m convertedcode partialresreg in + (BImmOp (MoreEqI, partialresreg, i, register) :: convertedcode, m) + ) + | (_, _) -> ( + let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in + let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a1 m convertedcode partialresreg1 in + let convertedcode, m = c_ss_sa a2 m convertedcode partialresreg2 in + (BRegOp (MoreEq, partialresreg1, partialresreg2, register) :: convertedcode, m) + ) + ) + +and c_ss_sa + (ss: CfgImp.SimpleStatements.simpleArithmetic) + (m: RegisterMap.m) + (convertedcode: RISCSimpleStatements.t list) + (register: RISCSimpleStatements.register) + : RISCSimpleStatements.t list * RegisterMap.m = + match ss with + SimpleVariable (x) -> ( + let r1, m = RegisterMap.get_or_set_register x m in + (Load (r1, register) :: convertedcode, m) + ) + | SimpleInteger (i) -> (LoadI (register, i) :: convertedcode, m) + | SimplePlus (a1, a2) -> ( + match (a1, a2) with + | (SimpleInteger (i), a) + | (a, SimpleInteger (i)) -> ( + let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a m convertedcode partialresreg in + (BImmOp (AddI, partialresreg, i, register) :: convertedcode, m) + ) + | (_, _) -> ( + let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in + let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a1 m convertedcode partialresreg1 in + let convertedcode, m = c_ss_sa a2 m convertedcode partialresreg2 in + (BRegOp (Add, partialresreg1, partialresreg2, register) :: convertedcode, m) + ) + ) + | SimpleMinus (a1, a2) -> ( + match (a1, a2) with + | (a, SimpleInteger (i)) -> ( + let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a m convertedcode partialresreg in + (BImmOp (SubI, partialresreg, i, register) :: convertedcode, m) + ) + | (_, _) -> ( + let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in + let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a1 m convertedcode partialresreg1 in + let convertedcode, m = c_ss_sa a2 m convertedcode partialresreg2 in + (BRegOp (Sub, partialresreg1, partialresreg2, register) :: convertedcode, m) + ) + ) + | SimpleTimes (a1, a2) -> ( + match (a1, a2) with + | (SimpleInteger (i), a) + | (a, SimpleInteger (i)) -> ( + let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a m convertedcode partialresreg in + (BImmOp (MultI, partialresreg, i, register) :: convertedcode, m) + ) + | (_, _) -> ( + let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in + let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a1 m convertedcode partialresreg1 in + let convertedcode, m = c_ss_sa a2 m convertedcode partialresreg2 in + (BRegOp (Mult, partialresreg1, partialresreg2, register) :: convertedcode, m) + ) + ) + | SimpleDivision (a1, a2) -> ( + match (a1, a2) with + | (a, SimpleInteger (i)) -> ( + let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a m convertedcode partialresreg in + (BImmOp (DivI, partialresreg, i, register) :: convertedcode, m) + ) + | (_, _) -> ( + let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in + let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a1 m convertedcode partialresreg1 in + let convertedcode, m = c_ss_sa a2 m convertedcode partialresreg2 in + (BRegOp (Div, partialresreg1, partialresreg2, register) :: convertedcode, m) + ) + ) + | SimpleModulo (a1, a2) -> ( + match (a1, a2) with + | (a, SimpleInteger (i)) -> ( + let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a m convertedcode partialresreg in + (BImmOp (ModI, partialresreg, i, register) :: convertedcode, m) + ) + | (_, _) -> ( + let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in + let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a1 m convertedcode partialresreg1 in + let convertedcode, m = c_ss_sa a2 m convertedcode partialresreg2 in + (BRegOp (Mod, partialresreg1, partialresreg2, register) :: convertedcode, m) + ) + ) + | SimplePower (a1, a2) -> ( + match (a1, a2) with + | (a, SimpleInteger (i)) -> ( + let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a m convertedcode partialresreg in + (BImmOp (PowI, partialresreg, i, register) :: convertedcode, m) + ) + | (_, _) -> ( + let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in + let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a1 m convertedcode partialresreg1 in + let convertedcode, m = c_ss_sa a2 m convertedcode partialresreg2 in + (BRegOp (Pow, partialresreg1, partialresreg2, register) :: convertedcode, m) + ) + ) + | SimplePowerMod (_a1, _a2, _a3) -> failwith "Not implemented Powermod" + | SimpleRand (a) -> ( + let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a m convertedcode partialresreg in + (URegOp (Rand, partialresreg, register) :: convertedcode, m) + ) + +let convert_ss + (m: RegisterMap.m) + (value: CfgImp.SimpleStatements.t list) + (node: Cfg.Node.t) + (risccode: RISCSimpleStatements.t list Cfg.NodeMap.t) + : RISCSimpleStatements.t list Cfg.NodeMap.t * RegisterMap.m = + let instructions, m = List.fold_right (fun code (convertedcode, m) -> + c_ss_t code m convertedcode) value ([], m) in + (Cfg.NodeMap.add node instructions risccode, m) + +let helper (c: CfgImp.SimpleStatements.t list Cfg.NodeMap.t) (m: RegisterMap.m) : RISCSimpleStatements.t list Cfg.NodeMap.t = + let risccode, _ = Cfg.NodeMap.fold (fun node value (risccode, m) -> convert_ss m value node risccode) c (Cfg.NodeMap.empty, m) in + risccode + +let convert_content (c: CfgImp.SimpleStatements.t list Cfg.NodeMap.t) : RISCSimpleStatements.t list Cfg.NodeMap.t = + helper c RegisterMap.empty + +let convert (prg: CfgImp.SSCfg.t) : RISCCfg.t = + match prg with + { empty: bool; + nodes: Cfg.NodeSet.t; + edges: (Cfg.Node.t * (Cfg.Node.t option)) Cfg.NodeMap.t; + reverseEdges: (Cfg.Node.t list) Cfg.NodeMap.t; + inputVal: int option; + inputOutputVar: (string * string) option; + initial: Cfg.Node.t option; + terminal: Cfg.Node.t option; + content: CfgImp.SimpleStatements.t list Cfg.NodeMap.t + } -> { empty = empty; + nodes = nodes; + edges = edges; + reverseEdges = reverseEdges; + inputVal = inputVal; + inputOutputVar = inputOutputVar; + initial = initial; + terminal = terminal; + content = convert_content content; + } diff --git a/lib/miniImp/CfgRISC.mli b/lib/miniImp/CfgRISC.mli new file mode 100644 index 0000000..4d1b650 --- /dev/null +++ b/lib/miniImp/CfgRISC.mli @@ -0,0 +1,53 @@ +module RISCSimpleStatements : sig + type register = { + index: int + } + + type t = + | Nop + | BRegOp of brop * register * register * register + | BImmOp of biop * register * int * register + | URegOp of urop * register * register + | Load of register * register + | LoadI of register * int + | Store of register * register + and brop = + | Add + | Sub + | Mult + | Div + | Mod + | Pow + | And + | Or + | Eq + | Less + | LessEq + | More + | MoreEq + and biop = + | AddI + | SubI + | MultI + | DivI + | ModI + | PowI + | AndI + | OrI + | EqI + | LessI + | LessEqI + | MoreI + | MoreEqI + and urop = + | Not + | Copy + | Rand + + val pp : out_channel -> t -> unit + val pplist : out_channel -> t list -> unit +end + +module RISCCfg : Cfg.C with type elt = RISCSimpleStatements.t + +val convert : CfgImp.SSCfg.t -> RISCCfg.t diff --git a/lib/miniImp/dune b/lib/miniImp/dune index ffeaddc..25890dd 100644 --- a/lib/miniImp/dune +++ b/lib/miniImp/dune @@ -10,7 +10,7 @@ (library (name miniImp) (public_name miniImp) - (modules Lexer Parser Types Semantics CfgImp) + (modules Lexer Parser Types Semantics CfgImp CfgRISC) (libraries cfg utility menhirLib)) (include_subdirs qualified) From 7465c2daff176ad577ca8831c518a069e53effd0 Mon Sep 17 00:00:00 2001 From: elvis Date: Thu, 28 Nov 2024 00:35:30 +0100 Subject: [PATCH 03/29] Optimizing risc instructions for arithmetic expressions --- lib/miniImp/CfgRISC.ml | 162 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 162 insertions(+) diff --git a/lib/miniImp/CfgRISC.ml b/lib/miniImp/CfgRISC.ml index 7c6d42e..b6f9429 100644 --- a/lib/miniImp/CfgRISC.ml +++ b/lib/miniImp/CfgRISC.ml @@ -322,12 +322,29 @@ and c_ss_sa | SimpleInteger (i) -> (LoadI (register, i) :: convertedcode, m) | SimplePlus (a1, a2) -> ( match (a1, a2) with + | (SimpleInteger (i), SimpleVariable (x)) + | (SimpleVariable (x), SimpleInteger (i)) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + (BImmOp (AddI, xreg, i, register) :: convertedcode, m) + ) | (SimpleInteger (i), a) | (a, SimpleInteger (i)) -> ( let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in (BImmOp (AddI, partialresreg, i, register) :: convertedcode, m) ) + | (SimpleVariable (x), SimpleVariable (y)) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + let yreg, m = RegisterMap.get_or_set_register y m in + (BRegOp (Add, xreg, yreg, register) :: convertedcode, m) + ) + | (SimpleVariable (x), a) + | (a, SimpleVariable (x)) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a m convertedcode partialresreg in + (BRegOp (Add, partialresreg, xreg, register) :: convertedcode, m) + ) | (_, _) -> ( let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in @@ -338,11 +355,43 @@ and c_ss_sa ) | SimpleMinus (a1, a2) -> ( match (a1, a2) with + | (SimpleInteger (i), SimpleVariable (x)) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + (BRegOp (Sub, partialresreg, xreg, register) :: LoadI (partialresreg, i) :: convertedcode, m) + ) + | (SimpleVariable (x), SimpleInteger (i)) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + (BImmOp (SubI, xreg, i, register) :: convertedcode, m) + ) + | (SimpleInteger (i), a) -> ( + let partialresregi, m, _partialresvari = RegisterMap.get_fresh_register m in + let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a m convertedcode partialresreg in + (BRegOp (Sub, partialresregi, partialresreg, register) :: LoadI (partialresregi, i) :: convertedcode, m) + ) | (a, SimpleInteger (i)) -> ( let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in (BImmOp (SubI, partialresreg, i, register) :: convertedcode, m) ) + | (SimpleVariable (x), SimpleVariable (y)) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + let yreg, m = RegisterMap.get_or_set_register y m in + (BRegOp (Sub, xreg, yreg, register) :: convertedcode, m) + ) + | (SimpleVariable (x), a) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a m convertedcode partialresreg in + (BRegOp (Sub, xreg, partialresreg, register) :: convertedcode, m) + ) + | (a, SimpleVariable (x)) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a m convertedcode partialresreg in + (BRegOp (Sub, partialresreg, xreg, register) :: convertedcode, m) + ) | (_, _) -> ( let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in @@ -353,12 +402,29 @@ and c_ss_sa ) | SimpleTimes (a1, a2) -> ( match (a1, a2) with + | (SimpleInteger (i), SimpleVariable (x)) + | (SimpleVariable (x), SimpleInteger (i)) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + (BImmOp (MultI, xreg, i, register) :: convertedcode, m) + ) | (SimpleInteger (i), a) | (a, SimpleInteger (i)) -> ( let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in (BImmOp (MultI, partialresreg, i, register) :: convertedcode, m) ) + | (SimpleVariable (x), SimpleVariable (y)) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + let yreg, m = RegisterMap.get_or_set_register y m in + (BRegOp (Mult, xreg, yreg, register) :: convertedcode, m) + ) + | (SimpleVariable (x), a) + | (a, SimpleVariable (x)) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a m convertedcode partialresreg in + (BRegOp (Mult, partialresreg, xreg, register) :: convertedcode, m) + ) | (_, _) -> ( let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in @@ -369,11 +435,43 @@ and c_ss_sa ) | SimpleDivision (a1, a2) -> ( match (a1, a2) with + | (SimpleInteger (i), SimpleVariable (x)) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + (BRegOp (Div, partialresreg, xreg, register) :: LoadI (partialresreg, i) :: convertedcode, m) + ) + | (SimpleVariable (x), SimpleInteger (i)) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + (BImmOp (DivI, xreg, i, register) :: convertedcode, m) + ) + | (SimpleInteger (i), a) -> ( + let partialresregi, m, _partialresvari = RegisterMap.get_fresh_register m in + let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a m convertedcode partialresreg in + (BRegOp (Div, partialresregi, partialresreg, register) :: LoadI (partialresregi, i) :: convertedcode, m) + ) | (a, SimpleInteger (i)) -> ( let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in (BImmOp (DivI, partialresreg, i, register) :: convertedcode, m) ) + | (SimpleVariable (x), SimpleVariable (y)) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + let yreg, m = RegisterMap.get_or_set_register y m in + (BRegOp (Div, xreg, yreg, register) :: convertedcode, m) + ) + | (SimpleVariable (x), a) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a m convertedcode partialresreg in + (BRegOp (Div, xreg, partialresreg, register) :: convertedcode, m) + ) + | (a, SimpleVariable (x)) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a m convertedcode partialresreg in + (BRegOp (Div, partialresreg, xreg, register) :: convertedcode, m) + ) | (_, _) -> ( let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in @@ -384,11 +482,43 @@ and c_ss_sa ) | SimpleModulo (a1, a2) -> ( match (a1, a2) with + | (SimpleInteger (i), SimpleVariable (x)) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + (BRegOp (Mod, partialresreg, xreg, register) :: LoadI (partialresreg, i) :: convertedcode, m) + ) + | (SimpleVariable (x), SimpleInteger (i)) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + (BImmOp (ModI, xreg, i, register) :: convertedcode, m) + ) + | (SimpleInteger (i), a) -> ( + let partialresregi, m, _partialresvari = RegisterMap.get_fresh_register m in + let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a m convertedcode partialresreg in + (BRegOp (Mod, partialresregi, partialresreg, register) :: LoadI (partialresregi, i) :: convertedcode, m) + ) | (a, SimpleInteger (i)) -> ( let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in (BImmOp (ModI, partialresreg, i, register) :: convertedcode, m) ) + | (SimpleVariable (x), SimpleVariable (y)) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + let yreg, m = RegisterMap.get_or_set_register y m in + (BRegOp (Mod, xreg, yreg, register) :: convertedcode, m) + ) + | (SimpleVariable (x), a) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a m convertedcode partialresreg in + (BRegOp (Mod, xreg, partialresreg, register) :: convertedcode, m) + ) + | (a, SimpleVariable (x)) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a m convertedcode partialresreg in + (BRegOp (Mod, partialresreg, xreg, register) :: convertedcode, m) + ) | (_, _) -> ( let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in @@ -399,11 +529,43 @@ and c_ss_sa ) | SimplePower (a1, a2) -> ( match (a1, a2) with + | (SimpleInteger (i), SimpleVariable (x)) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + (BRegOp (Pow, partialresreg, xreg, register) :: LoadI (partialresreg, i) :: convertedcode, m) + ) + | (SimpleVariable (x), SimpleInteger (i)) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + (BImmOp (PowI, xreg, i, register) :: convertedcode, m) + ) + | (SimpleInteger (i), a) -> ( + let partialresregi, m, _partialresvari = RegisterMap.get_fresh_register m in + let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a m convertedcode partialresreg in + (BRegOp (Pow, partialresregi, partialresreg, register) :: LoadI (partialresregi, i) :: convertedcode, m) + ) | (a, SimpleInteger (i)) -> ( let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in (BImmOp (PowI, partialresreg, i, register) :: convertedcode, m) ) + | (SimpleVariable (x), SimpleVariable (y)) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + let yreg, m = RegisterMap.get_or_set_register y m in + (BRegOp (Pow, xreg, yreg, register) :: convertedcode, m) + ) + | (SimpleVariable (x), a) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a m convertedcode partialresreg in + (BRegOp (Pow, xreg, partialresreg, register) :: convertedcode, m) + ) + | (a, SimpleVariable (x)) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a m convertedcode partialresreg in + (BRegOp (Pow, partialresreg, xreg, register) :: convertedcode, m) + ) | (_, _) -> ( let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in From 08f0eb5c9193fef84cdf78f47717e2c2c4032b4b Mon Sep 17 00:00:00 2001 From: elvis Date: Thu, 28 Nov 2024 11:17:59 +0100 Subject: [PATCH 04/29] Optimizing risc instructions for boolean expressions --- lib/miniImp/CfgRISC.ml | 119 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 118 insertions(+), 1 deletion(-) diff --git a/lib/miniImp/CfgRISC.ml b/lib/miniImp/CfgRISC.ml index b6f9429..1642220 100644 --- a/lib/miniImp/CfgRISC.ml +++ b/lib/miniImp/CfgRISC.ml @@ -199,7 +199,7 @@ and c_ss_sb ) | SimpleBNot (b) -> ( match (b) with - | SimpleBoolean (b) ->( + | SimpleBoolean (b) -> ( if b then (LoadI (register, 0) :: convertedcode, m) else @@ -213,12 +213,29 @@ and c_ss_sb ) | SimpleBCmp (a1, a2) -> ( match (a1, a2) with + | (SimpleInteger (i), SimpleVariable (x)) + | (SimpleVariable (x), SimpleInteger (i)) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + (BImmOp (EqI, xreg, i, register) :: convertedcode, m) + ) | (SimpleInteger (i), a) | (a, SimpleInteger (i)) -> ( let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in (BImmOp (EqI, partialresreg, i, register) :: convertedcode, m) ) + | (SimpleVariable (x), SimpleVariable (y)) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + let yreg, m = RegisterMap.get_or_set_register y m in + (BRegOp (Eq, xreg, yreg, register) :: convertedcode, m) + ) + | (SimpleVariable (x), a) + | (a, SimpleVariable (x)) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a m convertedcode partialresreg in + (BRegOp (Eq, partialresreg, xreg, register) :: convertedcode, m) + ) | (_, _) -> ( let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in @@ -229,6 +246,14 @@ and c_ss_sb ) | SimpleBCmpLess (a1, a2) -> ( match (a1, a2) with + | (SimpleInteger (i), SimpleVariable (x)) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + (BImmOp (MoreI, xreg, i, register) :: convertedcode, m) + ) + | (SimpleVariable (x), SimpleInteger (i)) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + (BImmOp (LessI, xreg, i, register) :: convertedcode, m) + ) | (SimpleInteger (i), a) -> ( let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in @@ -239,6 +264,23 @@ and c_ss_sb let convertedcode, m = c_ss_sa a m convertedcode partialresreg in (BImmOp (LessI, partialresreg, i, register) :: convertedcode, m) ) + | (SimpleVariable (x), SimpleVariable (y)) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + let yreg, m = RegisterMap.get_or_set_register y m in + (BRegOp (Less, xreg, yreg, register) :: convertedcode, m) + ) + | (SimpleVariable (x), a) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a m convertedcode partialresreg in + (BRegOp (Less, xreg, partialresreg, register) :: convertedcode, m) + ) + | (a, SimpleVariable (x)) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a m convertedcode partialresreg in + (BRegOp (Less, partialresreg, xreg, register) :: convertedcode, m) + ) | (_, _) -> ( let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in @@ -249,6 +291,14 @@ and c_ss_sb ) | SimpleBCmpLessEq (a1, a2) -> ( match (a1, a2) with + | (SimpleInteger (i), SimpleVariable (x)) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + (BImmOp (MoreEqI, xreg, i, register) :: convertedcode, m) + ) + | (SimpleVariable (x), SimpleInteger (i)) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + (BImmOp (LessEqI, xreg, i, register) :: convertedcode, m) + ) | (SimpleInteger (i), a) -> ( let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in @@ -259,6 +309,23 @@ and c_ss_sb let convertedcode, m = c_ss_sa a m convertedcode partialresreg in (BImmOp (LessEqI, partialresreg, i, register) :: convertedcode, m) ) + | (SimpleVariable (x), SimpleVariable (y)) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + let yreg, m = RegisterMap.get_or_set_register y m in + (BRegOp (LessEq, xreg, yreg, register) :: convertedcode, m) + ) + | (SimpleVariable (x), a) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a m convertedcode partialresreg in + (BRegOp (LessEq, xreg, partialresreg, register) :: convertedcode, m) + ) + | (a, SimpleVariable (x)) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a m convertedcode partialresreg in + (BRegOp (LessEq, partialresreg, xreg, register) :: convertedcode, m) + ) | (_, _) -> ( let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in @@ -269,6 +336,14 @@ and c_ss_sb ) | SimpleBCmpGreater (a1, a2) -> ( match (a1, a2) with + | (SimpleInteger (i), SimpleVariable (x)) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + (BImmOp (LessI, xreg, i, register) :: convertedcode, m) + ) + | (SimpleVariable (x), SimpleInteger (i)) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + (BImmOp (MoreI, xreg, i, register) :: convertedcode, m) + ) | (SimpleInteger (i), a) -> ( let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in @@ -279,6 +354,23 @@ and c_ss_sb let convertedcode, m = c_ss_sa a m convertedcode partialresreg in (BImmOp (MoreI, partialresreg, i, register) :: convertedcode, m) ) + | (SimpleVariable (x), SimpleVariable (y)) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + let yreg, m = RegisterMap.get_or_set_register y m in + (BRegOp (More, xreg, yreg, register) :: convertedcode, m) + ) + | (SimpleVariable (x), a) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a m convertedcode partialresreg in + (BRegOp (More, xreg, partialresreg, register) :: convertedcode, m) + ) + | (a, SimpleVariable (x)) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a m convertedcode partialresreg in + (BRegOp (More, partialresreg, xreg, register) :: convertedcode, m) + ) | (_, _) -> ( let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in @@ -289,6 +381,14 @@ and c_ss_sb ) | SimpleBCmpGreaterEq (a1, a2) -> ( match (a1, a2) with + | (SimpleInteger (i), SimpleVariable (x)) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + (BImmOp (LessEqI, xreg, i, register) :: convertedcode, m) + ) + | (SimpleVariable (x), SimpleInteger (i)) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + (BImmOp (MoreEqI, xreg, i, register) :: convertedcode, m) + ) | (SimpleInteger (i), a) -> ( let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in @@ -299,6 +399,23 @@ and c_ss_sb let convertedcode, m = c_ss_sa a m convertedcode partialresreg in (BImmOp (MoreEqI, partialresreg, i, register) :: convertedcode, m) ) + | (SimpleVariable (x), SimpleVariable (y)) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + let yreg, m = RegisterMap.get_or_set_register y m in + (BRegOp (MoreEq, xreg, yreg, register) :: convertedcode, m) + ) + | (SimpleVariable (x), a) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a m convertedcode partialresreg in + (BRegOp (MoreEq, xreg, partialresreg, register) :: convertedcode, m) + ) + | (a, SimpleVariable (x)) -> ( + let xreg, m = RegisterMap.get_or_set_register x m in + let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let convertedcode, m = c_ss_sa a m convertedcode partialresreg in + (BRegOp (MoreEq, partialresreg, xreg, register) :: convertedcode, m) + ) | (_, _) -> ( let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in From 52cd7e7e1361b3e81ae59beb787bb809fbbf8585 Mon Sep 17 00:00:00 2001 From: elvis Date: Sun, 1 Dec 2024 12:55:20 +0100 Subject: [PATCH 05/29] RISC code output TODO: fix wrong order for conversion in cfgrisc --- bin/main.ml | 17 ++- lib/miniImp/CfgRISC.ml | 13 +-- lib/miniImp/RISC.ml | 244 ++++++++++++++++++++++++++++++++++++++++ lib/miniImp/RISC.mli | 56 +++++++++ lib/miniImp/dune | 2 +- lib/utility/utility.ml | 9 ++ lib/utility/utility.mli | 2 + 7 files changed, 332 insertions(+), 11 deletions(-) create mode 100644 lib/miniImp/RISC.ml create mode 100644 lib/miniImp/RISC.mli diff --git a/bin/main.ml b/bin/main.ml index 4b5bd64..8f8e14a 100644 --- a/bin/main.ml +++ b/bin/main.ml @@ -1,11 +1,22 @@ open MiniImp let () = + (* let program = "def main with input x output y as *) + (* x := 2; *) + (* if y < 0 then ( *) + (* y := x + 3; *) + (* x := y; *) + (* ) else *) + (* x := 1 - y;" in *) + let program = "def main with input x output y as x := 2; if y < 0 then ( y := x + 3; - x := y; + if x > 0 then + x := y; + else + x := y + 1; ) else x := 1 - y;" in @@ -19,4 +30,6 @@ let () = let convertedrisccfg = CfgRISC.convert convertedcfg in - Printf.printf "%a" CfgRISC.RISCCfg.pp convertedrisccfg + let risc = RISC.convert convertedrisccfg in + + Printf.printf "%a" RISC.RISCAssembly.pp risc diff --git a/lib/miniImp/CfgRISC.ml b/lib/miniImp/CfgRISC.ml index 1642220..0a71b79 100644 --- a/lib/miniImp/CfgRISC.ml +++ b/lib/miniImp/CfgRISC.ml @@ -47,13 +47,13 @@ module RISCSimpleStatements = struct let pp (ppf: out_channel) (v: t) : unit = let rec pp_t (ppf: out_channel) (v: t) : unit = match v with - Nop -> Printf.fprintf ppf "nop" + Nop -> Printf.fprintf ppf "Nop" | BRegOp (b, r1, r2, r3) -> Printf.fprintf ppf "%a r%d r%d => r%d" pp_brop b r1.index r2.index r3.index | BImmOp (b, r1, i, r3) -> Printf.fprintf ppf "%a r%d %d => r%d" pp_biop b r1.index i r3.index | URegOp (u, r1, r2) -> Printf.fprintf ppf "%a r%d => r%d" pp_urop u r1.index r2.index - | Load (r1, r2) -> Printf.fprintf ppf "load r%d => r%d" r1.index r2.index - | LoadI (r2, i) -> Printf.fprintf ppf "loadi %d => r%d" i r2.index - | Store (r1, r2) -> Printf.fprintf ppf "store r%d => r%d" r1.index r2.index + | Load (r1, r2) -> Printf.fprintf ppf "Load r%d => r%d" r1.index r2.index + | LoadI (r2, i) -> Printf.fprintf ppf "LoadI %d => r%d" i r2.index + | Store (r1, r2) -> Printf.fprintf ppf "Store r%d => r%d" r1.index r2.index and pp_brop (ppf: out_channel) (v: brop) : unit = match v with Add -> Printf.fprintf ppf "Add" @@ -712,9 +712,6 @@ let helper (c: CfgImp.SimpleStatements.t list Cfg.NodeMap.t) (m: RegisterMap.m) let risccode, _ = Cfg.NodeMap.fold (fun node value (risccode, m) -> convert_ss m value node risccode) c (Cfg.NodeMap.empty, m) in risccode -let convert_content (c: CfgImp.SimpleStatements.t list Cfg.NodeMap.t) : RISCSimpleStatements.t list Cfg.NodeMap.t = - helper c RegisterMap.empty - let convert (prg: CfgImp.SSCfg.t) : RISCCfg.t = match prg with { empty: bool; @@ -734,5 +731,5 @@ let convert (prg: CfgImp.SSCfg.t) : RISCCfg.t = inputOutputVar = inputOutputVar; initial = initial; terminal = terminal; - content = convert_content content; + content = helper content RegisterMap.empty; } diff --git a/lib/miniImp/RISC.ml b/lib/miniImp/RISC.ml new file mode 100644 index 0000000..f59f4b7 --- /dev/null +++ b/lib/miniImp/RISC.ml @@ -0,0 +1,244 @@ +let globalCounterLabel = ref 0 + +let nextLabel () : string = + globalCounterLabel := !globalCounterLabel + 1; + "l" ^ (string_of_int !globalCounterLabel) + +module RISCAssembly = struct + type register = { + index : int + } + + type label = + string + + type risci = + | Nop + | BRegOp of brop * register * register * register + | BImmOp of biop * register * int * register + | URegOp of urop * register * register + | Load of register * register + | LoadI of register * int + | Store of register * register + | Jump of label + | CJump of register * label * label + | Label of label + and brop = + | Add + | Sub + | Mult + | Div + | Mod + | Pow + | And + | Or + | Eq + | Less + | LessEq + | More + | MoreEq + and biop = + | AddI + | SubI + | MultI + | DivI + | ModI + | PowI + | AndI + | OrI + | EqI + | LessI + | LessEqI + | MoreI + | MoreEqI + and urop = + | Not + | Copy + | Rand + + type t = risci list + + let pp (ppf: out_channel) (t: t) : unit = + let rec pp_risci (ppf: out_channel) (v: risci) : unit = + match v with + Nop -> Printf.fprintf ppf "\tNop\n" + | BRegOp (b, r1, r2, r3) -> Printf.fprintf ppf "\t%a r%d r%d => r%d\n" pp_brop b r1.index r2.index r3.index + | BImmOp (b, r1, i, r3) -> Printf.fprintf ppf "\t%a r%d %d => r%d\n" pp_biop b r1.index i r3.index + | URegOp (u, r1, r2) -> Printf.fprintf ppf "\t%a r%d => r%d\n" pp_urop u r1.index r2.index + | Load (r1, r2) -> Printf.fprintf ppf "\tLoad r%d => r%d\n" r1.index r2.index + | LoadI (r2, i) -> Printf.fprintf ppf "\tLoadI %d => r%d\n" i r2.index + | Store (r1, r2) -> Printf.fprintf ppf "\tStore r%d => r%d\n" r1.index r2.index + | Jump (label) -> Printf.fprintf ppf "\tJump %s\n" label + | CJump (r, l1, l2) -> Printf.fprintf ppf "\tCJump r%d => %s, %s\n" r.index l1 l2 + | Label (label) -> Printf.fprintf ppf "%s:" label + and pp_brop (ppf: out_channel) (v: brop) : unit = + match v with + Add -> Printf.fprintf ppf "Add" + | Sub -> Printf.fprintf ppf "Sub" + | Mult -> Printf.fprintf ppf "Mult" + | Div -> Printf.fprintf ppf "Div" + | Mod -> Printf.fprintf ppf "Mod" + | Pow -> Printf.fprintf ppf "Pow" + | And -> Printf.fprintf ppf "And" + | Or -> Printf.fprintf ppf "Or" + | Eq -> Printf.fprintf ppf "Eq" + | Less -> Printf.fprintf ppf "Less" + | LessEq -> Printf.fprintf ppf "LessEq" + | More -> Printf.fprintf ppf "More" + | MoreEq -> Printf.fprintf ppf "MoreEq" + and pp_biop (ppf: out_channel) (v: biop) : unit = + match v with + AddI -> Printf.fprintf ppf "AddI" + | SubI -> Printf.fprintf ppf "SubI" + | MultI -> Printf.fprintf ppf "MultI" + | DivI -> Printf.fprintf ppf "DivI" + | ModI -> Printf.fprintf ppf "ModI" + | PowI -> Printf.fprintf ppf "PowI" + | AndI -> Printf.fprintf ppf "AndI" + | OrI -> Printf.fprintf ppf "OrI" + | EqI -> Printf.fprintf ppf "EqI" + | LessI -> Printf.fprintf ppf "LessI" + | LessEqI -> Printf.fprintf ppf "LessEqI" + | MoreI -> Printf.fprintf ppf "MoreI" + | MoreEqI -> Printf.fprintf ppf "MoreEqI" + and pp_urop (ppf: out_channel) (v: urop) : unit = + match v with + Not -> Printf.fprintf ppf "Nop" + | Copy -> Printf.fprintf ppf "Copy" + | Rand -> Printf.fprintf ppf "Rand" + in + List.iter (pp_risci ppf) t +end + +let convert_cfgrisc_risci (i: CfgRISC.RISCSimpleStatements.t list) : (RISCAssembly.t) = + let rec helper (i: CfgRISC.RISCSimpleStatements.t) : RISCAssembly.risci = + match i with + | Nop -> Nop + | BRegOp (brop, r1, r2, r3) -> BRegOp (helper_brop brop, + {index = r1.index}, + {index = r2.index}, + {index = r3.index}) + | BImmOp (biop, r1, imm, r3) -> BImmOp (helper_biop biop, + {index = r1.index}, + imm, + {index = r3.index}) + | URegOp (urop, r1, r3) -> URegOp (helper_urop urop, + {index = r1.index}, + {index = r3.index}) + | Load (r1, r3) -> Load ({index = r1.index}, + {index = r3.index}) + | LoadI (r3, imm) -> LoadI ({index = r3.index}, + imm) + | Store (r1, r3) -> Store ({index = r1.index}, + {index = r3.index}) + and helper_brop (brop: CfgRISC.RISCSimpleStatements.brop) : RISCAssembly.brop = + match brop with + | Add -> Add + | Sub -> Sub + | Mult -> Mult + | Div -> Div + | Mod -> Mod + | Pow -> Pow + | And -> And + | Or -> Or + | Eq -> Eq + | Less -> Less + | LessEq -> LessEq + | More -> More + | MoreEq -> MoreEq + and helper_biop (biop: CfgRISC.RISCSimpleStatements.biop) : RISCAssembly.biop = + match biop with + | AddI -> AddI + | SubI -> SubI + | MultI -> MultI + | DivI -> DivI + | ModI -> ModI + | PowI -> PowI + | AndI -> AndI + | OrI -> OrI + | EqI -> EqI + | LessI -> LessI + | LessEqI -> LessEqI + | MoreI -> MoreI + | MoreEqI -> MoreEqI + and helper_urop (urop: CfgRISC.RISCSimpleStatements.urop) : RISCAssembly.urop = + match urop with + | Not -> Not + | Copy -> Copy + | Rand -> Rand + in + List.map helper i + +let nextCommonSuccessor (prg: CfgRISC.RISCCfg.t) (node1: Cfg.Node.t) (node2: Cfg.Node.t) : Cfg.Node.t option = + (* Assume the two input nodes are the two branches of an if then else + statement, then create the two lists that represent the runs until the + terminal node by choosing always the false statement in guard statements + (if the guard is for a while statement it gets ignored, if it is for an + if then else it chooses one of the branches) then find the first common + node in the lists + *) + let rec walk (node: Cfg.Node.t) : Cfg.Node.t list = + node :: match Cfg.NodeMap.find_opt node prg.edges with + | None -> [] + | Some (edge, None) -> (walk edge) + | Some (_, Some edge) -> (walk edge) + in + + let list1 = walk node1 in + let list2 = walk node2 in + let common = List.filter (fun x -> List.mem x list2) list1 in + match common with + [] -> None + | a::_ -> Some a + + +let rec helper (prg: CfgRISC.RISCCfg.t) (currentnode: Cfg.Node.t) (alreadyVisited: Cfg.Node.t list) : RISCAssembly.t * Cfg.Node.t list = + if List.mem currentnode alreadyVisited then + ([], alreadyVisited) + else ( + let nextnodes = (Cfg.NodeMap.find_opt currentnode prg.edges) in + let currentcode = (Cfg.NodeMap.find currentnode prg.content |> convert_cfgrisc_risci) in + match nextnodes with + | Some (nextnode1, None) -> + let res, vis = (helper prg nextnode1) (currentnode :: alreadyVisited) in + (currentcode @ res, vis) + | Some (nextnode1, Some nextnode2) -> ( + let ncs = nextCommonSuccessor prg nextnode1 nextnode2 in + match ncs with + | None -> (* should never happen since the terminal node should always be + rechable *) + failwith "Topology got a little mixed up" + | Some ncs -> ( + if (ncs.id = nextnode2.id) + then (* while or for loop *) + failwith "Not implemented" + else (* if branches *) + let label1 = nextLabel () in + let label2 = nextLabel () in + let label3 = nextLabel () in + + let res1, vis1 = (helper prg nextnode1 (currentnode :: ncs :: alreadyVisited)) in + let res2, _ = (helper prg nextnode2 vis1) in + let res3, vis3 = (helper prg ncs (currentnode :: alreadyVisited)) in + match List.nth currentcode ((List.length currentcode) - 1) with + | BRegOp (_, _, _, r) + | BImmOp (_, _, _, r) + | URegOp (_, _, r) + | Load (_, r) + | LoadI (r, _) -> (currentcode @ + ([CJump (r, label1, label2); Label label1] : RISCAssembly.t) @ + res1 @ + ([Jump label3; Label label2] : RISCAssembly.t) @ + res2 @ + ([Label label3] : RISCAssembly.t) @ + res3 + , vis3) + | _ -> failwith "Missing instruction" + ) + ) + | None -> (currentcode, currentnode :: alreadyVisited) + ) + +let convert (prg: CfgRISC.RISCCfg.t) : RISCAssembly.t = + let res, _ = helper prg (Option.get prg.initial) [] in + res diff --git a/lib/miniImp/RISC.mli b/lib/miniImp/RISC.mli new file mode 100644 index 0000000..d12a017 --- /dev/null +++ b/lib/miniImp/RISC.mli @@ -0,0 +1,56 @@ +module RISCAssembly : sig + type register = { + index : int + } + + type label + type risci = + | Nop + | BRegOp of brop * register * register * register + | BImmOp of biop * register * int * register + | URegOp of urop * register * register + | Load of register * register + | LoadI of register * int + | Store of register * register + | Jump of label + | CJump of register * label * label + | Label of label + and brop = + | Add + | Sub + | Mult + | Div + | Mod + | Pow + | And + | Or + | Eq + | Less + | LessEq + | More + | MoreEq + and biop = + | AddI + | SubI + | MultI + | DivI + | ModI + | PowI + | AndI + | OrI + | EqI + | LessI + | LessEqI + | MoreI + | MoreEqI + and urop = + | Not + | Copy + | Rand + + type t = risci list + + val pp : out_channel -> t -> unit +end + +val convert : CfgRISC.RISCCfg.t -> RISCAssembly.t diff --git a/lib/miniImp/dune b/lib/miniImp/dune index 25890dd..48684a1 100644 --- a/lib/miniImp/dune +++ b/lib/miniImp/dune @@ -10,7 +10,7 @@ (library (name miniImp) (public_name miniImp) - (modules Lexer Parser Types Semantics CfgImp CfgRISC) + (modules Lexer Parser Types Semantics CfgImp CfgRISC RISC) (libraries cfg utility menhirLib)) (include_subdirs qualified) diff --git a/lib/utility/utility.ml b/lib/utility/utility.ml index d16e044..4cd3ed2 100644 --- a/lib/utility/utility.ml +++ b/lib/utility/utility.ml @@ -11,3 +11,12 @@ let rec powmod a d = function | n -> let b = (powmod a d (n / 2)) mod d in (((b * b) mod d) * (if n mod 2 = 0 then 1 else a)) mod d + +let rec fromIntToString (alphabet: string) (x: int) : string = + let base = String.length alphabet in + if x < 0 then + "" + else if x < base then + String.get alphabet x |> String.make 1 + else + (fromIntToString (alphabet) (x/base - 1)) ^ (String.get alphabet (x mod base) |> String.make 1) diff --git a/lib/utility/utility.mli b/lib/utility/utility.mli index c5f2cfb..f7cf347 100644 --- a/lib/utility/utility.mli +++ b/lib/utility/utility.mli @@ -1,3 +1,5 @@ val pow : int -> int -> int val powmod : int -> int -> int -> int + +val fromIntToString : string -> int -> string From 15356aa5a9b9bd6d059a3b8555a1f6a6f4548a40 Mon Sep 17 00:00:00 2001 From: elvis Date: Mon, 2 Dec 2024 00:45:24 +0100 Subject: [PATCH 06/29] Fixes wrong order for conversion --- lib/cfg/Cfg.ml | 14 ++- lib/cfg/Cfg.mli | 7 +- lib/miniImp/CfgImp.ml | 17 ++- lib/miniImp/CfgRISC.ml | 235 ++++++++++++++++++++++------------------- lib/miniImp/RISC.ml | 6 +- 5 files changed, 163 insertions(+), 116 deletions(-) diff --git a/lib/cfg/Cfg.ml b/lib/cfg/Cfg.ml index 70e26a9..f9b6fb8 100644 --- a/lib/cfg/Cfg.ml +++ b/lib/cfg/Cfg.ml @@ -18,7 +18,15 @@ module Node = struct end ;; -module NodeMap = Map.Make(Node) +module NodeMap = struct + include Map.Make(Node) + + let add_to_list_last x data m = + let add = function None -> Some [data] + | Some l -> Some (l @ [data]) in + update x add m +end + module NodeSet = Set.Make(Node) module type C = sig @@ -141,7 +149,7 @@ module Make(M: PrintableType) = struct | false -> let prevcfgterminal = Option.get cfg.terminal in { cfg with - content = (NodeMap.add_to_list + content = (NodeMap.add_to_list_last prevcfgterminal newcontent cfg.content) } @@ -192,7 +200,7 @@ module Make(M: PrintableType) = struct Printf.fprintf ppf "Code:\n"; List.iter (fun ((n, stms) : Node.t * elt list) : unit -> - Printf.fprintf ppf "\tid %d --> %a\n%!" n.id M.pplist (List.rev stms) + Printf.fprintf ppf "\tid %d --> %a\n%!" n.id M.pplist stms ) (NodeMap.to_list c.content); Printf.fprintf ppf "\n"; end diff --git a/lib/cfg/Cfg.mli b/lib/cfg/Cfg.mli index 1c23d2d..d331259 100644 --- a/lib/cfg/Cfg.mli +++ b/lib/cfg/Cfg.mli @@ -12,7 +12,12 @@ module Node : sig val create : unit -> t end -module NodeMap : Map.S with type key = Node.t +module NodeMap : sig + include Map.S with type key = Node.t + + val add_to_list_last : key -> 'a -> 'a list t -> 'a list t +end + module NodeSet : Set.S with type elt = Node.t module type C = sig diff --git a/lib/miniImp/CfgImp.ml b/lib/miniImp/CfgImp.ml index 6655d30..59584d3 100644 --- a/lib/miniImp/CfgImp.ml +++ b/lib/miniImp/CfgImp.ml @@ -68,12 +68,17 @@ module SSCfg = Cfg.Make(SimpleStatements) let rec convert_c (prevcfg: SSCfg.t) (prg: Types.c_exp) : SSCfg.t = let open SimpleStatements in match prg with - | Skip -> prevcfg |> SSCfg.addToLastNode SimpleSkip - | Assignment (x, a) -> prevcfg |> SSCfg.addToLastNode (SimpleAssignment (x, convert_a a)) + | Skip -> + prevcfg |> SSCfg.addToLastNode SimpleSkip + + | Assignment (x, a) -> + prevcfg |> SSCfg.addToLastNode (SimpleAssignment (x, convert_a a)) + | Sequence (c1, c2) -> let cfg1 = convert_c prevcfg c1 in let cfg2 = convert_c cfg1 c2 in cfg2 + | If (b, c1, c2) -> let convertedb = convert_b b in let cfg1 = convert_c SSCfg.empty c1 in @@ -86,6 +91,7 @@ let rec convert_c (prevcfg: SSCfg.t) (prg: Types.c_exp) : SSCfg.t = content = mergedcfg.content |> NodeMap.add_to_list entrynode (SimpleGuard convertedb) |> NodeMap.add_to_list exitnode (SimpleSkip) } + | While (b, c) -> let convertedb = convert_b b in let cfg = convert_c SSCfg.empty c in @@ -112,9 +118,11 @@ let rec convert_c (prevcfg: SSCfg.t) (prg: Types.c_exp) : SSCfg.t = inputOutputVar = prevcfg.inputOutputVar; initial = Some entrynode; terminal = Some exitnode; - content = NodeMap.add_to_list guardnode (SimpleGuard (convertedb)) cfg.content |> + content = cfg.content |> + NodeMap.add_to_list guardnode (SimpleGuard (convertedb)) |> NodeMap.add_to_list exitnode (SimpleSkip) } |> SSCfg.concat prevcfg + | For (assignment, guard, increment, body) -> let cfgassignment = convert_c SSCfg.empty assignment in let convertedguard = convert_b guard in @@ -144,7 +152,8 @@ let rec convert_c (prevcfg: SSCfg.t) (prg: Types.c_exp) : SSCfg.t = inputOutputVar = prevcfg.inputOutputVar; initial = Some guardnode; terminal = Some exitnode; - content = NodeMap.add_to_list guardnode (SimpleGuard (convertedguard)) bodyincrement.content |> + content = bodyincrement.content |> + NodeMap.add_to_list guardnode (SimpleGuard (convertedguard)) |> NodeMap.add_to_list exitnode (SimpleSkip) } |> SSCfg.concat prevassignment diff --git a/lib/miniImp/CfgRISC.ml b/lib/miniImp/CfgRISC.ml index 0a71b79..69e0e2b 100644 --- a/lib/miniImp/CfgRISC.ml +++ b/lib/miniImp/CfgRISC.ml @@ -103,42 +103,37 @@ module RegisterMap = struct type m = { assignments: int Types.VariableMap.t } - let _get_opt_register (x: Types.variable) (m: m) : RISCSimpleStatements.register option = - Option.bind - (Types.VariableMap.find_opt x m.assignments) - (fun (x: int) : RISCSimpleStatements.register option -> Some {index = x}) - let get_or_set_register (x: Types.variable) (m: m) : RISCSimpleStatements.register * m = + let get_or_set_register (x: Types.variable) (m: m) + : RISCSimpleStatements.register * m = match Types.VariableMap.find_opt x m.assignments with None -> ( globalcounter := !globalcounter + 1; - ({index = !globalcounter}, {assignments = Types.VariableMap.add x !globalcounter m.assignments}) ) + ({index = !globalcounter}, + {assignments = Types.VariableMap.add x !globalcounter m.assignments})) | Some i -> ({index = i}, m) - let get_fresh_register (m: m) : RISCSimpleStatements.register * m * Types.variable = + let get_fresh_register (m: m) + : RISCSimpleStatements.register * m * Types.variable = globalcounter := !globalcounter + 1; let freshvariable = string_of_int !globalcounter in ({index = !globalcounter}, - {assignments = Types.VariableMap.add freshvariable !globalcounter m.assignments}, + {assignments = + Types.VariableMap.add freshvariable !globalcounter m.assignments}, freshvariable) let empty : m = {assignments = Types.VariableMap.empty} - - (* let pp (ppx) (m: m) : unit = *) - (* Printf.fprintf ppx "RegisterMap contents: "; *) - (* List.iter (fun (n, v) -> Printf.fprintf ppx "%s -> %d, " n v) (Types.VariableMap.to_list m.assignments); *) - (* Printf.fprintf ppx "\n"; *) end - +(* converts a simple statement into RISC simple statements *) let rec c_ss_t (ss: CfgImp.SimpleStatements.t) (m: RegisterMap.m) (convertedcode: RISCSimpleStatements.t list) : RISCSimpleStatements.t list * RegisterMap.m = match ss with - SimpleSkip -> (Nop :: convertedcode, m) + SimpleSkip -> (convertedcode @ [Nop], m) | SimpleAssignment (v, sa) -> ( let r1, m = RegisterMap.get_or_set_register v m in c_ss_sa sa m convertedcode r1 @@ -147,6 +142,10 @@ let rec c_ss_t let returnreg, m, _returnregvar = RegisterMap.get_fresh_register m in c_ss_sb b m convertedcode returnreg ) + +(* converts a boolean simple statement into RISC simple statements, requires + the register where the result sould be put into, does a lookahead to optimize + with operations where an integer is one side of the operation *) and c_ss_sb (ss: CfgImp.SimpleStatements.simpleBoolean) (m: RegisterMap.m) @@ -157,9 +156,9 @@ and c_ss_sb SimpleBoolean (b) -> ( let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in if b then - (LoadI (partialresreg, 1) :: convertedcode, m) + (convertedcode @ [LoadI (partialresreg, 1)], m) else - (LoadI (partialresreg, 0) :: convertedcode, m) + (convertedcode @ [LoadI (partialresreg, 0)], m) ) | SimpleBAnd (b1, b2) -> ( match (b1, b2) with @@ -169,14 +168,14 @@ and c_ss_sb ) | (SimpleBoolean (false), _) | (_, SimpleBoolean (false)) -> ( - (LoadI (register, 0) :: convertedcode, m) + (convertedcode @ [LoadI (register, 0)], m) ) | (_, _) -> ( let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sb b1 m convertedcode partialresreg1 in let convertedcode, m = c_ss_sb b2 m convertedcode partialresreg2 in - (BRegOp (And, partialresreg1, partialresreg2, register) :: convertedcode, m) + (convertedcode @ [BRegOp (And, partialresreg1, partialresreg2, register)], m) ) ) | SimpleBOr (b1, b2) -> ( @@ -194,7 +193,7 @@ and c_ss_sb let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sb b1 m convertedcode partialresreg1 in let convertedcode, m = c_ss_sb b2 m convertedcode partialresreg2 in - (BRegOp (Or, partialresreg1, partialresreg2, register) :: convertedcode, m) + (convertedcode @ [BRegOp (Or, partialresreg1, partialresreg2, register)], m) ) ) | SimpleBNot (b) -> ( @@ -208,7 +207,7 @@ and c_ss_sb | _ -> ( let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sb b m convertedcode partialresreg in - (URegOp (Not, partialresreg, register) :: convertedcode, m) + (convertedcode @ [URegOp (Not, partialresreg, register)], m) ) ) | SimpleBCmp (a1, a2) -> ( @@ -216,215 +215,218 @@ and c_ss_sb | (SimpleInteger (i), SimpleVariable (x)) | (SimpleVariable (x), SimpleInteger (i)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in - (BImmOp (EqI, xreg, i, register) :: convertedcode, m) + (convertedcode @ [BImmOp (EqI, xreg, i, register)], m) ) | (SimpleInteger (i), a) | (a, SimpleInteger (i)) -> ( let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in - (BImmOp (EqI, partialresreg, i, register) :: convertedcode, m) + (convertedcode @ [BImmOp (EqI, partialresreg, i, register)], m) ) | (SimpleVariable (x), SimpleVariable (y)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in let yreg, m = RegisterMap.get_or_set_register y m in - (BRegOp (Eq, xreg, yreg, register) :: convertedcode, m) + (convertedcode @ [BRegOp (Eq, xreg, yreg, register)], m) ) | (SimpleVariable (x), a) | (a, SimpleVariable (x)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in - (BRegOp (Eq, partialresreg, xreg, register) :: convertedcode, m) + (convertedcode @ [BRegOp (Eq, partialresreg, xreg, register)], m) ) | (_, _) -> ( let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a1 m convertedcode partialresreg1 in let convertedcode, m = c_ss_sa a2 m convertedcode partialresreg2 in - (BRegOp (Eq, partialresreg1, partialresreg2, register) :: convertedcode, m) + (convertedcode @ [BRegOp (Eq, partialresreg1, partialresreg2, register)], m) ) ) | SimpleBCmpLess (a1, a2) -> ( match (a1, a2) with | (SimpleInteger (i), SimpleVariable (x)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in - (BImmOp (MoreI, xreg, i, register) :: convertedcode, m) + (convertedcode @ [BImmOp (MoreI, xreg, i, register)], m) ) | (SimpleVariable (x), SimpleInteger (i)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in - (BImmOp (LessI, xreg, i, register) :: convertedcode, m) + (convertedcode @ [BImmOp (LessI, xreg, i, register)], m) ) | (SimpleInteger (i), a) -> ( let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in - (BImmOp (MoreI, partialresreg, i, register) :: convertedcode, m) + (convertedcode @ [BImmOp (MoreI, partialresreg, i, register)], m) ) | (a, SimpleInteger (i)) -> ( let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in - (BImmOp (LessI, partialresreg, i, register) :: convertedcode, m) + (convertedcode @ [BImmOp (LessI, partialresreg, i, register)], m) ) | (SimpleVariable (x), SimpleVariable (y)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in let yreg, m = RegisterMap.get_or_set_register y m in - (BRegOp (Less, xreg, yreg, register) :: convertedcode, m) + (convertedcode @ [BRegOp (Less, xreg, yreg, register)], m) ) | (SimpleVariable (x), a) -> ( let xreg, m = RegisterMap.get_or_set_register x m in let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in - (BRegOp (Less, xreg, partialresreg, register) :: convertedcode, m) + (convertedcode @ [BRegOp (Less, xreg, partialresreg, register)], m) ) | (a, SimpleVariable (x)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in - (BRegOp (Less, partialresreg, xreg, register) :: convertedcode, m) + (convertedcode @ [BRegOp (Less, partialresreg, xreg, register)], m) ) | (_, _) -> ( let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a1 m convertedcode partialresreg1 in let convertedcode, m = c_ss_sa a2 m convertedcode partialresreg2 in - (BRegOp (Less, partialresreg1, partialresreg2, register) :: convertedcode, m) + (convertedcode @ [BRegOp (Less, partialresreg1, partialresreg2, register)], m) ) ) | SimpleBCmpLessEq (a1, a2) -> ( match (a1, a2) with | (SimpleInteger (i), SimpleVariable (x)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in - (BImmOp (MoreEqI, xreg, i, register) :: convertedcode, m) + (convertedcode @ [BImmOp (MoreEqI, xreg, i, register)], m) ) | (SimpleVariable (x), SimpleInteger (i)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in - (BImmOp (LessEqI, xreg, i, register) :: convertedcode, m) + (convertedcode @ [BImmOp (LessEqI, xreg, i, register)], m) ) | (SimpleInteger (i), a) -> ( let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in - (BImmOp (MoreEqI, partialresreg, i, register) :: convertedcode, m) + (convertedcode @ [BImmOp (MoreEqI, partialresreg, i, register)], m) ) | (a, SimpleInteger (i)) -> ( let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in - (BImmOp (LessEqI, partialresreg, i, register) :: convertedcode, m) + (convertedcode @ [BImmOp (LessEqI, partialresreg, i, register)], m) ) | (SimpleVariable (x), SimpleVariable (y)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in let yreg, m = RegisterMap.get_or_set_register y m in - (BRegOp (LessEq, xreg, yreg, register) :: convertedcode, m) + (convertedcode @ [BRegOp (LessEq, xreg, yreg, register)], m) ) | (SimpleVariable (x), a) -> ( let xreg, m = RegisterMap.get_or_set_register x m in let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in - (BRegOp (LessEq, xreg, partialresreg, register) :: convertedcode, m) + (convertedcode @ [BRegOp (LessEq, xreg, partialresreg, register)], m) ) | (a, SimpleVariable (x)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in - (BRegOp (LessEq, partialresreg, xreg, register) :: convertedcode, m) + (convertedcode @ [BRegOp (LessEq, partialresreg, xreg, register)], m) ) | (_, _) -> ( let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a1 m convertedcode partialresreg1 in let convertedcode, m = c_ss_sa a2 m convertedcode partialresreg2 in - (BRegOp (LessEq, partialresreg1, partialresreg2, register) :: convertedcode, m) + (convertedcode @ [BRegOp (LessEq, partialresreg1, partialresreg2, register)], m) ) ) | SimpleBCmpGreater (a1, a2) -> ( match (a1, a2) with | (SimpleInteger (i), SimpleVariable (x)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in - (BImmOp (LessI, xreg, i, register) :: convertedcode, m) + (convertedcode @ [BImmOp (LessI, xreg, i, register)], m) ) | (SimpleVariable (x), SimpleInteger (i)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in - (BImmOp (MoreI, xreg, i, register) :: convertedcode, m) + (convertedcode @ [BImmOp (MoreI, xreg, i, register)], m) ) | (SimpleInteger (i), a) -> ( let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in - (BImmOp (LessI, partialresreg, i, register) :: convertedcode, m) + (convertedcode @ [BImmOp (LessI, partialresreg, i, register)], m) ) | (a, SimpleInteger (i)) -> ( let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in - (BImmOp (MoreI, partialresreg, i, register) :: convertedcode, m) + (convertedcode @ [BImmOp (MoreI, partialresreg, i, register)], m) ) | (SimpleVariable (x), SimpleVariable (y)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in let yreg, m = RegisterMap.get_or_set_register y m in - (BRegOp (More, xreg, yreg, register) :: convertedcode, m) + (convertedcode @ [BRegOp (More, xreg, yreg, register)], m) ) | (SimpleVariable (x), a) -> ( let xreg, m = RegisterMap.get_or_set_register x m in let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in - (BRegOp (More, xreg, partialresreg, register) :: convertedcode, m) + (convertedcode @ [BRegOp (More, xreg, partialresreg, register)], m) ) | (a, SimpleVariable (x)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in - (BRegOp (More, partialresreg, xreg, register) :: convertedcode, m) + (convertedcode @ [BRegOp (More, partialresreg, xreg, register)], m) ) | (_, _) -> ( let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a1 m convertedcode partialresreg1 in let convertedcode, m = c_ss_sa a2 m convertedcode partialresreg2 in - (BRegOp (More, partialresreg1, partialresreg2, register) :: convertedcode, m) + (convertedcode @ [BRegOp (More, partialresreg1, partialresreg2, register)], m) ) ) | SimpleBCmpGreaterEq (a1, a2) -> ( match (a1, a2) with | (SimpleInteger (i), SimpleVariable (x)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in - (BImmOp (LessEqI, xreg, i, register) :: convertedcode, m) + (convertedcode @ [BImmOp (LessEqI, xreg, i, register)], m) ) | (SimpleVariable (x), SimpleInteger (i)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in - (BImmOp (MoreEqI, xreg, i, register) :: convertedcode, m) + (convertedcode @ [BImmOp (MoreEqI, xreg, i, register)], m) ) | (SimpleInteger (i), a) -> ( let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in - (BImmOp (LessEqI, partialresreg, i, register) :: convertedcode, m) + (convertedcode @ [BImmOp (LessEqI, partialresreg, i, register)], m) ) | (a, SimpleInteger (i)) -> ( let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in - (BImmOp (MoreEqI, partialresreg, i, register) :: convertedcode, m) + (convertedcode @ [BImmOp (MoreEqI, partialresreg, i, register)], m) ) | (SimpleVariable (x), SimpleVariable (y)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in let yreg, m = RegisterMap.get_or_set_register y m in - (BRegOp (MoreEq, xreg, yreg, register) :: convertedcode, m) + (convertedcode @ [BRegOp (MoreEq, xreg, yreg, register)], m) ) | (SimpleVariable (x), a) -> ( let xreg, m = RegisterMap.get_or_set_register x m in let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in - (BRegOp (MoreEq, xreg, partialresreg, register) :: convertedcode, m) + (convertedcode @ [BRegOp (MoreEq, xreg, partialresreg, register)], m) ) | (a, SimpleVariable (x)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in - (BRegOp (MoreEq, partialresreg, xreg, register) :: convertedcode, m) + (convertedcode @ [BRegOp (MoreEq, partialresreg, xreg, register)], m) ) | (_, _) -> ( let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a1 m convertedcode partialresreg1 in let convertedcode, m = c_ss_sa a2 m convertedcode partialresreg2 in - (BRegOp (MoreEq, partialresreg1, partialresreg2, register) :: convertedcode, m) + (convertedcode @ [BRegOp (MoreEq, partialresreg1, partialresreg2, register)], m) ) ) +(* converts a arithmetic simple statement into RISC simple statements, requires + the register where the result sould be put into, does a lookahead to optimize + with operations where an integer is one side of the operation *) and c_ss_sa (ss: CfgImp.SimpleStatements.simpleArithmetic) (m: RegisterMap.m) @@ -434,40 +436,42 @@ and c_ss_sa match ss with SimpleVariable (x) -> ( let r1, m = RegisterMap.get_or_set_register x m in - (Load (r1, register) :: convertedcode, m) + (convertedcode @ [Load (r1, register)], m) + ) + | SimpleInteger (i) -> ( + (convertedcode @ [LoadI (register, i)], m) ) - | SimpleInteger (i) -> (LoadI (register, i) :: convertedcode, m) | SimplePlus (a1, a2) -> ( match (a1, a2) with | (SimpleInteger (i), SimpleVariable (x)) | (SimpleVariable (x), SimpleInteger (i)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in - (BImmOp (AddI, xreg, i, register) :: convertedcode, m) + (convertedcode @ [BImmOp (AddI, xreg, i, register)], m) ) | (SimpleInteger (i), a) | (a, SimpleInteger (i)) -> ( let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in - (BImmOp (AddI, partialresreg, i, register) :: convertedcode, m) + (convertedcode @ [BImmOp (AddI, partialresreg, i, register)], m) ) | (SimpleVariable (x), SimpleVariable (y)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in let yreg, m = RegisterMap.get_or_set_register y m in - (BRegOp (Add, xreg, yreg, register) :: convertedcode, m) + (convertedcode @ [BRegOp (Add, xreg, yreg, register)], m) ) | (SimpleVariable (x), a) | (a, SimpleVariable (x)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in - (BRegOp (Add, partialresreg, xreg, register) :: convertedcode, m) + (convertedcode @ [BRegOp (Add, partialresreg, xreg, register)], m) ) | (_, _) -> ( let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a1 m convertedcode partialresreg1 in let convertedcode, m = c_ss_sa a2 m convertedcode partialresreg2 in - (BRegOp (Add, partialresreg1, partialresreg2, register) :: convertedcode, m) + (convertedcode @ [BRegOp (Add, partialresreg1, partialresreg2, register)], m) ) ) | SimpleMinus (a1, a2) -> ( @@ -475,46 +479,46 @@ and c_ss_sa | (SimpleInteger (i), SimpleVariable (x)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in - (BRegOp (Sub, partialresreg, xreg, register) :: LoadI (partialresreg, i) :: convertedcode, m) + (convertedcode @ [LoadI (partialresreg, i); BRegOp (Sub, partialresreg, xreg, register)], m) ) | (SimpleVariable (x), SimpleInteger (i)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in - (BImmOp (SubI, xreg, i, register) :: convertedcode, m) + (convertedcode @ [BImmOp (SubI, xreg, i, register)], m) ) | (SimpleInteger (i), a) -> ( let partialresregi, m, _partialresvari = RegisterMap.get_fresh_register m in let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in - (BRegOp (Sub, partialresregi, partialresreg, register) :: LoadI (partialresregi, i) :: convertedcode, m) + (convertedcode @ [LoadI (partialresregi, i); BRegOp (Sub, partialresregi, partialresreg, register)], m) ) | (a, SimpleInteger (i)) -> ( let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in - (BImmOp (SubI, partialresreg, i, register) :: convertedcode, m) + (convertedcode @ [BImmOp (SubI, partialresreg, i, register)], m) ) | (SimpleVariable (x), SimpleVariable (y)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in let yreg, m = RegisterMap.get_or_set_register y m in - (BRegOp (Sub, xreg, yreg, register) :: convertedcode, m) + (convertedcode @ [BRegOp (Sub, xreg, yreg, register)], m) ) | (SimpleVariable (x), a) -> ( let xreg, m = RegisterMap.get_or_set_register x m in let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in - (BRegOp (Sub, xreg, partialresreg, register) :: convertedcode, m) + (convertedcode @ [BRegOp (Sub, xreg, partialresreg, register)], m) ) | (a, SimpleVariable (x)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in - (BRegOp (Sub, partialresreg, xreg, register) :: convertedcode, m) + (convertedcode @ [BRegOp (Sub, partialresreg, xreg, register)], m) ) | (_, _) -> ( let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a1 m convertedcode partialresreg1 in let convertedcode, m = c_ss_sa a2 m convertedcode partialresreg2 in - (BRegOp (Sub, partialresreg1, partialresreg2, register) :: convertedcode, m) + (convertedcode @ [BRegOp (Sub, partialresreg1, partialresreg2, register)], m) ) ) | SimpleTimes (a1, a2) -> ( @@ -522,32 +526,32 @@ and c_ss_sa | (SimpleInteger (i), SimpleVariable (x)) | (SimpleVariable (x), SimpleInteger (i)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in - (BImmOp (MultI, xreg, i, register) :: convertedcode, m) + (convertedcode @ [BImmOp (MultI, xreg, i, register)], m) ) | (SimpleInteger (i), a) | (a, SimpleInteger (i)) -> ( let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in - (BImmOp (MultI, partialresreg, i, register) :: convertedcode, m) + (convertedcode @ [BImmOp (MultI, partialresreg, i, register)], m) ) | (SimpleVariable (x), SimpleVariable (y)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in let yreg, m = RegisterMap.get_or_set_register y m in - (BRegOp (Mult, xreg, yreg, register) :: convertedcode, m) + (convertedcode @ [BRegOp (Mult, xreg, yreg, register)], m) ) | (SimpleVariable (x), a) | (a, SimpleVariable (x)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in - (BRegOp (Mult, partialresreg, xreg, register) :: convertedcode, m) + (convertedcode @ [BRegOp (Mult, partialresreg, xreg, register)], m) ) | (_, _) -> ( let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a1 m convertedcode partialresreg1 in let convertedcode, m = c_ss_sa a2 m convertedcode partialresreg2 in - (BRegOp (Mult, partialresreg1, partialresreg2, register) :: convertedcode, m) + (convertedcode @ [BRegOp (Mult, partialresreg1, partialresreg2, register)], m) ) ) | SimpleDivision (a1, a2) -> ( @@ -555,46 +559,46 @@ and c_ss_sa | (SimpleInteger (i), SimpleVariable (x)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in - (BRegOp (Div, partialresreg, xreg, register) :: LoadI (partialresreg, i) :: convertedcode, m) + (convertedcode @ [LoadI (partialresreg, i); BRegOp (Div, partialresreg, xreg, register)], m) ) | (SimpleVariable (x), SimpleInteger (i)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in - (BImmOp (DivI, xreg, i, register) :: convertedcode, m) + (convertedcode @ [BImmOp (DivI, xreg, i, register)], m) ) | (SimpleInteger (i), a) -> ( let partialresregi, m, _partialresvari = RegisterMap.get_fresh_register m in let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in - (BRegOp (Div, partialresregi, partialresreg, register) :: LoadI (partialresregi, i) :: convertedcode, m) + (convertedcode @ [LoadI (partialresregi, i); BRegOp (Div, partialresregi, partialresreg, register)], m) ) | (a, SimpleInteger (i)) -> ( let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in - (BImmOp (DivI, partialresreg, i, register) :: convertedcode, m) + (convertedcode @ [BImmOp (DivI, partialresreg, i, register)], m) ) | (SimpleVariable (x), SimpleVariable (y)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in let yreg, m = RegisterMap.get_or_set_register y m in - (BRegOp (Div, xreg, yreg, register) :: convertedcode, m) + (convertedcode @ [BRegOp (Div, xreg, yreg, register)], m) ) | (SimpleVariable (x), a) -> ( let xreg, m = RegisterMap.get_or_set_register x m in let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in - (BRegOp (Div, xreg, partialresreg, register) :: convertedcode, m) + (convertedcode @ [BRegOp (Div, xreg, partialresreg, register)], m) ) | (a, SimpleVariable (x)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in - (BRegOp (Div, partialresreg, xreg, register) :: convertedcode, m) + (convertedcode @ [BRegOp (Div, partialresreg, xreg, register)], m) ) | (_, _) -> ( let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a1 m convertedcode partialresreg1 in let convertedcode, m = c_ss_sa a2 m convertedcode partialresreg2 in - (BRegOp (Div, partialresreg1, partialresreg2, register) :: convertedcode, m) + (convertedcode @ [BRegOp (Div, partialresreg1, partialresreg2, register)], m) ) ) | SimpleModulo (a1, a2) -> ( @@ -602,46 +606,46 @@ and c_ss_sa | (SimpleInteger (i), SimpleVariable (x)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in - (BRegOp (Mod, partialresreg, xreg, register) :: LoadI (partialresreg, i) :: convertedcode, m) + (convertedcode @ [LoadI (partialresreg, i); BRegOp (Mod, partialresreg, xreg, register)], m) ) | (SimpleVariable (x), SimpleInteger (i)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in - (BImmOp (ModI, xreg, i, register) :: convertedcode, m) + (convertedcode @ [BImmOp (ModI, xreg, i, register)], m) ) | (SimpleInteger (i), a) -> ( let partialresregi, m, _partialresvari = RegisterMap.get_fresh_register m in let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in - (BRegOp (Mod, partialresregi, partialresreg, register) :: LoadI (partialresregi, i) :: convertedcode, m) + (convertedcode @ [LoadI (partialresregi, i); BRegOp (Mod, partialresregi, partialresreg, register)], m) ) | (a, SimpleInteger (i)) -> ( let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in - (BImmOp (ModI, partialresreg, i, register) :: convertedcode, m) + (convertedcode @ [BImmOp (ModI, partialresreg, i, register)], m) ) | (SimpleVariable (x), SimpleVariable (y)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in let yreg, m = RegisterMap.get_or_set_register y m in - (BRegOp (Mod, xreg, yreg, register) :: convertedcode, m) + (convertedcode @ [BRegOp (Mod, xreg, yreg, register)], m) ) | (SimpleVariable (x), a) -> ( let xreg, m = RegisterMap.get_or_set_register x m in let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in - (BRegOp (Mod, xreg, partialresreg, register) :: convertedcode, m) + (convertedcode @ [BRegOp (Mod, xreg, partialresreg, register)], m) ) | (a, SimpleVariable (x)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in - (BRegOp (Mod, partialresreg, xreg, register) :: convertedcode, m) + (convertedcode @ [BRegOp (Mod, partialresreg, xreg, register)], m) ) | (_, _) -> ( let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a1 m convertedcode partialresreg1 in let convertedcode, m = c_ss_sa a2 m convertedcode partialresreg2 in - (BRegOp (Mod, partialresreg1, partialresreg2, register) :: convertedcode, m) + (convertedcode @ [BRegOp (Mod, partialresreg1, partialresreg2, register)], m) ) ) | SimplePower (a1, a2) -> ( @@ -649,53 +653,53 @@ and c_ss_sa | (SimpleInteger (i), SimpleVariable (x)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in - (BRegOp (Pow, partialresreg, xreg, register) :: LoadI (partialresreg, i) :: convertedcode, m) + (convertedcode @ [LoadI (partialresreg, i); BRegOp (Pow, partialresreg, xreg, register)], m) ) | (SimpleVariable (x), SimpleInteger (i)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in - (BImmOp (PowI, xreg, i, register) :: convertedcode, m) + (convertedcode @ [BImmOp (PowI, xreg, i, register)], m) ) | (SimpleInteger (i), a) -> ( let partialresregi, m, _partialresvari = RegisterMap.get_fresh_register m in let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in - (BRegOp (Pow, partialresregi, partialresreg, register) :: LoadI (partialresregi, i) :: convertedcode, m) + (convertedcode @ [LoadI (partialresregi, i); BRegOp (Pow, partialresregi, partialresreg, register)], m) ) | (a, SimpleInteger (i)) -> ( let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in - (BImmOp (PowI, partialresreg, i, register) :: convertedcode, m) + (convertedcode @ [BImmOp (PowI, partialresreg, i, register)], m) ) | (SimpleVariable (x), SimpleVariable (y)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in let yreg, m = RegisterMap.get_or_set_register y m in - (BRegOp (Pow, xreg, yreg, register) :: convertedcode, m) + (convertedcode @ [BRegOp (Pow, xreg, yreg, register)], m) ) | (SimpleVariable (x), a) -> ( let xreg, m = RegisterMap.get_or_set_register x m in let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in - (BRegOp (Pow, xreg, partialresreg, register) :: convertedcode, m) + (convertedcode @ [BRegOp (Pow, xreg, partialresreg, register)], m) ) | (a, SimpleVariable (x)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in - (BRegOp (Pow, partialresreg, xreg, register) :: convertedcode, m) + (convertedcode @ [BRegOp (Pow, partialresreg, xreg, register)], m) ) | (_, _) -> ( let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a1 m convertedcode partialresreg1 in let convertedcode, m = c_ss_sa a2 m convertedcode partialresreg2 in - (BRegOp (Pow, partialresreg1, partialresreg2, register) :: convertedcode, m) + (convertedcode @ [BRegOp (Pow, partialresreg1, partialresreg2, register)], m) ) ) | SimplePowerMod (_a1, _a2, _a3) -> failwith "Not implemented Powermod" | SimpleRand (a) -> ( let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in - (URegOp (Rand, partialresreg, register) :: convertedcode, m) + (convertedcode @ [URegOp (Rand, partialresreg, register)], m) ) let convert_ss @@ -704,12 +708,29 @@ let convert_ss (node: Cfg.Node.t) (risccode: RISCSimpleStatements.t list Cfg.NodeMap.t) : RISCSimpleStatements.t list Cfg.NodeMap.t * RegisterMap.m = - let instructions, m = List.fold_right (fun code (convertedcode, m) -> - c_ss_t code m convertedcode) value ([], m) in + (* we iterate over the list of simple statements and convert each operation to + the equivalent three address operations, we need to propagate the + association between variables and registers so we choose a fold instead of + a mapreduce *) + let instructions, m = List.fold_left + (fun (convertedcode, m) code -> ( + Printf.printf "considering: %a\n" CfgImp.SimpleStatements.pp code; + c_ss_t code m convertedcode)) + ([], m) value + in (Cfg.NodeMap.add node instructions risccode, m) -let helper (c: CfgImp.SimpleStatements.t list Cfg.NodeMap.t) (m: RegisterMap.m) : RISCSimpleStatements.t list Cfg.NodeMap.t = - let risccode, _ = Cfg.NodeMap.fold (fun node value (risccode, m) -> convert_ss m value node risccode) c (Cfg.NodeMap.empty, m) in +let helper + (c: CfgImp.SimpleStatements.t list Cfg.NodeMap.t) + (m: RegisterMap.m) + : RISCSimpleStatements.t list Cfg.NodeMap.t = + (* *) + (* we use NodeMap.fold since order is not important, we assume that every + has an associated register and we ignore use before assignment errors *) + let risccode, _ = Cfg.NodeMap.fold + (fun node value (risccode, m) -> convert_ss m value node risccode) + c (Cfg.NodeMap.empty, m) + in risccode let convert (prg: CfgImp.SSCfg.t) : RISCCfg.t = diff --git a/lib/miniImp/RISC.ml b/lib/miniImp/RISC.ml index f59f4b7..cf69c2d 100644 --- a/lib/miniImp/RISC.ml +++ b/lib/miniImp/RISC.ml @@ -197,7 +197,11 @@ let rec helper (prg: CfgRISC.RISCCfg.t) (currentnode: Cfg.Node.t) (alreadyVisite ([], alreadyVisited) else ( let nextnodes = (Cfg.NodeMap.find_opt currentnode prg.edges) in - let currentcode = (Cfg.NodeMap.find currentnode prg.content |> convert_cfgrisc_risci) in + let currentcode = + (match (Cfg.NodeMap.find_opt currentnode prg.content) with + | None -> [] + | Some x -> convert_cfgrisc_risci x) + in match nextnodes with | Some (nextnode1, None) -> let res, vis = (helper prg nextnode1) (currentnode :: alreadyVisited) in From efa6ed21c96f47d122b2be271245b58c096f80a2 Mon Sep 17 00:00:00 2001 From: elvis Date: Mon, 2 Dec 2024 14:26:05 +0100 Subject: [PATCH 07/29] Added While loop to RISC --- lib/miniImp/RISC.ml | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/lib/miniImp/RISC.ml b/lib/miniImp/RISC.ml index cf69c2d..a7c5b96 100644 --- a/lib/miniImp/RISC.ml +++ b/lib/miniImp/RISC.ml @@ -192,7 +192,15 @@ let nextCommonSuccessor (prg: CfgRISC.RISCCfg.t) (node1: Cfg.Node.t) (node2: Cfg | a::_ -> Some a -let rec helper (prg: CfgRISC.RISCCfg.t) (currentnode: Cfg.Node.t) (alreadyVisited: Cfg.Node.t list) : RISCAssembly.t * Cfg.Node.t list = +let rec helper + (prg: CfgRISC.RISCCfg.t) + (currentnode: Cfg.Node.t) + (alreadyVisited: Cfg.Node.t list) + : RISCAssembly.t * Cfg.Node.t list = + (* takes the program, the current node and a list of already visited nodes to + compute the linearized three address instructions and the list of + previoulsy visited nodes plus the newly visited nodes. Stops as soon if + node has already been visited or if no nodes are next *) if List.mem currentnode alreadyVisited then ([], alreadyVisited) else ( @@ -214,9 +222,28 @@ let rec helper (prg: CfgRISC.RISCCfg.t) (currentnode: Cfg.Node.t) (alreadyVisite failwith "Topology got a little mixed up" | Some ncs -> ( if (ncs.id = nextnode2.id) - then (* while or for loop *) - failwith "Not implemented" - else (* if branches *) + then (* while or for loop, three labels are necessary *) + let label1 = nextLabel () in + let label2 = nextLabel () in + let label3 = nextLabel () in + + let res1, _ = (helper prg nextnode1 (currentnode :: nextnode2 :: alreadyVisited)) in + let res2, vis2 = (helper prg nextnode2 (currentnode :: nextnode1 :: alreadyVisited)) in + + match List.nth currentcode ((List.length currentcode) - 1) with + | BRegOp (_, _, _, r) + | BImmOp (_, _, _, r) + | URegOp (_, _, r) + | Load (_, r) + | LoadI (r, _) -> (([Label label1] : RISCAssembly.t) @ + currentcode @ + ([CJump (r, label2, label3); Label label2] : RISCAssembly.t) @ + res1 @ + ([Jump label1; Label label3] : RISCAssembly.t) @ + res2 + , vis2) + | _ -> failwith "Missing instruction" + else (* if branches, three labels are necessary *) let label1 = nextLabel () in let label2 = nextLabel () in let label3 = nextLabel () in From 08a8d074226d38b97b99e18572d359cf181a6ecc Mon Sep 17 00:00:00 2001 From: elvis Date: Tue, 3 Dec 2024 17:18:42 +0100 Subject: [PATCH 08/29] Semantics for RISC code --- bin/dune | 3 +- bin/main.ml | 44 +++--- lib/miniImp/CfgImp.ml | 6 +- lib/miniImp/CfgImp.mli | 2 +- lib/miniImp/CfgRISC.ml | 79 ++++++---- lib/miniImp/CfgRISC.mli | 2 +- lib/miniImp/RISC.ml | 61 ++++---- lib/miniImp/RISC.mli | 10 +- lib/miniImp/RISCSemantics.ml | 177 +++++++++++++++++++++++ lib/miniImp/RISCSemantics.mli | 5 + lib/miniImp/Types.ml | 36 +++++ lib/miniImp/Types.mli | 1 + lib/miniImp/dune | 5 +- lib/miniImp/replacePowerMod.ml | 246 ++++++++++++++++++++++++++++++++ lib/miniImp/replacePowerMod.mli | 1 + lib/utility/utility.ml | 28 ++++ lib/utility/utility.mli | 9 ++ test/dune | 6 +- test/testingRISC.expected | 8 ++ test/testingRISC.ml | 128 +++++++++++++++++ 20 files changed, 771 insertions(+), 86 deletions(-) create mode 100644 lib/miniImp/RISCSemantics.ml create mode 100644 lib/miniImp/RISCSemantics.mli create mode 100644 lib/miniImp/replacePowerMod.ml create mode 100644 lib/miniImp/replacePowerMod.mli create mode 100644 test/testingRISC.expected create mode 100644 test/testingRISC.ml diff --git a/bin/dune b/bin/dune index 961c8b4..02fa3b6 100644 --- a/bin/dune +++ b/bin/dune @@ -4,6 +4,7 @@ (libraries exercises miniImp miniFun + cfg utility) (package miniFun) (modes byte exe) @@ -25,4 +26,4 @@ clap) (package miniImp) (modes byte exe) - ) \ No newline at end of file + ) diff --git a/bin/main.ml b/bin/main.ml index 8f8e14a..c0edcd1 100644 --- a/bin/main.ml +++ b/bin/main.ml @@ -1,35 +1,37 @@ open MiniImp -let () = - (* let program = "def main with input x output y as *) - (* x := 2; *) - (* if y < 0 then ( *) - (* y := x + 3; *) - (* x := y; *) - (* ) else *) - (* x := 1 - y;" in *) +let colorred s = + "\027[31m" ^ s ^ "\027[0m" - let program = "def main with input x output y as - x := 2; - if y < 0 then ( - y := x + 3; - if x > 0 then - x := y; - else - x := y + 1; - ) else - x := 1 - y;" in +let () = + let program = " +def main with input a output b as + b := 1; + for (i := 1, i <= a, i := i + 1) do + b := b * i; +" + in + + Printf.printf "%s\n%s\n" (colorred "Program is") program; let get_result x = Lexing.from_string x |> Parser.prg Lexer.lex in let p = get_result program in - let convertedcfg = CfgImp.convert_io p 3 in + Format.printf "%s\n%a\n@?" (colorred "AST is") Types.pp_p_exp p; - Printf.printf "%a" CfgImp.SSCfg.pp convertedcfg; + let convertedcfg = CfgImp.convert_io 10 p in + + Printf.printf "%s\n%a" (colorred "Converted CFG is") CfgImp.SSCfg.pp convertedcfg; let convertedrisccfg = CfgRISC.convert convertedcfg in + Printf.printf "%s\n%a" (colorred "Converted RISC CFG is") CfgRISC.RISCCfg.pp convertedrisccfg; + let risc = RISC.convert convertedrisccfg in - Printf.printf "%a" RISC.RISCAssembly.pp risc + Printf.printf "%s\n%a" (colorred "RISC code is") RISC.RISCAssembly.pp risc; + + let computerisc = RISCSemantics.reduce risc in + + Printf.printf "%s\n%d\n" (colorred "Output of RISC code is") computerisc; diff --git a/lib/miniImp/CfgImp.ml b/lib/miniImp/CfgImp.ml index 59584d3..295e5cd 100644 --- a/lib/miniImp/CfgImp.ml +++ b/lib/miniImp/CfgImp.ml @@ -179,13 +179,15 @@ and convert_a (prg: Types.a_exp) : SimpleStatements.simpleArithmetic = | Division (a1, a2) -> SimpleDivision (convert_a a1, convert_a a2) | Modulo (a1, a2) -> SimpleModulo (convert_a a1, convert_a a2) | Power (a1, a2) -> SimplePower (convert_a a1, convert_a a2) - | PowerMod (a1, a2, a3) -> SimplePowerMod (convert_a a1, convert_a a2, convert_a a3) + | PowerMod (_) -> failwith "Cannot convert PowerMod into Simple Instruction" | Rand (a) -> SimpleRand (convert_a a) let convert (prg: Types.p_exp) : SSCfg.t = + let prg = ReplacePowerMod.rewrite_instructions prg in match prg with | Main (i, o, exp) -> {(convert_c SSCfg.empty exp) with inputOutputVar = Some (i, o)} -let convert_io (prg: Types.p_exp) (i: int) : SSCfg.t = +let convert_io (i: int) (prg: Types.p_exp) : SSCfg.t = + let prg = ReplacePowerMod.rewrite_instructions prg in {(convert prg) with inputVal = Some i} diff --git a/lib/miniImp/CfgImp.mli b/lib/miniImp/CfgImp.mli index b0f7035..b980502 100644 --- a/lib/miniImp/CfgImp.mli +++ b/lib/miniImp/CfgImp.mli @@ -32,4 +32,4 @@ end module SSCfg : Cfg.C with type elt = SimpleStatements.t val convert : Types.p_exp -> SSCfg.t -val convert_io : Types.p_exp -> int -> SSCfg.t +val convert_io : int -> Types.p_exp -> SSCfg.t diff --git a/lib/miniImp/CfgRISC.ml b/lib/miniImp/CfgRISC.ml index 69e0e2b..fe21435 100644 --- a/lib/miniImp/CfgRISC.ml +++ b/lib/miniImp/CfgRISC.ml @@ -1,6 +1,6 @@ module RISCSimpleStatements = struct type register = { - index: int + index: string } type t = @@ -48,12 +48,12 @@ module RISCSimpleStatements = struct let rec pp_t (ppf: out_channel) (v: t) : unit = match v with Nop -> Printf.fprintf ppf "Nop" - | BRegOp (b, r1, r2, r3) -> Printf.fprintf ppf "%a r%d r%d => r%d" pp_brop b r1.index r2.index r3.index - | BImmOp (b, r1, i, r3) -> Printf.fprintf ppf "%a r%d %d => r%d" pp_biop b r1.index i r3.index - | URegOp (u, r1, r2) -> Printf.fprintf ppf "%a r%d => r%d" pp_urop u r1.index r2.index - | Load (r1, r2) -> Printf.fprintf ppf "Load r%d => r%d" r1.index r2.index - | LoadI (r2, i) -> Printf.fprintf ppf "LoadI %d => r%d" i r2.index - | Store (r1, r2) -> Printf.fprintf ppf "Store r%d => r%d" r1.index r2.index + | BRegOp (b, r1, r2, r3) -> Printf.fprintf ppf "%a r%s r%s => r%s" pp_brop b r1.index r2.index r3.index + | BImmOp (b, r1, i, r3) -> Printf.fprintf ppf "%a r%s %d => r%s" pp_biop b r1.index i r3.index + | URegOp (u, r1, r2) -> Printf.fprintf ppf "%a r%s => r%s" pp_urop u r1.index r2.index + | Load (r1, r2) -> Printf.fprintf ppf "Load r%s => r%s" r1.index r2.index + | LoadI (r2, i) -> Printf.fprintf ppf "LoadI %d => r%s" i r2.index + | Store (r1, r2) -> Printf.fprintf ppf "Store r%s => r%s" r1.index r2.index and pp_brop (ppf: out_channel) (v: brop) : unit = match v with Add -> Printf.fprintf ppf "Add" @@ -101,25 +101,34 @@ module RISCCfg = Cfg.Make(RISCSimpleStatements) let globalcounter = ref 0 module RegisterMap = struct type m = { - assignments: int Types.VariableMap.t + assignments: RISCSimpleStatements.register Types.VariableMap.t } + let set_register (x: Types.variable) (v: RISCSimpleStatements.register) (m: m) + : m = + {assignments = Types.VariableMap.add x v m.assignments} + let get_or_set_register (x: Types.variable) (m: m) : RISCSimpleStatements.register * m = match Types.VariableMap.find_opt x m.assignments with - None -> - ( globalcounter := !globalcounter + 1; - ({index = !globalcounter}, - {assignments = Types.VariableMap.add x !globalcounter m.assignments})) - | Some i -> ({index = i}, m) + None -> ( + globalcounter := !globalcounter + 1; + ({index = string_of_int !globalcounter}, + {assignments = + Types.VariableMap.add x + ({index = (string_of_int !globalcounter)}: RISCSimpleStatements.register) + m.assignments})) + | Some i -> (i, m) let get_fresh_register (m: m) : RISCSimpleStatements.register * m * Types.variable = globalcounter := !globalcounter + 1; let freshvariable = string_of_int !globalcounter in - ({index = !globalcounter}, + ({index = string_of_int !globalcounter}, {assignments = - Types.VariableMap.add freshvariable !globalcounter m.assignments}, + Types.VariableMap.add freshvariable + ({index = string_of_int !globalcounter}: RISCSimpleStatements.register) + m.assignments}, freshvariable) let empty : m = @@ -436,7 +445,7 @@ and c_ss_sa match ss with SimpleVariable (x) -> ( let r1, m = RegisterMap.get_or_set_register x m in - (convertedcode @ [Load (r1, register)], m) + (convertedcode @ [URegOp (Copy, r1, register)], m) ) | SimpleInteger (i) -> ( (convertedcode @ [LoadI (register, i)], m) @@ -695,7 +704,9 @@ and c_ss_sa (convertedcode @ [BRegOp (Pow, partialresreg1, partialresreg2, register)], m) ) ) - | SimplePowerMod (_a1, _a2, _a3) -> failwith "Not implemented Powermod" + | SimplePowerMod (_a1, _a2, _a3) -> ( + failwith "not implemented" + ) | SimpleRand (a) -> ( let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in @@ -713,9 +724,7 @@ let convert_ss association between variables and registers so we choose a fold instead of a mapreduce *) let instructions, m = List.fold_left - (fun (convertedcode, m) code -> ( - Printf.printf "considering: %a\n" CfgImp.SimpleStatements.pp code; - c_ss_t code m convertedcode)) + (fun (convertedcode, m) code -> c_ss_t code m convertedcode) ([], m) value in (Cfg.NodeMap.add node instructions risccode, m) @@ -744,13 +753,23 @@ let convert (prg: CfgImp.SSCfg.t) : RISCCfg.t = initial: Cfg.Node.t option; terminal: Cfg.Node.t option; content: CfgImp.SimpleStatements.t list Cfg.NodeMap.t - } -> { empty = empty; - nodes = nodes; - edges = edges; - reverseEdges = reverseEdges; - inputVal = inputVal; - inputOutputVar = inputOutputVar; - initial = initial; - terminal = terminal; - content = helper content RegisterMap.empty; - } + } -> + let initial_bindings = + match inputOutputVar with + | Some (i, o) -> + RegisterMap.empty |> + RegisterMap.set_register i {index = "in"} |> + RegisterMap.set_register o {index = "out"} + | None -> + RegisterMap.empty + in + { empty = empty; + nodes = nodes; + edges = edges; + reverseEdges = reverseEdges; + inputVal = inputVal; + inputOutputVar = inputOutputVar; + initial = initial; + terminal = terminal; + content = helper content initial_bindings; + } diff --git a/lib/miniImp/CfgRISC.mli b/lib/miniImp/CfgRISC.mli index 4d1b650..95fd4fd 100644 --- a/lib/miniImp/CfgRISC.mli +++ b/lib/miniImp/CfgRISC.mli @@ -1,6 +1,6 @@ module RISCSimpleStatements : sig type register = { - index: int + index: string } type t = diff --git a/lib/miniImp/RISC.ml b/lib/miniImp/RISC.ml index a7c5b96..bb6542a 100644 --- a/lib/miniImp/RISC.ml +++ b/lib/miniImp/RISC.ml @@ -6,11 +6,10 @@ let nextLabel () : string = module RISCAssembly = struct type register = { - index : int + index : string } - type label = - string + type label = string type risci = | Nop @@ -56,20 +55,23 @@ module RISCAssembly = struct | Copy | Rand - type t = risci list + type t = { + code : risci list; + inputval: int option + } - let pp (ppf: out_channel) (t: t) : unit = + let pp_risci (ppf: out_channel) (v: risci) : unit = let rec pp_risci (ppf: out_channel) (v: risci) : unit = match v with Nop -> Printf.fprintf ppf "\tNop\n" - | BRegOp (b, r1, r2, r3) -> Printf.fprintf ppf "\t%a r%d r%d => r%d\n" pp_brop b r1.index r2.index r3.index - | BImmOp (b, r1, i, r3) -> Printf.fprintf ppf "\t%a r%d %d => r%d\n" pp_biop b r1.index i r3.index - | URegOp (u, r1, r2) -> Printf.fprintf ppf "\t%a r%d => r%d\n" pp_urop u r1.index r2.index - | Load (r1, r2) -> Printf.fprintf ppf "\tLoad r%d => r%d\n" r1.index r2.index - | LoadI (r2, i) -> Printf.fprintf ppf "\tLoadI %d => r%d\n" i r2.index - | Store (r1, r2) -> Printf.fprintf ppf "\tStore r%d => r%d\n" r1.index r2.index + | BRegOp (b, r1, r2, r3) -> Printf.fprintf ppf "\t%a r%s r%s => r%s\n" pp_brop b r1.index r2.index r3.index + | BImmOp (b, r1, i, r3) -> Printf.fprintf ppf "\t%a r%s %d => r%s\n" pp_biop b r1.index i r3.index + | URegOp (u, r1, r2) -> Printf.fprintf ppf "\t%a r%s => r%s\n" pp_urop u r1.index r2.index + | Load (r1, r2) -> Printf.fprintf ppf "\tLoad r%s => r%s\n" r1.index r2.index + | LoadI (r2, i) -> Printf.fprintf ppf "\tLoadI %d => r%s\n" i r2.index + | Store (r1, r2) -> Printf.fprintf ppf "\tStore r%s => r%s\n" r1.index r2.index | Jump (label) -> Printf.fprintf ppf "\tJump %s\n" label - | CJump (r, l1, l2) -> Printf.fprintf ppf "\tCJump r%d => %s, %s\n" r.index l1 l2 + | CJump (r, l1, l2) -> Printf.fprintf ppf "\tCJump r%s => %s, %s\n" r.index l1 l2 | Label (label) -> Printf.fprintf ppf "%s:" label and pp_brop (ppf: out_channel) (v: brop) : unit = match v with @@ -107,10 +109,18 @@ module RISCAssembly = struct | Copy -> Printf.fprintf ppf "Copy" | Rand -> Printf.fprintf ppf "Rand" in - List.iter (pp_risci ppf) t + pp_risci ppf v + + let pp (ppf: out_channel) (t: t) : unit = + Printf.fprintf ppf "Input Val: "; + match t.inputval with + None -> Printf.fprintf ppf "None\n" + | Some i -> Printf.fprintf ppf "Some %d\n" i; + Printf.fprintf ppf "Code:\n"; + List.iter (pp_risci ppf) t.code end -let convert_cfgrisc_risci (i: CfgRISC.RISCSimpleStatements.t list) : (RISCAssembly.t) = +let convert_cfgrisc_risci (i: CfgRISC.RISCSimpleStatements.t list) : (RISCAssembly.risci list) = let rec helper (i: CfgRISC.RISCSimpleStatements.t) : RISCAssembly.risci = match i with | Nop -> Nop @@ -196,7 +206,7 @@ let rec helper (prg: CfgRISC.RISCCfg.t) (currentnode: Cfg.Node.t) (alreadyVisited: Cfg.Node.t list) - : RISCAssembly.t * Cfg.Node.t list = + : (RISCAssembly.risci list) * (Cfg.Node.t list) = (* takes the program, the current node and a list of already visited nodes to compute the linearized three address instructions and the list of previoulsy visited nodes plus the newly visited nodes. Stops as soon if @@ -235,14 +245,14 @@ let rec helper | BImmOp (_, _, _, r) | URegOp (_, _, r) | Load (_, r) - | LoadI (r, _) -> (([Label label1] : RISCAssembly.t) @ + | LoadI (r, _) -> (([Label label1] : RISCAssembly.risci list) @ currentcode @ - ([CJump (r, label2, label3); Label label2] : RISCAssembly.t) @ + ([CJump (r, label2, label3); Label label2] : RISCAssembly.risci list) @ res1 @ - ([Jump label1; Label label3] : RISCAssembly.t) @ + ([Jump label1; Label label3] : RISCAssembly.risci list) @ res2 , vis2) - | _ -> failwith "Missing instruction" + | _ -> failwith "Missing instruction at branch" else (* if branches, three labels are necessary *) let label1 = nextLabel () in let label2 = nextLabel () in @@ -257,19 +267,20 @@ let rec helper | URegOp (_, _, r) | Load (_, r) | LoadI (r, _) -> (currentcode @ - ([CJump (r, label1, label2); Label label1] : RISCAssembly.t) @ + ([CJump (r, label1, label2); Label label1] : RISCAssembly.risci list) @ res1 @ - ([Jump label3; Label label2] : RISCAssembly.t) @ + ([Jump label3; Label label2] : RISCAssembly.risci list) @ res2 @ - ([Label label3] : RISCAssembly.t) @ + ([Label label3] : RISCAssembly.risci list) @ res3 , vis3) - | _ -> failwith "Missing instruction" + | _ -> failwith "Missing instruction at branch" ) ) | None -> (currentcode, currentnode :: alreadyVisited) ) let convert (prg: CfgRISC.RISCCfg.t) : RISCAssembly.t = - let res, _ = helper prg (Option.get prg.initial) [] in - res + {code = (helper prg (Option.get prg.initial) [] |> fst |> + List.append ([Label "main"] : RISCAssembly.risci list)); + inputval = prg.inputVal} diff --git a/lib/miniImp/RISC.mli b/lib/miniImp/RISC.mli index d12a017..6beb8a9 100644 --- a/lib/miniImp/RISC.mli +++ b/lib/miniImp/RISC.mli @@ -1,9 +1,9 @@ module RISCAssembly : sig type register = { - index : int + index : string } - type label + type label = string type risci = | Nop | BRegOp of brop * register * register * register @@ -48,8 +48,12 @@ module RISCAssembly : sig | Copy | Rand - type t = risci list + type t = { + code : risci list; + inputval: int option + } + val pp_risci : out_channel -> risci -> unit val pp : out_channel -> t -> unit end diff --git a/lib/miniImp/RISCSemantics.ml b/lib/miniImp/RISCSemantics.ml new file mode 100644 index 0000000..d01ff6e --- /dev/null +++ b/lib/miniImp/RISCSemantics.ml @@ -0,0 +1,177 @@ +module Register = struct + type t = {index: string} + + let compare a b = compare a.index b.index +end + + +module CodeMap = Map.Make(String) +module RegisterMap = Map.Make(Register) +module MemoryMap = Map.Make(Int) + +module RISCArchitecture = struct + type t = { + code: RISC.RISCAssembly.risci list CodeMap.t; + registers: int RegisterMap.t; + memory: int MemoryMap.t + } +end + +let convert (prg: RISC.RISCAssembly.t) : RISC.RISCAssembly.risci list CodeMap.t = + let rec helper + (prg: RISC.RISCAssembly.risci list) + (current: RISC.RISCAssembly.risci list) + (current_label: string) + (map: RISC.RISCAssembly.risci list CodeMap.t) + : (RISC.RISCAssembly.risci list CodeMap.t) = + match prg with + | [] -> (CodeMap.union + (fun _ _ _ -> failwith "Two labels are the same") + (CodeMap.singleton current_label current) + map) + | Label l :: tl -> helper tl ([]) l + (CodeMap.union + (fun _ _ _ -> failwith "Two labels are the same") + (CodeMap.singleton current_label current) + map) + | instr :: tl -> helper tl (current @ [instr]) current_label map + in + match prg.code with + | Label "main" :: tl -> helper tl [] "main" CodeMap.empty + | _ -> failwith "Program should begind with label main" + +let label_order (prg: RISC.RISCAssembly.t) : string list = + let rec helper + (prg: RISC.RISCAssembly.risci list) + : string list = + match prg with + [] -> [] + | Label l :: tl -> l :: (helper tl) + | _ :: tl -> (helper tl) + in + helper (prg.code) + +let reduce_instructions (prg: RISCArchitecture.t) (lo: string list) : int = + let match_operator_r (brop: RISC.RISCAssembly.brop) = + match brop with + | Add -> (+) + | Sub -> (-) + | Mult -> ( * ) + | Div -> (/) + | Mod -> (mod) + | Pow -> (Utility.pow) + | And -> (Utility.int_and) + | Or -> (Utility.int_or) + | Eq -> (Utility.int_eq) + | Less -> (Utility.int_less) + | LessEq -> (Utility.int_less_eq) + | More -> (Utility.int_more) + | MoreEq -> (Utility.int_more_eq) + in + let match_operator_i (biop: RISC.RISCAssembly.biop) = + match biop with + | AddI -> (+) + | SubI -> (-) + | MultI -> ( * ) + | DivI -> (/) + | ModI -> (mod) + | PowI -> (Utility.pow) + | AndI -> (Utility.int_and) + | OrI -> (Utility.int_or) + | EqI -> (Utility.int_eq) + | LessI -> (Utility.int_less) + | LessEqI -> (Utility.int_less_eq) + | MoreI -> (Utility.int_more) + | MoreEqI -> (Utility.int_more_eq) + in + + let rec helper + (prg: RISCArchitecture.t) + (current: RISC.RISCAssembly.risci list) + (current_label: string) + : RISCArchitecture.t = + match current with + | [] -> ( + (* falls to the next label *) + match List.find_index ((=) current_label) lo with + None -> prg (* should never happen *) + | Some i -> + if i + 1 < (List.length lo) then + helper prg (CodeMap.find (List.nth lo (i+1)) prg.code) (List.nth lo (i+1)) + else + prg + ) + | Nop :: tl -> helper prg tl current_label + | BRegOp (brop, r1, r2, r3) :: tl -> ( + let n = (match_operator_r brop) + (RegisterMap.find {index = r1.index} prg.registers) + (RegisterMap.find {index = r2.index} prg.registers) + in + helper {prg with registers = RegisterMap.add {index = r3.index} n prg.registers} tl current_label + ) + | BImmOp (biop, r1, i, r3) :: tl -> ( + let n = (match_operator_i biop) + (RegisterMap.find {index = r1.index} prg.registers) + i + in + helper {prg with registers = RegisterMap.add {index = r3.index} n prg.registers} tl current_label + ) + | URegOp (urop, r1, r3) :: tl -> ( + match urop with + | Copy -> ( + let n = RegisterMap.find {index = r1.index} prg.registers in + helper {prg with registers = RegisterMap.add {index = r3.index} n prg.registers} tl current_label + ) + | Not -> ( + let n = Utility.int_not + (RegisterMap.find {index = r1.index} prg.registers) + in + helper {prg with registers = RegisterMap.add {index = r3.index} n prg.registers} tl current_label + ) + | Rand -> ( + let n = Random.int + (RegisterMap.find {index = r1.index} prg.registers) + in + helper {prg with registers = RegisterMap.add {index = r3.index} n prg.registers} tl current_label + ) + ) + | Load (r1, r3) :: tl -> ( + let n = MemoryMap.find + (RegisterMap.find {index = r1.index} prg.registers) + prg.memory + in + helper {prg with registers = RegisterMap.add {index = r3.index} n prg.registers} tl current_label + ) + | LoadI (r1, i) :: tl -> ( + let n = i + in + helper {prg with registers = RegisterMap.add {index = r1.index} n prg.registers} tl current_label + ) + | Store (r1, r3) :: tl -> ( + let n = RegisterMap.find {index = r1.index} prg.registers in + let n1 = RegisterMap.find {index = r3.index} prg.registers in + helper {prg with memory = MemoryMap.add n1 n prg.memory} tl current_label + ) + | Jump l :: _ -> helper prg (CodeMap.find l prg.code) l + | CJump (r, l1, l2) :: _ -> ( + let br = (RegisterMap.find {index = r.index} prg.registers) > 0 in + if br + then + helper prg (CodeMap.find l1 prg.code) l1 + else + helper prg (CodeMap.find l2 prg.code) l2 + ) + | Label _ :: tl -> helper prg tl current_label + in + RegisterMap.find + {index = "out"} + (helper prg (CodeMap.find "main" prg.code) "main").registers + + +let reduce (prg: RISC.RISCAssembly.t) : int = + reduce_instructions {code = convert prg; + registers = + RegisterMap.singleton + {index = "in"} + (Option.value prg.inputval ~default:0); + memory = MemoryMap.empty} (label_order prg) diff --git a/lib/miniImp/RISCSemantics.mli b/lib/miniImp/RISCSemantics.mli new file mode 100644 index 0000000..d47e286 --- /dev/null +++ b/lib/miniImp/RISCSemantics.mli @@ -0,0 +1,5 @@ +module RISCArchitecture : sig + type t +end + +val reduce : RISC.RISCAssembly.t -> int diff --git a/lib/miniImp/Types.ml b/lib/miniImp/Types.ml index db449d1..950f5ab 100644 --- a/lib/miniImp/Types.ml +++ b/lib/miniImp/Types.ml @@ -31,6 +31,42 @@ and a_exp = | PowerMod of a_exp * a_exp * a_exp (* a ^ a % a *) | Rand of a_exp (* rand(0, a) *) +let pp_p_exp (ppf: Format.formatter) (p: p_exp) : unit = + let rec helper_c (ppf) (c: c_exp) : unit = + match c with + Skip -> Format.fprintf ppf "Skip" + | Assignment (x, a) -> Format.fprintf ppf "%S := @[%a@]" x helper_a a + | Sequence (c1, c2) -> Format.fprintf ppf "@[Sequence (@;<1 2>%a,@;<1 0>%a@;<0 0>)@]" helper_c c1 helper_c c2 + | If (b, c1, c2) -> Format.fprintf ppf "@[If @[%a@]@;<1 2>then (@[%a@])@;<1 2>else (@[%a@])@]" helper_b b helper_c c1 helper_c c2 + | While (b, c) -> Format.fprintf ppf "@[While @[%a@] do@;<1 2>%a@]@;<0 0>" helper_b b helper_c c + | For (c1, b, c2, c3) -> Format.fprintf ppf "@[For (@;<0 2>%a,@;<1 2>@[%a@],@;<1 2>%a) do@]@;<1 4>%a@;<0 0>" helper_c c1 helper_b b helper_c c2 helper_c c3 + and helper_b (ppf) (b: b_exp) = + match b with + Boolean (b) -> Format.fprintf ppf "%b" b + | BAnd (b1, b2) -> Format.fprintf ppf "(%a &&@;<1 2>%a)" helper_b b1 helper_b b2 + | BOr (b1, b2) -> Format.fprintf ppf "(%a ||@;<1 2>%a)" helper_b b1 helper_b b2 + | BNot (b) -> Format.fprintf ppf "(not %a)" helper_b b + | BCmp (a1, a2) -> Format.fprintf ppf "(%a ==@;<1 2>%a)" helper_a a1 helper_a a2 + | BCmpLess (a1, a2) -> Format.fprintf ppf "(%a <@;<1 2>%a)" helper_a a1 helper_a a2 + | BCmpLessEq (a1, a2) -> Format.fprintf ppf "(%a <=@;<1 2>%a)" helper_a a1 helper_a a2 + | BCmpGreater (a1, a2) -> Format.fprintf ppf "(%a >@;<1 2>%a)" helper_a a1 helper_a a2 + | BCmpGreaterEq (a1, a2) -> Format.fprintf ppf "(%a >=@;<1 2>%a)" helper_a a1 helper_a a2 + and helper_a (ppf) (a: a_exp) = + match a with + Variable v -> Format.fprintf ppf "%S" v + | Integer n -> Format.fprintf ppf "%i" n + | Plus (a1, a2) -> Format.fprintf ppf "%a +@;<1 2>%a" helper_a a1 helper_a a2 + | Minus (a1, a2) -> Format.fprintf ppf "%a -@;<1 2>%a" helper_a a1 helper_a a2 + | Times (a1, a2) -> Format.fprintf ppf "%a *@;<1 2>%a" helper_a a1 helper_a a2 + | Division (a1, a2) -> Format.fprintf ppf "%a /@;<1 2>%a" helper_a a1 helper_a a2 + | Modulo (a1, a2) -> Format.fprintf ppf "%a %%@;<1 2>%a" helper_a a1 helper_a a2 + | Power (a1, a2) -> Format.fprintf ppf "(%a ^@;<1 2>%a)" helper_a a1 helper_a a2 + | PowerMod (a1, a2, a3) -> Format.fprintf ppf "(%a ^ %a %% %a)" helper_a a1 helper_a a2 helper_a a3 + | Rand (a) -> Format.fprintf ppf "Rand (%a)" helper_a a + in + match p with + | Main (i, o, exp) -> + Format.fprintf ppf "def main with (input %S) (output %S) as @.%a" i o helper_c exp module VariableMap = Map.Make(String) diff --git a/lib/miniImp/Types.mli b/lib/miniImp/Types.mli index 92a9a35..f318615 100644 --- a/lib/miniImp/Types.mli +++ b/lib/miniImp/Types.mli @@ -31,6 +31,7 @@ and a_exp = | PowerMod of a_exp * a_exp * a_exp (* a ^ a % a *) | Rand of a_exp (* rand(0, a) *) +val pp_p_exp : Format.formatter -> p_exp -> unit module VariableMap : Map.S with type key = variable diff --git a/lib/miniImp/dune b/lib/miniImp/dune index 48684a1..0f3a888 100644 --- a/lib/miniImp/dune +++ b/lib/miniImp/dune @@ -10,7 +10,10 @@ (library (name miniImp) (public_name miniImp) - (modules Lexer Parser Types Semantics CfgImp CfgRISC RISC) + (modules Lexer Parser Types Semantics + CfgImp ReplacePowerMod + CfgRISC + RISC RISCSemantics) (libraries cfg utility menhirLib)) (include_subdirs qualified) diff --git a/lib/miniImp/replacePowerMod.ml b/lib/miniImp/replacePowerMod.ml new file mode 100644 index 0000000..e2016ba --- /dev/null +++ b/lib/miniImp/replacePowerMod.ml @@ -0,0 +1,246 @@ +let rewrite_instructions (prg: Types.p_exp) : Types.p_exp = + (* function takes a program and replaces all occurrences of powermod with + simpler instructions *) + let i, o, prg = ( + match prg with + | Main (i, o, exp) -> i, o, exp + ) in + + let rec contains_rewrite (prg: Types.c_exp) : Types.a_exp option = + (* if the ast contains powermod anywhere returns the powermod, otherwise + returns none *) + match prg with + | Skip -> None + | Assignment (_, a) -> contains_rewrite_a a + | Sequence (c1, c2) -> ( + match contains_rewrite c1, contains_rewrite c2 with + | None, None -> None + | Some a, _ + | _, Some a -> Some a + ) + | If (b, c1, c2) -> ( + match contains_rewrite_b b, contains_rewrite c1, contains_rewrite c2 with + | None, None, None -> None + | Some a, _, _ + | _, Some a, _ + | _, _, Some a -> Some a + ) + | While (b, c) -> ( + match contains_rewrite_b b, contains_rewrite c with + | None, None -> None + | Some a, _ + | _, Some a -> Some a + ) + | For (c1, b, c2, c3) -> ( + match contains_rewrite c1, contains_rewrite_b b, contains_rewrite c2, contains_rewrite c3 with + | None, None, None, None -> None + | Some a, _, _, _ + | _, Some a, _, _ + | _, _, Some a, _ + | _, _, _, Some a -> Some a + ) + and contains_rewrite_b (b: Types.b_exp) : Types.a_exp option = + match b with + | Boolean (_) -> None + | BAnd (b1, b2) + | BOr (b1, b2) -> ( + match contains_rewrite_b b1, contains_rewrite_b b2 with + None, None -> None + | Some a, _ + | _, Some a -> Some a + ) + | BNot (b) -> contains_rewrite_b b + | BCmp (a1, a2) + | BCmpLess (a1, a2) + | BCmpLessEq (a1, a2) + | BCmpGreater (a1, a2) + | BCmpGreaterEq (a1, a2) -> ( + match contains_rewrite_a a1, contains_rewrite_a a2 with + None, None -> None + | Some a, _ + | _, Some a -> Some a + ) + and contains_rewrite_a (a: Types.a_exp) : Types.a_exp option = + match a with + | Variable _ + | Integer _ -> None + | Plus (a1, a2) + | Minus (a1, a2) + | Times (a1, a2) + | Division (a1, a2) + | Modulo (a1, a2) + | Power (a1, a2) -> ( + match contains_rewrite_a a1, contains_rewrite_a a2 with + None, None -> None + | Some a, _ + | _, Some a -> Some a + ) + | PowerMod (_) -> Some a + | Rand (a) -> contains_rewrite_a a + in + + (* obtain the list of used variables so that fresh ones can be created *) + let rec uv (prg: Types.c_exp) : Types.variable list = + match prg with + | Skip -> [] + | Assignment (x, _) -> [x] + | Sequence (c1, c2) -> uv c1 @ uv c2 + | If (_, c1, c2) -> uv c1 @ uv c2 + | While (_, c) -> uv c + | For (c1, _, c2, c3) -> uv c1 @ uv c2 @ uv c3 + in + let usedvariables = i :: o :: (uv prg) in + + let counter = ref 0 in + let new_fresh_var (pref: string) : Types.variable = + let rec h () = + let candidate = pref ^ (string_of_int !counter) in + if (List.mem candidate usedvariables) then ( + counter := !counter + 1; + h () + ) else ( + counter := !counter + 1; + candidate + ) + in + h () + in + + (* functions that replace a pattern in a subexpression *) + let rec replace_occurrence_a (pattern: Types.a_exp) (replace: Types.a_exp) (a: Types.a_exp) : Types.a_exp = + if a = pattern then + replace + else ( + let r_o_a = replace_occurrence_a pattern replace in + match a with + | Variable _ + | Integer _ -> a + | Plus (a1, a2) -> Plus (r_o_a a1, r_o_a a2) + | Minus (a1, a2) -> Minus (r_o_a a1, r_o_a a2) + | Times (a1, a2) -> Times (r_o_a a1, r_o_a a2) + | Division (a1, a2) -> Division (r_o_a a1, r_o_a a2) + | Modulo (a1, a2) -> Modulo (r_o_a a1, r_o_a a2) + | Power (a1, a2) -> Power (r_o_a a1, r_o_a a2) + | PowerMod (a1, a2, a3) -> PowerMod (r_o_a a1, r_o_a a2, r_o_a a3) + | Rand (a) -> Rand (r_o_a a) + ) + and replace_occurrence_b (pattern: Types.a_exp) (replace: Types.a_exp) (b: Types.b_exp) : Types.b_exp = + let r_o_b = replace_occurrence_b pattern replace in + let r_o_a = replace_occurrence_a pattern replace in + match b with + | Boolean _ -> b + | BAnd (b1, b2) -> BAnd (r_o_b b1, r_o_b b2) + | BOr (b1, b2) -> BOr (r_o_b b1, r_o_b b2) + | BNot (b) -> BNot (r_o_b b) + | BCmp (a1, a2) -> BCmp (r_o_a a1, r_o_a a2) + | BCmpLess (a1, a2) -> BCmpLess (r_o_a a1, r_o_a a2) + | BCmpLessEq (a1, a2) -> BCmpLessEq (r_o_a a1, r_o_a a2) + | BCmpGreater (a1, a2) -> BCmpGreater (r_o_a a1, r_o_a a2) + | BCmpGreaterEq (a1, a2) -> BCmpGreaterEq (r_o_a a1, r_o_a a2) + in + + (* function that creates the equivalent code for a powermod using simpler + instructions *) + let partial freshres a1 a2 a3 : Types.c_exp = + let freshpow = new_fresh_var "pow" in + let freshexp = new_fresh_var "exp" in + let freshmod = new_fresh_var "mod" in + Sequence ( + Sequence ( + Sequence ( + Assignment (freshpow, a1), + Assignment (freshexp, a2)), + Sequence ( + Assignment (freshmod, a3), + Assignment (freshres, Integer 1))), + Sequence ( + If (BCmpLess (Variable freshexp, Integer 0), + Assignment (freshexp, Minus (Integer 0, Variable freshexp)), + Skip), + While ( + BCmpGreater (Variable freshexp, Integer 0), + Sequence ( + If (BCmp (Integer 1, Modulo (Variable freshexp, Integer 2)), + Assignment (freshres, + Modulo (Times (Variable freshres, + Variable freshpow), + Variable freshmod)), + Skip), + Sequence ( + Assignment (freshpow, + Modulo (Times (Variable freshpow, + Variable freshpow), + Variable freshmod)), + Assignment (freshexp, Division (Variable freshexp, Integer 2)) + ))))) + in + + let replace_pwm (pwm: Types.a_exp) (p: Types.c_exp) : Types.c_exp = + match pwm, p with + | PowerMod (a1, a2, a3), Assignment (x, a) -> + let freshres = new_fresh_var "res" in + Sequence ( + partial freshres a1 a2 a3, + Assignment(x, replace_occurrence_a pwm (Variable freshres) a) + ) + | PowerMod (a1, a2, a3), If (b, ifa1, ifa2) -> + let freshres = new_fresh_var "res" in + Sequence ( + partial freshres a1 a2 a3, + If (replace_occurrence_b pwm (Variable freshres) b, ifa1, ifa2) + ) + | PowerMod (a1, a2, a3), While (b, wa) -> + let freshres = new_fresh_var "res" in + Sequence ( + partial freshres a1 a2 a3, + While (replace_occurrence_b pwm (Variable freshres) b, wa) + ) + | PowerMod (a1, a2, a3), For (fora1, b, fora2, fora3) -> + let freshres = new_fresh_var "res" in + Sequence ( + partial freshres a1 a2 a3, + For (fora1, replace_occurrence_b pwm (Variable freshres) b, fora2, fora3) + ) + | _ -> failwith "PowerMod is not present" + in + + let rec rw_a (prg: Types.c_exp) : Types.c_exp = + match prg with + | Skip -> Skip + | Assignment (x, a) -> ( + match contains_rewrite_a a with + None -> Assignment (x, a) + | Some (PowerMod (a1, a2, a3)) -> ( + replace_pwm (PowerMod (a1, a2, a3)) prg + ) + | Some _ -> failwith "Found powmod then lost it." + ) + | Sequence (c1, c2) -> Sequence (rw_a c1, rw_a c2) + | If (b, c1, c2) -> ( + match contains_rewrite_b b with + None -> If (b, rw_a c1, rw_a c2) + | Some (PowerMod (a1, a2, a3)) -> + replace_pwm (PowerMod (a1, a2, a3)) prg + | Some _ -> failwith "Found powmod then lost it." + ) + | While (b, c) -> ( + match contains_rewrite_b b with + None -> While (b, rw_a c) + | Some (PowerMod (a1, a2, a3)) -> + replace_pwm (PowerMod (a1, a2, a3)) prg + | Some _ -> failwith "Found powmod then lost it." + ) + | For (c1, b, c2, c3) -> ( + match contains_rewrite_b b with + None -> For (rw_a c1, b, rw_a c2, rw_a c3) + | Some (PowerMod (a1, a2, a3)) -> + replace_pwm (PowerMod (a1, a2, a3)) prg + | Some _ -> failwith "Found powmod then lost it." + ) + in + + (* we first check that at least one powermod is present *) + if Option.is_none (contains_rewrite prg) then + Main (i, o, prg) + else + Main (i, o, rw_a prg) diff --git a/lib/miniImp/replacePowerMod.mli b/lib/miniImp/replacePowerMod.mli new file mode 100644 index 0000000..3dd4900 --- /dev/null +++ b/lib/miniImp/replacePowerMod.mli @@ -0,0 +1 @@ +val rewrite_instructions : Types.p_exp -> Types.p_exp diff --git a/lib/utility/utility.ml b/lib/utility/utility.ml index 4cd3ed2..4553be5 100644 --- a/lib/utility/utility.ml +++ b/lib/utility/utility.ml @@ -12,6 +12,34 @@ let rec powmod a d = function let b = (powmod a d (n / 2)) mod d in (((b * b) mod d) * (if n mod 2 = 0 then 1 else a)) mod d +let int_and a b = + match (a>0, b>0) with + true, true -> 1 + | _, _ -> 0 + +let int_or a b = + match (a>0, b>0) with + false, false -> 0 + | _, _ -> 1 + +let int_eq a b = + if a = b then 1 else 0 + +let int_less a b = + if a < b then 1 else 0 + +let int_less_eq a b = + if a <= b then 1 else 0 + +let int_more a b = + if a > b then 1 else 0 + +let int_more_eq a b = + if a >= b then 1 else 0 + +let int_not a = + if a > 0 then 0 else 1 + let rec fromIntToString (alphabet: string) (x: int) : string = let base = String.length alphabet in if x < 0 then diff --git a/lib/utility/utility.mli b/lib/utility/utility.mli index f7cf347..4a7ad3e 100644 --- a/lib/utility/utility.mli +++ b/lib/utility/utility.mli @@ -2,4 +2,13 @@ val pow : int -> int -> int val powmod : int -> int -> int -> int +val int_and : int -> int -> int +val int_or : int -> int -> int +val int_eq : int -> int -> int +val int_less : int -> int -> int +val int_less_eq : int -> int -> int +val int_more : int -> int -> int +val int_more_eq : int -> int -> int +val int_not : int -> int + val fromIntToString : string -> int -> string diff --git a/test/dune b/test/dune index dbab5bc..c173233 100644 --- a/test/dune +++ b/test/dune @@ -6,6 +6,10 @@ (name testingImpParser) (libraries miniImp)) +(test + (name testingRISC) + (libraries miniImp)) + (test (name testingFun) (libraries miniFun)) @@ -16,4 +20,4 @@ (test (name testingTypeFunParser) - (libraries miniFun)) \ No newline at end of file + (libraries miniFun)) diff --git a/test/testingRISC.expected b/test/testingRISC.expected new file mode 100644 index 0000000..bc5b107 --- /dev/null +++ b/test/testingRISC.expected @@ -0,0 +1,8 @@ +Identity program: 1 +Factorial program: 3628800 +Hailstone sequence's lenght program: 351 +Sum multiples of 3 and 5 program: 35565945 +Rand program: true +Fibonacci program: 4807526976 +Miller-Rabin primality test program 1: 0 +Miller-Rabin primality test program 2: 1 diff --git a/test/testingRISC.ml b/test/testingRISC.ml new file mode 100644 index 0000000..3a35404 --- /dev/null +++ b/test/testingRISC.ml @@ -0,0 +1,128 @@ +open MiniImp + +let compute x i = + Lexing.from_string x |> + Parser.prg Lexer.lex |> + CfgImp.convert_io i |> + CfgRISC.convert |> + RISC.convert |> + RISCSemantics.reduce + + +(* -------------------------------------------------------------------------- *) +(* Identity program *) +let program = + "def main with input a output b as b := a" +;; + +Printf.printf "Identity program: "; +Printf.printf "%d\n" (compute program 1) +;; + +(* -------------------------------------------------------------------------- *) +(* Factorial program *) +let program = +"def main with input a output b as + b := 1; + for (i := 1, i <= a, i := i + 1) do + b := b * i; +" +;; + +Printf.printf "Factorial program: "; +Printf.printf "%d\n" (compute program 10) + +(* -------------------------------------------------------------------------- *) +(* Hailstone sequence's lenght program *) +let program = +"def main with input a output b as + b := 1; + while not a == 1 do ( + b := b + 1; + if ((a % 2) == 1) then a := 3 * a + 1 else a := a / 2 + ) +" +;; + +Printf.printf "Hailstone sequence's lenght program: "; +Printf.printf "%d\n" (compute program 77031) + +(* -------------------------------------------------------------------------- *) +(* Sum multiples of 3 and 5 program *) +let program = +"def main with input a output b as + b := 0; + for (i := 0, i <= a, i := i+1) do + if (i % 3 == 0 || i % 5 == 0) then b := b + i; + else skip; +" +;; + +Printf.printf "Sum multiples of 3 and 5 program: "; +Printf.printf "%d\n" (compute program 12345) + +(* -------------------------------------------------------------------------- *) +(* Rand program *) +let program = + "def main with input a output b as b := rand(a)" +;; + +Printf.printf "Rand program: "; +Printf.printf "%b\n" ((compute program 10) < 10) + +(* -------------------------------------------------------------------------- *) +(* Fibonacci program *) +let program = +"def main with input n output fnext as + fnow := 0; + fnext := 1; + while (n > 1) do ( + tmp := fnow + fnext; + fnow := fnext; + fnext := tmp; + n := n - 1; + ) +" +;; + +Printf.printf "Fibonacci program: "; +Printf.printf "%d\n" (compute program 48) + +(* -------------------------------------------------------------------------- *) +(* Miller-Rabin primality test program *) +let program = +"def main with input n output result as + if (n % 2) == 0 then result := 1 + else ( + + result := 0; + s := 0; + while (0 == ((n - 1) / (2 ^ s)) % 2) do ( + s := s + 1 + ); + d := ((n - 1) / 2 ^ s); + for (i := 20, i > 0, i := i - 1) do ( + a := rand(n - 4) + 2; + x := powmod(a, d, n); + for (j := 0, j < s, j := j+1) do ( + y := powmod(x, 2, n); + if (y == 1 && (not x == 1) && (not x == n - 1)) then + result := 1; + else + skip; + x := y; + ); + if not y == 1 then result := 1; + else skip; + ) + ) +" +;; + +(* should return 0 because prime *) +Printf.printf "Miller-Rabin primality test program 1: "; +Printf.printf "%d\n" (compute program 179424673); + +(* should return 1 because not prime *) +Printf.printf "Miller-Rabin primality test program 2: "; +Printf.printf "%d\n" (compute program 179424675); From 590123d988faf2fe17ee6cf6df3eb89a1eb4ddab Mon Sep 17 00:00:00 2001 From: elvis Date: Thu, 12 Dec 2024 16:37:36 +0100 Subject: [PATCH 09/29] Added dataflow module --- bin/dune | 4 +- bin/main.ml | 26 ++++-- dune-project | 4 +- lib/{cfg => analysis}/Cfg.ml | 43 ++++------ lib/{cfg => analysis}/Cfg.mli | 27 +++--- lib/analysis/Dataflow.ml | 136 +++++++++++++++++++++++++++++++ lib/analysis/Dataflow.mli | 29 +++++++ lib/analysis/dune | 6 ++ lib/cfg/dune | 5 -- lib/miniImp/CfgImp.ml | 5 +- lib/miniImp/CfgImp.mli | 2 + lib/miniImp/CfgRISC.ml | 2 + lib/miniImp/CfgRISC.mli | 2 + lib/miniImp/RISC.ml | 2 + lib/miniImp/definedVariables.ml | 20 +++++ lib/miniImp/definedVariables.mli | 15 ++++ lib/miniImp/dune | 4 +- 17 files changed, 275 insertions(+), 57 deletions(-) rename lib/{cfg => analysis}/Cfg.ml (89%) rename lib/{cfg => analysis}/Cfg.mli (65%) create mode 100644 lib/analysis/Dataflow.ml create mode 100644 lib/analysis/Dataflow.mli create mode 100644 lib/analysis/dune delete mode 100644 lib/cfg/dune create mode 100644 lib/miniImp/definedVariables.ml create mode 100644 lib/miniImp/definedVariables.mli diff --git a/bin/dune b/bin/dune index 02fa3b6..9f2f5b0 100644 --- a/bin/dune +++ b/bin/dune @@ -4,9 +4,9 @@ (libraries exercises miniImp miniFun - cfg + analysis utility) - (package miniFun) + (package miniImp) (modes byte exe) ) diff --git a/bin/main.ml b/bin/main.ml index c0edcd1..cdfc151 100644 --- a/bin/main.ml +++ b/bin/main.ml @@ -12,26 +12,40 @@ def main with input a output b as " in - Printf.printf "%s\n%s\n" (colorred "Program is") program; + (* Printf.printf "%s\n%s\n" (colorred "Program is") program; *) let get_result x = Lexing.from_string x |> Parser.prg Lexer.lex in let p = get_result program in - Format.printf "%s\n%a\n@?" (colorred "AST is") Types.pp_p_exp p; + (* Format.printf "%s\n%a\n@?" (colorred "AST is") Types.pp_p_exp p; *) let convertedcfg = CfgImp.convert_io 10 p in - Printf.printf "%s\n%a" (colorred "Converted CFG is") CfgImp.SSCfg.pp convertedcfg; + (* Printf.printf "%s\n%a" (colorred "Converted CFG is") CfgImp.SSCfg.pp convertedcfg; *) let convertedrisccfg = CfgRISC.convert convertedcfg in Printf.printf "%s\n%a" (colorred "Converted RISC CFG is") CfgRISC.RISCCfg.pp convertedrisccfg; + (* ---------------------------------- *) + + let analysiscfg = DefinedVariables.compute_defined_variables convertedrisccfg in + + Printf.printf "%s\n%a" (colorred "Analysis CFG is") DefinedVariables.DVCfg.pp analysiscfg; + + let convertedrisccfg = DefinedVariables.compute_cfg analysiscfg in + + Printf.printf "%s\n%a" (colorred "Converted RISC after analysis CFG is") CfgRISC.RISCCfg.pp convertedrisccfg; + + (* ---------------------------------- *) + let risc = RISC.convert convertedrisccfg in - Printf.printf "%s\n%a" (colorred "RISC code is") RISC.RISCAssembly.pp risc; + (* Printf.printf "%s\n%a" (colorred "RISC code is") RISC.RISCAssembly.pp risc; *) - let computerisc = RISCSemantics.reduce risc in + let _computerisc = RISCSemantics.reduce risc in - Printf.printf "%s\n%d\n" (colorred "Output of RISC code is") computerisc; + (* Printf.printf "%s\n%d\n" (colorred "Output of RISC code is") computerisc; *) + + () diff --git a/dune-project b/dune-project index bbe1b2e..a984e98 100644 --- a/dune-project +++ b/dune-project @@ -11,12 +11,12 @@ (depends ocaml dune)) (package - (name cfg) + (name analysis) (depends ocaml dune)) (package (name miniImp) - (depends ocaml dune utility)) + (depends ocaml dune utility analysis)) (package (name miniFun) diff --git a/lib/cfg/Cfg.ml b/lib/analysis/Cfg.ml similarity index 89% rename from lib/cfg/Cfg.ml rename to lib/analysis/Cfg.ml index f9b6fb8..55b2a5f 100644 --- a/lib/cfg/Cfg.ml +++ b/lib/analysis/Cfg.ml @@ -8,15 +8,14 @@ let globalIdNode = ref 0; module Node = struct type t = { - id: int + id: int; } let compare a b = compare a.id b.id let create () = globalIdNode := !globalIdNode + 1; - {id = !globalIdNode} + {id = !globalIdNode;} end -;; module NodeMap = struct include Map.Make(Node) @@ -29,19 +28,21 @@ end module NodeSet = Set.Make(Node) +type 'a cfginternal = { + empty: bool; + nodes: NodeSet.t; + edges: (Node.t * (Node.t option)) NodeMap.t; + reverseEdges: (Node.t list) NodeMap.t; + inputVal: int option; + inputOutputVar: (string * string) option; + initial: Node.t option; + terminal: Node.t option; + content: 'a list NodeMap.t; +} + module type C = sig type elt - type t = { - empty: bool; - nodes: NodeSet.t; - edges: (Node.t * (Node.t option)) NodeMap.t; - reverseEdges: (Node.t list) NodeMap.t; - inputVal: int option; - inputOutputVar: (string * string) option; - initial: Node.t option; - terminal: Node.t option; - content: elt list NodeMap.t - } + type t = elt cfginternal val empty : t val merge : t -> t -> Node.t -> Node.t -> t @@ -51,19 +52,9 @@ module type C = sig val pp : out_channel -> t -> unit end -module Make(M: PrintableType) = struct +module Make (M: PrintableType) = struct type elt = M.t - type t = { - empty: bool; - nodes: NodeSet.t; - edges: (Node.t * (Node.t option)) NodeMap.t; - reverseEdges: (Node.t list) NodeMap.t; - inputVal: int option; - inputOutputVar: (string * string) option; - initial: Node.t option; - terminal: Node.t option; - content: elt list NodeMap.t - } + type t = elt cfginternal let empty : t = { empty = true; diff --git a/lib/cfg/Cfg.mli b/lib/analysis/Cfg.mli similarity index 65% rename from lib/cfg/Cfg.mli rename to lib/analysis/Cfg.mli index d331259..ae1d790 100644 --- a/lib/cfg/Cfg.mli +++ b/lib/analysis/Cfg.mli @@ -6,7 +6,7 @@ end module Node : sig type t = { - id: int + id: int; } val compare : t -> t -> int val create : unit -> t @@ -20,19 +20,22 @@ end module NodeSet : Set.S with type elt = Node.t + +type 'a cfginternal = { + empty: bool; + nodes: NodeSet.t; + edges: (Node.t * (Node.t option)) NodeMap.t; + reverseEdges: (Node.t list) NodeMap.t; + inputVal: int option; + inputOutputVar: (string * string) option; + initial: Node.t option; + terminal: Node.t option; + content: 'a list NodeMap.t; +} + module type C = sig type elt - type t = { - empty: bool; - nodes: NodeSet.t; - edges: (Node.t * (Node.t option)) NodeMap.t; - reverseEdges: (Node.t list) NodeMap.t; - inputVal: int option; - inputOutputVar: (string * string) option; - initial: Node.t option; - terminal: Node.t option; - content: elt list NodeMap.t - } + type t = elt cfginternal val empty : t val merge : t -> t -> Node.t -> Node.t -> t diff --git a/lib/analysis/Dataflow.ml b/lib/analysis/Dataflow.ml new file mode 100644 index 0000000..f9041da --- /dev/null +++ b/lib/analysis/Dataflow.ml @@ -0,0 +1,136 @@ +module type C = sig + type elt + type internal + + type internalnode = { + internalin: internal list; + internalout: internal list; + internalbetween: internal list list; + } + + type cfgt = elt Cfg.cfginternal + + type t = { + t: cfgt; + internalvar: internalnode Cfg.NodeMap.t; + } + + val from_cfg : cfgt -> t + val to_cfg : t -> cfgt + + val fixed_point : ?init:(elt list -> internalnode) -> ?update:(t -> Cfg.Node.t -> internalnode) -> t -> t + + val pp : out_channel -> t -> unit +end + +module Make (M: Cfg.PrintableType) (I: Cfg.PrintableType) = struct + type elt = M.t + type internal = I.t + + type internalnode = { + internalin: internal list; + internalout: internal list; + internalbetween: internal list list; + } + + type cfgt = elt Cfg.cfginternal + + type t = { + t: cfgt; + internalvar: internalnode Cfg.NodeMap.t; + } + + let from_cfg (cfg: cfgt) : t = + {t = cfg; internalvar = Cfg.NodeMap.empty} + + let to_cfg ({t; _}: t) : cfgt = + t + + let fixed_point + ?(init : (elt list -> internalnode) = + (fun _ -> {internalin = []; internalout = []; internalbetween = []})) + ?(update : (t -> Cfg.Node.t -> internalnode) = + (fun t n -> Cfg.NodeMap.find n t.internalvar)) + (t: t) + : t = + (* init function is applied only once to each node content, + the update function takes the node and the whole structure and is + expected to return the updated structure for the appropriate node, + update function is applied to the resulting structure until no change is + observed + *) + let rec helper t = + let newt = + {t with + internalvar = Cfg.NodeMap.mapi (fun n _ -> update t n) t.internalvar} + in + if newt = t then newt else helper newt + in + helper { t with internalvar = Cfg.NodeMap.map init t.t.content } + + + open Cfg + let pp (ppf: out_channel) (c: t) : unit = + Printf.fprintf ppf "Cfg:\n"; + Printf.fprintf ppf "Nodes' ids: "; + List.iter (fun (x : Node.t) -> Printf.fprintf ppf "%d " x.id) (NodeSet.to_list c.t.nodes); + Printf.fprintf ppf "\n"; + + Printf.fprintf ppf "Nodes' edges:\n"; + List.iter (fun ((n, (a, b)) : (Node.t * (Node.t * Node.t option))) : unit -> + match b with None -> Printf.fprintf ppf "\t%d -> %d\n" n.id a.id + | Some b -> Printf.fprintf ppf "\t%d -> %d, %d\n" n.id a.id b.id + ) (NodeMap.to_list c.t.edges); + Printf.fprintf ppf "\n"; + + Printf.fprintf ppf "Nodes' back edges:\n"; + List.iter (fun ((n, xs) : (Node.t * (Node.t list))) : unit -> + Printf.fprintf ppf "\t%d -> " n.id; + List.iter (fun (x: Node.t) -> Printf.fprintf ppf "%d, " x.id) xs; + Printf.fprintf ppf "\n" + ) (NodeMap.to_list c.t.reverseEdges); + Printf.fprintf ppf "\n"; + + Printf.fprintf ppf "Input Value: "; + (match c.t.inputVal with + Some i -> Printf.fprintf ppf "%d" i; + | None -> Printf.fprintf ppf "None";); + Printf.fprintf ppf "\n"; + + Printf.fprintf ppf "Input and Output Vars: "; + (match c.t.inputOutputVar with + Some (i, o) -> Printf.fprintf ppf "(in: %s, out: %s)" i o; + | None -> Printf.fprintf ppf "None";); + Printf.fprintf ppf "\n"; + + Printf.fprintf ppf "Initial node's id: "; + (match c.t.initial with + Some i -> Printf.fprintf ppf "%d" (i.id); + | None -> Printf.fprintf ppf "None";); + Printf.fprintf ppf "\n"; + + Printf.fprintf ppf "Terminal node's id: "; + (match c.t.terminal with + Some i -> Printf.fprintf ppf "%d" (i.id); + | None -> Printf.fprintf ppf "None";); + Printf.fprintf ppf "\n"; + + Printf.fprintf ppf "Code:\n"; + List.iter (fun ((n, stms) : Node.t * elt list) : unit -> + Printf.fprintf ppf "\tid %d --> %a\n%!" n.id M.pplist stms + ) (NodeMap.to_list c.t.content); + Printf.fprintf ppf "\n"; + + Printf.fprintf ppf "Analysis structure:\n"; + List.iter (fun ((n, {internalin; internalout; internalbetween}) + : (Node.t * internalnode)) : unit -> + Printf.fprintf ppf "Node: %d\n" n.id; + Printf.fprintf ppf "Internal Input: "; + Printf.fprintf ppf "%a\n" I.pplist internalin; + Printf.fprintf ppf "Internal Output: "; + Printf.fprintf ppf "%a\n" I.pplist internalout; + Printf.fprintf ppf "Internal Between: "; + List.iter (Printf.fprintf ppf "%a;" I.pplist) internalbetween; + Printf.fprintf ppf "\n"; + ) (NodeMap.to_list c.internalvar); +end diff --git a/lib/analysis/Dataflow.mli b/lib/analysis/Dataflow.mli new file mode 100644 index 0000000..6b2173a --- /dev/null +++ b/lib/analysis/Dataflow.mli @@ -0,0 +1,29 @@ +module type C = sig + type elt + type internal + + type internalnode = { + internalin: internal list; + internalout: internal list; + internalbetween: internal list list; + } + + type cfgt = elt Cfg.cfginternal + + type t = { + t: cfgt; + internalvar: internalnode Cfg.NodeMap.t; + } + + val from_cfg : cfgt -> t + val to_cfg : t -> cfgt + + val fixed_point : ?init:(elt list -> internalnode) -> ?update:(t -> Cfg.Node.t -> internalnode) -> t -> t + + val pp : out_channel -> t -> unit +end + +module Make + (M: Cfg.PrintableType) + (I: Cfg.PrintableType) + : C with type elt = M.t and type internal = I.t diff --git a/lib/analysis/dune b/lib/analysis/dune new file mode 100644 index 0000000..fe21d54 --- /dev/null +++ b/lib/analysis/dune @@ -0,0 +1,6 @@ +(library + (name analysis) + (public_name analysis) + (modules Cfg Dataflow)) + +(include_subdirs qualified) diff --git a/lib/cfg/dune b/lib/cfg/dune deleted file mode 100644 index 772e4a7..0000000 --- a/lib/cfg/dune +++ /dev/null @@ -1,5 +0,0 @@ -(library - (name cfg) - (public_name cfg)) - -(include_subdirs qualified) diff --git a/lib/miniImp/CfgImp.ml b/lib/miniImp/CfgImp.ml index 295e5cd..54f58c6 100644 --- a/lib/miniImp/CfgImp.ml +++ b/lib/miniImp/CfgImp.ml @@ -1,4 +1,5 @@ -open Cfg +open Analysis +open Analysis.Cfg module SimpleStatements = struct type t = @@ -63,7 +64,7 @@ module SimpleStatements = struct List.iter (fun x -> pp ppf x; Printf.printf "; ") c end -module SSCfg = Cfg.Make(SimpleStatements) +module SSCfg = Cfg.Make(SimpleStatements) let rec convert_c (prevcfg: SSCfg.t) (prg: Types.c_exp) : SSCfg.t = let open SimpleStatements in diff --git a/lib/miniImp/CfgImp.mli b/lib/miniImp/CfgImp.mli index b980502..dfcbfc6 100644 --- a/lib/miniImp/CfgImp.mli +++ b/lib/miniImp/CfgImp.mli @@ -1,3 +1,5 @@ +open Analysis + module SimpleStatements : sig type t = | SimpleSkip diff --git a/lib/miniImp/CfgRISC.ml b/lib/miniImp/CfgRISC.ml index fe21435..ca51e35 100644 --- a/lib/miniImp/CfgRISC.ml +++ b/lib/miniImp/CfgRISC.ml @@ -1,3 +1,5 @@ +open Analysis + module RISCSimpleStatements = struct type register = { index: string diff --git a/lib/miniImp/CfgRISC.mli b/lib/miniImp/CfgRISC.mli index 95fd4fd..2d3d778 100644 --- a/lib/miniImp/CfgRISC.mli +++ b/lib/miniImp/CfgRISC.mli @@ -1,3 +1,5 @@ +open Analysis + module RISCSimpleStatements : sig type register = { index: string diff --git a/lib/miniImp/RISC.ml b/lib/miniImp/RISC.ml index bb6542a..4d59fe2 100644 --- a/lib/miniImp/RISC.ml +++ b/lib/miniImp/RISC.ml @@ -1,3 +1,5 @@ +open Analysis + let globalCounterLabel = ref 0 let nextLabel () : string = diff --git a/lib/miniImp/definedVariables.ml b/lib/miniImp/definedVariables.ml new file mode 100644 index 0000000..b998e13 --- /dev/null +++ b/lib/miniImp/definedVariables.ml @@ -0,0 +1,20 @@ +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 +end + +module RISCCfg = CfgRISC.RISCCfg + +module DVCfg = Dataflow.Make (CfgRISC.RISCSimpleStatements) (Variable) + +let compute_defined_variables (cfg: RISCCfg.t) : DVCfg.t = + DVCfg.from_cfg cfg + +let compute_cfg (dvcfg : DVCfg.t) : RISCCfg.t = + DVCfg.to_cfg dvcfg diff --git a/lib/miniImp/definedVariables.mli b/lib/miniImp/definedVariables.mli new file mode 100644 index 0000000..67d4ee4 --- /dev/null +++ b/lib/miniImp/definedVariables.mli @@ -0,0 +1,15 @@ +open Analysis + +module Variable : sig + type t + val pp : out_channel -> t -> unit +end + +module RISCCfg = CfgRISC.RISCCfg + +module DVCfg : Dataflow.C with type elt = CfgRISC.RISCSimpleStatements.t + and type internal = Variable.t + +val compute_defined_variables : RISCCfg.t -> DVCfg.t + +val compute_cfg : DVCfg.t -> RISCCfg.t diff --git a/lib/miniImp/dune b/lib/miniImp/dune index 0f3a888..b7451ca 100644 --- a/lib/miniImp/dune +++ b/lib/miniImp/dune @@ -12,8 +12,8 @@ (public_name miniImp) (modules Lexer Parser Types Semantics CfgImp ReplacePowerMod - CfgRISC + CfgRISC DefinedVariables RISC RISCSemantics) - (libraries cfg utility menhirLib)) + (libraries analysis utility menhirLib)) (include_subdirs qualified) From 25f9f12525d6ddaeeeb9cbbbda098b9515f05f11 Mon Sep 17 00:00:00 2001 From: elvis Date: Mon, 16 Dec 2024 05:15:33 +0100 Subject: [PATCH 10/29] Defined variables module, not fully working --- bin/main.ml | 20 ++- lib/analysis/Dataflow.ml | 25 +++- lib/analysis/Dataflow.mli | 2 +- lib/miniImp/CfgRISC.ml | 36 ++--- lib/miniImp/CfgRISC.mli | 2 +- lib/miniImp/RISC.ml | 12 +- lib/miniImp/RISC.mli | 2 +- lib/miniImp/RISCSemantics.ml | 4 +- lib/miniImp/definedVariables.ml | 238 ++++++++++++++++++++++++++++++- lib/miniImp/definedVariables.mli | 4 + lib/utility/utility.ml | 107 +++++++++++++- lib/utility/utility.mli | 33 +++-- 12 files changed, 435 insertions(+), 50 deletions(-) diff --git a/bin/main.ml b/bin/main.ml index cdfc151..86c7546 100644 --- a/bin/main.ml +++ b/bin/main.ml @@ -5,10 +5,13 @@ let colorred s = let () = let program = " -def main with input a output b as - b := 1; - for (i := 1, i <= a, i := i + 1) do - b := b * i; +def main with input n output result as + result := 0; + s := 0; + while (0 == ((n - 1) / (2 ^ s)) % 2) do ( + s := s + 1 + ); + " in @@ -34,17 +37,22 @@ def main with input a output b as Printf.printf "%s\n%a" (colorred "Analysis CFG is") DefinedVariables.DVCfg.pp analysiscfg; + Printf.printf "%s%b\n" (colorred "Analysis CFG defined variables: ") (DefinedVariables.check_defined_variables analysiscfg); + Printf.printf "%s\n" (colorred "Undefined Variables are:"); + List.iter (fun v -> Printf.printf "%a, " DefinedVariables.Variable.pp v) (DefinedVariables.undefined_variables analysiscfg); + Printf.printf "\n"; + let convertedrisccfg = DefinedVariables.compute_cfg analysiscfg in Printf.printf "%s\n%a" (colorred "Converted RISC after analysis CFG is") CfgRISC.RISCCfg.pp convertedrisccfg; (* ---------------------------------- *) - let risc = RISC.convert convertedrisccfg in + let _risc = RISC.convert convertedrisccfg in (* Printf.printf "%s\n%a" (colorred "RISC code is") RISC.RISCAssembly.pp risc; *) - let _computerisc = RISCSemantics.reduce risc in + (* let computerisc = RISCSemantics.reduce risc in *) (* Printf.printf "%s\n%d\n" (colorred "Output of RISC code is") computerisc; *) diff --git a/lib/analysis/Dataflow.ml b/lib/analysis/Dataflow.ml index f9041da..ace70be 100644 --- a/lib/analysis/Dataflow.ml +++ b/lib/analysis/Dataflow.ml @@ -5,7 +5,7 @@ module type C = sig type internalnode = { internalin: internal list; internalout: internal list; - internalbetween: internal list list; + internalbetween: (internal list * internal list) list; } type cfgt = elt Cfg.cfginternal @@ -30,7 +30,7 @@ module Make (M: Cfg.PrintableType) (I: Cfg.PrintableType) = struct type internalnode = { internalin: internal list; internalout: internal list; - internalbetween: internal list list; + internalbetween: (internal list * internal list) list; } type cfgt = elt Cfg.cfginternal @@ -66,7 +66,19 @@ module Make (M: Cfg.PrintableType) (I: Cfg.PrintableType) = struct in if newt = t then newt else helper newt in - helper { t with internalvar = Cfg.NodeMap.map init t.t.content } + let content = List.fold_left + (fun cfg node -> Cfg.NodeMap.add node {internalin = []; + internalout = []; + internalbetween = []} cfg) + Cfg.NodeMap.empty + (Cfg.NodeSet.to_list t.t.nodes) + in + let content = Cfg.NodeMap.union + (fun _ket _empty code -> Some code) + content + (Cfg.NodeMap.map init t.t.content) + in + helper { t with internalvar = content } open Cfg @@ -130,7 +142,10 @@ module Make (M: Cfg.PrintableType) (I: Cfg.PrintableType) = struct Printf.fprintf ppf "Internal Output: "; Printf.fprintf ppf "%a\n" I.pplist internalout; Printf.fprintf ppf "Internal Between: "; - List.iter (Printf.fprintf ppf "%a;" I.pplist) internalbetween; + List.iter (fun (i, o) -> + Printf.fprintf ppf "IN: %a;" I.pplist i; + Printf.fprintf ppf "OUT: %a;" I.pplist o;) internalbetween; Printf.fprintf ppf "\n"; - ) (NodeMap.to_list c.internalvar); + ) (NodeMap.to_list c.internalvar); + Printf.fprintf ppf "\n"; end diff --git a/lib/analysis/Dataflow.mli b/lib/analysis/Dataflow.mli index 6b2173a..f45667f 100644 --- a/lib/analysis/Dataflow.mli +++ b/lib/analysis/Dataflow.mli @@ -5,7 +5,7 @@ module type C = sig type internalnode = { internalin: internal list; internalout: internal list; - internalbetween: internal list list; + internalbetween: (internal list * internal list) list; } type cfgt = elt Cfg.cfginternal diff --git a/lib/miniImp/CfgRISC.ml b/lib/miniImp/CfgRISC.ml index ca51e35..f85fda2 100644 --- a/lib/miniImp/CfgRISC.ml +++ b/lib/miniImp/CfgRISC.ml @@ -11,7 +11,7 @@ module RISCSimpleStatements = struct | BImmOp of biop * register * int * register | URegOp of urop * register * register | Load of register * register - | LoadI of register * int + | LoadI of int * register | Store of register * register and brop = | Add @@ -54,7 +54,7 @@ module RISCSimpleStatements = struct | BImmOp (b, r1, i, r3) -> Printf.fprintf ppf "%a r%s %d => r%s" pp_biop b r1.index i r3.index | URegOp (u, r1, r2) -> Printf.fprintf ppf "%a r%s => r%s" pp_urop u r1.index r2.index | Load (r1, r2) -> Printf.fprintf ppf "Load r%s => r%s" r1.index r2.index - | LoadI (r2, i) -> Printf.fprintf ppf "LoadI %d => r%s" i r2.index + | LoadI (i, r2) -> Printf.fprintf ppf "LoadI %d => r%s" i r2.index | Store (r1, r2) -> Printf.fprintf ppf "Store r%s => r%s" r1.index r2.index and pp_brop (ppf: out_channel) (v: brop) : unit = match v with @@ -167,9 +167,9 @@ and c_ss_sb SimpleBoolean (b) -> ( let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in if b then - (convertedcode @ [LoadI (partialresreg, 1)], m) + (convertedcode @ [LoadI (1, partialresreg)], m) else - (convertedcode @ [LoadI (partialresreg, 0)], m) + (convertedcode @ [LoadI (0, partialresreg)], m) ) | SimpleBAnd (b1, b2) -> ( match (b1, b2) with @@ -179,7 +179,7 @@ and c_ss_sb ) | (SimpleBoolean (false), _) | (_, SimpleBoolean (false)) -> ( - (convertedcode @ [LoadI (register, 0)], m) + (convertedcode @ [LoadI (0, register)], m) ) | (_, _) -> ( let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in @@ -197,7 +197,7 @@ and c_ss_sb ) | (SimpleBoolean (true), _) | (_, SimpleBoolean (true)) -> ( - (LoadI (register, 1) :: convertedcode, m) + (LoadI (1, register) :: convertedcode, m) ) | (_, _) -> ( let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in @@ -211,9 +211,9 @@ and c_ss_sb match (b) with | SimpleBoolean (b) -> ( if b then - (LoadI (register, 0) :: convertedcode, m) + (LoadI (0, register) :: convertedcode, m) else - (LoadI (register, 1) :: convertedcode, m) + (LoadI (1, register) :: convertedcode, m) ) | _ -> ( let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in @@ -450,7 +450,7 @@ and c_ss_sa (convertedcode @ [URegOp (Copy, r1, register)], m) ) | SimpleInteger (i) -> ( - (convertedcode @ [LoadI (register, i)], m) + (convertedcode @ [LoadI (i, register)], m) ) | SimplePlus (a1, a2) -> ( match (a1, a2) with @@ -490,7 +490,7 @@ and c_ss_sa | (SimpleInteger (i), SimpleVariable (x)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in - (convertedcode @ [LoadI (partialresreg, i); BRegOp (Sub, partialresreg, xreg, register)], m) + (convertedcode @ [LoadI (i, partialresreg); BRegOp (Sub, partialresreg, xreg, register)], m) ) | (SimpleVariable (x), SimpleInteger (i)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in @@ -500,7 +500,7 @@ and c_ss_sa let partialresregi, m, _partialresvari = RegisterMap.get_fresh_register m in let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in - (convertedcode @ [LoadI (partialresregi, i); BRegOp (Sub, partialresregi, partialresreg, register)], m) + (convertedcode @ [LoadI (i, partialresregi); BRegOp (Sub, partialresregi, partialresreg, register)], m) ) | (a, SimpleInteger (i)) -> ( let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in @@ -570,7 +570,7 @@ and c_ss_sa | (SimpleInteger (i), SimpleVariable (x)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in - (convertedcode @ [LoadI (partialresreg, i); BRegOp (Div, partialresreg, xreg, register)], m) + (convertedcode @ [LoadI (i, partialresreg); BRegOp (Div, partialresreg, xreg, register)], m) ) | (SimpleVariable (x), SimpleInteger (i)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in @@ -580,7 +580,7 @@ and c_ss_sa let partialresregi, m, _partialresvari = RegisterMap.get_fresh_register m in let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in - (convertedcode @ [LoadI (partialresregi, i); BRegOp (Div, partialresregi, partialresreg, register)], m) + (convertedcode @ [LoadI (i, partialresregi); BRegOp (Div, partialresregi, partialresreg, register)], m) ) | (a, SimpleInteger (i)) -> ( let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in @@ -617,7 +617,7 @@ and c_ss_sa | (SimpleInteger (i), SimpleVariable (x)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in - (convertedcode @ [LoadI (partialresreg, i); BRegOp (Mod, partialresreg, xreg, register)], m) + (convertedcode @ [LoadI (i, partialresreg); BRegOp (Mod, partialresreg, xreg, register)], m) ) | (SimpleVariable (x), SimpleInteger (i)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in @@ -627,7 +627,7 @@ and c_ss_sa let partialresregi, m, _partialresvari = RegisterMap.get_fresh_register m in let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in - (convertedcode @ [LoadI (partialresregi, i); BRegOp (Mod, partialresregi, partialresreg, register)], m) + (convertedcode @ [LoadI (i, partialresregi); BRegOp (Mod, partialresregi, partialresreg, register)], m) ) | (a, SimpleInteger (i)) -> ( let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in @@ -664,7 +664,7 @@ and c_ss_sa | (SimpleInteger (i), SimpleVariable (x)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in - (convertedcode @ [LoadI (partialresreg, i); BRegOp (Pow, partialresreg, xreg, register)], m) + (convertedcode @ [LoadI (i, partialresreg); BRegOp (Pow, partialresreg, xreg, register)], m) ) | (SimpleVariable (x), SimpleInteger (i)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in @@ -674,7 +674,7 @@ and c_ss_sa let partialresregi, m, _partialresvari = RegisterMap.get_fresh_register m in let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in - (convertedcode @ [LoadI (partialresregi, i); BRegOp (Pow, partialresregi, partialresreg, register)], m) + (convertedcode @ [LoadI (i, partialresregi); BRegOp (Pow, partialresregi, partialresreg, register)], m) ) | (a, SimpleInteger (i)) -> ( let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in @@ -770,7 +770,7 @@ let convert (prg: CfgImp.SSCfg.t) : RISCCfg.t = edges = edges; reverseEdges = reverseEdges; inputVal = inputVal; - inputOutputVar = inputOutputVar; + inputOutputVar = Some ("in", "out"); initial = initial; terminal = terminal; content = helper content initial_bindings; diff --git a/lib/miniImp/CfgRISC.mli b/lib/miniImp/CfgRISC.mli index 2d3d778..53b56d7 100644 --- a/lib/miniImp/CfgRISC.mli +++ b/lib/miniImp/CfgRISC.mli @@ -11,7 +11,7 @@ module RISCSimpleStatements : sig | BImmOp of biop * register * int * register | URegOp of urop * register * register | Load of register * register - | LoadI of register * int + | LoadI of int * register | Store of register * register and brop = | Add diff --git a/lib/miniImp/RISC.ml b/lib/miniImp/RISC.ml index 4d59fe2..0480db6 100644 --- a/lib/miniImp/RISC.ml +++ b/lib/miniImp/RISC.ml @@ -19,7 +19,7 @@ module RISCAssembly = struct | BImmOp of biop * register * int * register | URegOp of urop * register * register | Load of register * register - | LoadI of register * int + | LoadI of int * register | Store of register * register | Jump of label | CJump of register * label * label @@ -70,7 +70,7 @@ module RISCAssembly = struct | BImmOp (b, r1, i, r3) -> Printf.fprintf ppf "\t%a r%s %d => r%s\n" pp_biop b r1.index i r3.index | URegOp (u, r1, r2) -> Printf.fprintf ppf "\t%a r%s => r%s\n" pp_urop u r1.index r2.index | Load (r1, r2) -> Printf.fprintf ppf "\tLoad r%s => r%s\n" r1.index r2.index - | LoadI (r2, i) -> Printf.fprintf ppf "\tLoadI %d => r%s\n" i r2.index + | LoadI (i, r2) -> Printf.fprintf ppf "\tLoadI %d => r%s\n" i r2.index | Store (r1, r2) -> Printf.fprintf ppf "\tStore r%s => r%s\n" r1.index r2.index | Jump (label) -> Printf.fprintf ppf "\tJump %s\n" label | CJump (r, l1, l2) -> Printf.fprintf ppf "\tCJump r%s => %s, %s\n" r.index l1 l2 @@ -139,8 +139,8 @@ let convert_cfgrisc_risci (i: CfgRISC.RISCSimpleStatements.t list) : (RISCAssemb {index = r3.index}) | Load (r1, r3) -> Load ({index = r1.index}, {index = r3.index}) - | LoadI (r3, imm) -> LoadI ({index = r3.index}, - imm) + | LoadI (imm, r3) -> LoadI (imm, + {index = r3.index}) | Store (r1, r3) -> Store ({index = r1.index}, {index = r3.index}) and helper_brop (brop: CfgRISC.RISCSimpleStatements.brop) : RISCAssembly.brop = @@ -247,7 +247,7 @@ let rec helper | BImmOp (_, _, _, r) | URegOp (_, _, r) | Load (_, r) - | LoadI (r, _) -> (([Label label1] : RISCAssembly.risci list) @ + | LoadI (_, r) -> (([Label label1] : RISCAssembly.risci list) @ currentcode @ ([CJump (r, label2, label3); Label label2] : RISCAssembly.risci list) @ res1 @ @@ -268,7 +268,7 @@ let rec helper | BImmOp (_, _, _, r) | URegOp (_, _, r) | Load (_, r) - | LoadI (r, _) -> (currentcode @ + | LoadI (_, r) -> (currentcode @ ([CJump (r, label1, label2); Label label1] : RISCAssembly.risci list) @ res1 @ ([Jump label3; Label label2] : RISCAssembly.risci list) @ diff --git a/lib/miniImp/RISC.mli b/lib/miniImp/RISC.mli index 6beb8a9..32edc36 100644 --- a/lib/miniImp/RISC.mli +++ b/lib/miniImp/RISC.mli @@ -10,7 +10,7 @@ module RISCAssembly : sig | BImmOp of biop * register * int * register | URegOp of urop * register * register | Load of register * register - | LoadI of register * int + | LoadI of int * register | Store of register * register | Jump of label | CJump of register * label * label diff --git a/lib/miniImp/RISCSemantics.ml b/lib/miniImp/RISCSemantics.ml index d01ff6e..d371482 100644 --- a/lib/miniImp/RISCSemantics.ml +++ b/lib/miniImp/RISCSemantics.ml @@ -142,10 +142,10 @@ let reduce_instructions (prg: RISCArchitecture.t) (lo: string list) : int = in helper {prg with registers = RegisterMap.add {index = r3.index} n prg.registers} tl current_label ) - | LoadI (r1, i) :: tl -> ( + | LoadI (i, r3) :: tl -> ( let n = i in - helper {prg with registers = RegisterMap.add {index = r1.index} n prg.registers} tl current_label + helper {prg with registers = RegisterMap.add {index = r3.index} n prg.registers} tl current_label ) | Store (r1, r3) :: tl -> ( let n = RegisterMap.find {index = r1.index} prg.registers in diff --git a/lib/miniImp/definedVariables.ml b/lib/miniImp/definedVariables.ml index b998e13..919771b 100644 --- a/lib/miniImp/definedVariables.ml +++ b/lib/miniImp/definedVariables.ml @@ -7,14 +7,250 @@ module Variable = struct 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 DVCfg = Dataflow.Make (CfgRISC.RISCSimpleStatements) (Variable) +module DVCeltSet = Set.Make(Variable) + + +let variables_used (instr : DVCfg.elt) : DVCfg.internal list = + let helper (acc: DVCeltSet.t) (instr: DVCfg.elt) = + match instr with + | Nop + | LoadI (_, _) -> + acc + | BRegOp (_, r1, r2, _) -> + DVCeltSet.add r1.index acc |> + DVCeltSet.add r2.index + | BImmOp (_, r1, _, _) + | URegOp (_, r1, _) + | Load (r1, _) + | Store (r1, _) -> + DVCeltSet.add r1.index acc + in + + helper DVCeltSet.empty instr |> DVCeltSet.to_list + +let variables_used_all (instructions : DVCfg.elt list) : DVCfg.internal list = + List.fold_left (fun (acc: DVCeltSet.t) (instr: DVCfg.elt) -> + DVCeltSet.union acc (variables_used instr |> DVCeltSet.of_list) + ) DVCeltSet.empty instructions |> DVCeltSet.to_list + + +let variables_defined (instructions : DVCfg.elt) : DVCfg.internal list = + let helper (acc: DVCeltSet.t) (instr: DVCfg.elt) = + match instr with + | Nop -> acc + | BRegOp (_, _, _, r3) + | BImmOp (_, _, _, r3) + | URegOp (_, _, r3) + | Load (_, r3) + | LoadI (_, r3) + | Store (_, r3) -> + DVCeltSet.add r3.index acc + in + + helper DVCeltSet.empty instructions |> DVCeltSet.to_list + +let variables_defined_all (instructions : DVCfg.elt list) : DVCfg.internal list = + List.fold_left (fun (acc: DVCeltSet.t) (instr: DVCfg.elt) -> + DVCeltSet.union acc (variables_defined instr |> DVCeltSet.of_list) + ) DVCeltSet.empty instructions |> DVCeltSet.to_list + +let _variables_defined_nth (instructions : DVCfg.elt list) (i: int) : DVCfg.internal list = + variables_defined (List.nth instructions i) + +let _variables_defined_last (instructions : DVCfg.elt list) : DVCfg.internal list = + variables_defined (List.nth instructions ((List.length instructions) - 1)) + + + + +(* init function, assign the epmpty set to everything *) +let init : (DVCfg.elt list -> DVCfg.internalnode) = + (fun l -> {internalin = []; + internalout = []; + internalbetween = (List.init (List.length l) (fun _ -> ([], [])))}) + + +(* piece of code that computes vout for the whole block, not used, + use lub below *) +let _dumb_lub (t: DVCfg.t) (node: Cfg.Node.t) : DVCfg.internalnode = + let previnternalvar = Cfg.NodeMap.find node t.internalvar in + let code = Cfg.NodeMap.find node t.t.content in + { previnternalvar with + internalout = + Utility.unique_union (variables_defined_all code) (previnternalvar.internalin) + } + + +(* We consider only the propagation in the middle elements during the lub. + This incurs in a performance penality, but it is simpler to implement. + Each node is connected to one previus node. +*) +let lub (t: DVCfg.t) (node: Cfg.Node.t) : DVCfg.internalnode = + let previnternalvar = Cfg.NodeMap.find node t.internalvar in + let code = match Cfg.NodeMap.find_opt node t.t.content with + None -> [] + | Some c -> c + in + { previnternalvar with + internalbetween = + List.mapi (* we don't NEED the index but i = 0 is easier to write than + to check if vinout is None *) + (fun i (ithcode, vinout, ithcodeprev) -> + if i = 0 then + let dvin = previnternalvar.internalin in + (dvin, Utility.unique_union dvin (variables_defined ithcode)) + else ( + let ithcodeprev = match ithcodeprev with + None -> ([], []) + | Some x -> x + in + match vinout with + None -> + ([], variables_defined ithcode) + | Some prevdvbtw -> + (snd prevdvbtw, + Utility.unique_union + (variables_defined ithcode) + (ithcodeprev |> fst) + )) + ) + (* ugly code that zips the three lists that we need to compute each vin + and vout for the middle of the code *) + (Utility.combine_thrice + code + (Utility.pad_opt + (Utility.prev previnternalvar.internalbetween None) None (List.length code)) + (Utility.pad previnternalvar.internalbetween None (List.length code)) + ); + internalout = + match previnternalvar.internalbetween with + [] -> previnternalvar.internalin + | _ -> (snd (Utility.last_list previnternalvar.internalbetween)) + } + +let lucf (t: DVCfg.t) (node: Cfg.Node.t) : DVCfg.internalnode = + let previnternalvar = Cfg.NodeMap.find node t.internalvar in + if Option.equal (=) (Some node) t.t.initial then + { previnternalvar with + internalin = + match t.t.inputOutputVar with + Some (i, _) -> [i] + | None -> [] + } + else + let prevnodes = Cfg.NodeMap.find node t.t.reverseEdges in + { previnternalvar with + internalin = + match prevnodes with + [] -> [] + | [prevnode] -> (Cfg.NodeMap.find prevnode t.internalvar).internalout + | [prevnode1; prevnode2] -> + Utility.unique_intersection + (Cfg.NodeMap.find prevnode1 t.internalvar).internalout + (Cfg.NodeMap.find prevnode2 t.internalvar).internalout + | _ -> + List.fold_left (* intersection of all previous nodes' dvout *) + (fun acc prevnode -> + Utility.unique_intersection acc (Cfg.NodeMap.find prevnode t.internalvar).internalout) + [] + prevnodes + } + + +let update (t: DVCfg.t) (node: Cfg.Node.t) : DVCfg.internalnode = + let newt = {t with internalvar = (Cfg.NodeMap.add node (lucf t node) t.internalvar)} in + lub newt node + let compute_defined_variables (cfg: RISCCfg.t) : DVCfg.t = DVCfg.from_cfg cfg + |> DVCfg.fixed_point ~init:init ~update:update -let compute_cfg (dvcfg : DVCfg.t) : RISCCfg.t = + + +let check_defined_variables (dvcfg: DVCfg.t) : bool = + let helper node (dvcfg: DVCfg.t) = + let code = match Cfg.NodeMap.find_opt node dvcfg.t.content with + None -> [] + | Some c -> c + in + let internalvar = Cfg.NodeMap.find node dvcfg.internalvar in + let vua = variables_used_all code in + + let outvar = (* is true if we are in the last node and the out variable is + not in vout, so its true if the out variable is not + defined *) + match (Option.equal (=) (Some node) dvcfg.t.terminal, + dvcfg.t.inputOutputVar, + internalvar.internalout) with + | (true, Some (_, outvar), vout) -> + not (List.mem outvar vout) + | (_, _, _) -> + false + in + + if Utility.inclusion vua (internalvar.internalin) then + not outvar + else + (* the variable might be defined inside the block, so check all vin and + return true only if all variables are properly defined *) + let vuabetween = List.map variables_used code in + let check = List.fold_left + (fun acc (codevars, (vin, _vout)) -> + acc && (Utility.inclusion codevars vin)) + true + (List.combine vuabetween internalvar.internalbetween) + in + check && (not outvar) + in + Cfg.NodeSet.fold (fun node acc -> acc && (helper node dvcfg)) dvcfg.t.nodes true + + +let undefined_variables (dvcfg: DVCfg.t) : Variable.t list = + let helper (node: Cfg.Node.t) (dvcfg: DVCfg.t) = + let code = match Cfg.NodeMap.find_opt node dvcfg.t.content with + None -> [] + | Some c -> c + in + let internalvar = Cfg.NodeMap.find node dvcfg.internalvar in + let vua = variables_used_all code in + + let outvar = + match (Option.equal (=) (Some node) dvcfg.t.terminal, + dvcfg.t.inputOutputVar, + internalvar.internalout) with + | (true, Some (_, outvar), vout) -> + if List.mem outvar vout + then None + else Some outvar + | (_, _, _) -> + None + in + + if Utility.inclusion vua (internalvar.internalin) then + match outvar with None -> [] | Some outvar -> [outvar] + else + (* the variable might be defined inside the block, so check all vin and + return true only if all variables are properly defined *) + let vuabetween = List.map variables_used code in + let undef_vars = List.fold_left + (fun acc (codevars, (vin, _vout)) -> + (Utility.subtraction codevars vin) @ acc) + [] + (List.combine vuabetween internalvar.internalbetween) + in + match outvar with None -> undef_vars | Some outvar -> outvar :: undef_vars + in + Cfg.NodeSet.fold (fun node acc -> acc @ (helper node dvcfg)) dvcfg.t.nodes [] + + +let compute_cfg (dvcfg: DVCfg.t) : RISCCfg.t = DVCfg.to_cfg dvcfg diff --git a/lib/miniImp/definedVariables.mli b/lib/miniImp/definedVariables.mli index 67d4ee4..bbb1e7c 100644 --- a/lib/miniImp/definedVariables.mli +++ b/lib/miniImp/definedVariables.mli @@ -13,3 +13,7 @@ module DVCfg : Dataflow.C with type elt = CfgRISC.RISCSimpleStatements.t val compute_defined_variables : RISCCfg.t -> DVCfg.t val compute_cfg : DVCfg.t -> RISCCfg.t + +val check_defined_variables : DVCfg.t -> bool + +val undefined_variables : DVCfg.t -> Variable.t list diff --git a/lib/utility/utility.ml b/lib/utility/utility.ml index 4553be5..758ba17 100644 --- a/lib/utility/utility.ml +++ b/lib/utility/utility.ml @@ -40,6 +40,7 @@ let int_more_eq a b = let int_not a = if a > 0 then 0 else 1 +(* converts an integer to a list of chars such that it is pretty and linear *) let rec fromIntToString (alphabet: string) (x: int) : string = let base = String.length alphabet in if x < 0 then @@ -47,4 +48,108 @@ let rec fromIntToString (alphabet: string) (x: int) : string = else if x < base then String.get alphabet x |> String.make 1 else - (fromIntToString (alphabet) (x/base - 1)) ^ (String.get alphabet (x mod base) |> String.make 1) + (fromIntToString (alphabet) (x/base - 1)) ^ (String.get alphabet (x mod base) + |> String.make 1) + + +let inclusion la lb = + let rec aux la = + function + [] -> true + | b::lb -> + if List.mem b la + then aux la lb + else false + in + aux lb la + +let subtraction la lb = + let rec aux la = + function + [] -> la + | b::lb -> + aux (List.filter ((<>) b) la) lb + in + aux la lb + +(* returns only the unique elements of l *) +let unique l = + let rec aux l acc = + match l with + | [] -> + List.rev acc + | h :: t -> + if List.mem h acc + then aux t acc + else aux t (h :: acc) + in + aux l [] + +(* returns the unique elements of the concat of the lists *) +let unique_union la lb = + unique (la @ lb) + +let unique_intersection la lb = + let rec aux la lb acc = + match la with + [] -> acc + | a::la -> + if List.mem a lb + then aux la lb (a::acc) + else aux la lb acc + in + aux la lb [] |> unique + + +(* Complicated way to drop the last element and add a new option element to the + beginning *) +let prev l a = + match l with + | [] -> + [a] + | _ -> + a :: (List.map (fun x -> Some x) (l |> List.rev |> List.tl |> List.rev)) + +let pad l a n = + let l = List.map (fun i -> Some i) l in + if List.length l < n + then + l @ (List.init (n - List.length l) (fun _ -> a)) + else + l + +let pad_opt l a n = + if List.length l < n + then + l @ (List.init (n - List.length l) (fun _ -> a)) + else + l + +let combine la lb = + List.map2 (fun a b -> + match b with + None -> None + | Some b -> Some (a, b) + ) la lb + +let rec last_list l = + match l with + [] -> failwith "Utility.last_list, not enough items" + | [a] -> a + | _::ll -> last_list ll + +let add_to_last_list (la: 'a list list) (a: 'a) : 'a list list = + let rec aux la a = + match la with + [] -> [[a]] + | [l] -> [a :: l] + | l::la -> l :: (aux la a) + in + aux la a + +let rec combine_thrice la lb lc = + match (la, lb, lc) with + | [], [], [] -> [] + | [a], [b], [c] -> [a, b, c] + | a::la, b::lb, c::lc -> (a, b, c) :: (combine_thrice la lb lc) + | _ -> [] diff --git a/lib/utility/utility.mli b/lib/utility/utility.mli index 4a7ad3e..fe02ef8 100644 --- a/lib/utility/utility.mli +++ b/lib/utility/utility.mli @@ -1,14 +1,31 @@ -val pow : int -> int -> int - +val pow : int -> int -> int val powmod : int -> int -> int -> int -val int_and : int -> int -> int -val int_or : int -> int -> int -val int_eq : int -> int -> int -val int_less : int -> int -> int +val int_and : int -> int -> int +val int_or : int -> int -> int +val int_eq : int -> int -> int +val int_less : int -> int -> int val int_less_eq : int -> int -> int -val int_more : int -> int -> int +val int_more : int -> int -> int val int_more_eq : int -> int -> int -val int_not : int -> int +val int_not : int -> int val fromIntToString : string -> int -> string + +val inclusion : 'a list -> 'a list -> bool +val subtraction : 'a list -> 'a list -> 'a list + +val unique : 'a list -> 'a list +val unique_union : 'a list -> 'a list -> 'a list +val unique_intersection : 'a list -> 'a list -> 'a list + +val prev : 'a list -> 'a option -> 'a option list + +val pad : 'a list -> 'a option -> int -> 'a option list +val pad_opt : 'a option list -> 'a option -> int -> 'a option list + +val combine : 'a list -> 'b option list -> ('a * 'b) option list +val last_list : 'a list -> 'a +val add_to_last_list : 'a list list -> 'a -> 'a list list + +val combine_thrice : 'a list -> 'b list -> 'c list -> ('a * 'b * 'c) list From 1f0d48263a6539b48644949426763826ff977f2e Mon Sep 17 00:00:00 2001 From: elvis Date: Mon, 16 Dec 2024 16:40:59 +0100 Subject: [PATCH 11/29] Switching to top instead of bottom for the defined variables --- bin/main.ml | 11 ++++--- lib/miniImp/definedVariables.ml | 51 ++++++++++++++++++++++++++------- 2 files changed, 46 insertions(+), 16 deletions(-) diff --git a/bin/main.ml b/bin/main.ml index 86c7546..21c2600 100644 --- a/bin/main.ml +++ b/bin/main.ml @@ -5,13 +5,12 @@ let colorred s = let () = let program = " -def main with input n output result as - result := 0; - s := 0; - while (0 == ((n - 1) / (2 ^ s)) % 2) do ( - s := s + 1 +def main with input n output out as + for (x := 2, x < 0, x := 2) do ( + y := x + 3; + x := y; ); - + out := 1 - y; " in diff --git a/lib/miniImp/definedVariables.ml b/lib/miniImp/definedVariables.ml index 919771b..0c2a86b 100644 --- a/lib/miniImp/definedVariables.ml +++ b/lib/miniImp/definedVariables.ml @@ -18,6 +18,32 @@ module DVCfg = Dataflow.Make (CfgRISC.RISCSimpleStatements) (Variable) module DVCeltSet = Set.Make(Variable) +let variables (instr : DVCfg.elt) : DVCfg.internal list = + let helper (acc: DVCeltSet.t) (instr: DVCfg.elt) = + match instr with + | Nop -> + acc + | BRegOp (_, r1, r2, r3) -> + DVCeltSet.add r1.index acc |> + DVCeltSet.add r2.index |> + DVCeltSet.add r3.index + | BImmOp (_, r1, _, r3) + | URegOp (_, r1, r3) + | Load (r1, r3) + | Store (r1, r3) -> + DVCeltSet.add r1.index acc |> + DVCeltSet.add r3.index + | LoadI (_, r3) -> + DVCeltSet.add r3.index acc + in + + helper DVCeltSet.empty instr |> DVCeltSet.to_list + +let variables_all (instructions : DVCfg.elt list) : DVCfg.internal list = + List.fold_left (fun (acc: DVCeltSet.t) (instr: DVCfg.elt) -> + DVCeltSet.union acc (variables instr |> DVCeltSet.of_list) + ) DVCeltSet.empty instructions |> DVCeltSet.to_list + let variables_used (instr : DVCfg.elt) : DVCfg.internal list = let helper (acc: DVCeltSet.t) (instr: DVCfg.elt) = match instr with @@ -62,21 +88,21 @@ let variables_defined_all (instructions : DVCfg.elt list) : DVCfg.internal list DVCeltSet.union acc (variables_defined instr |> DVCeltSet.of_list) ) DVCeltSet.empty instructions |> DVCeltSet.to_list -let _variables_defined_nth (instructions : DVCfg.elt list) (i: int) : DVCfg.internal list = - variables_defined (List.nth instructions i) - -let _variables_defined_last (instructions : DVCfg.elt list) : DVCfg.internal list = - variables_defined (List.nth instructions ((List.length instructions) - 1)) - - -(* init function, assign the epmpty set to everything *) -let init : (DVCfg.elt list -> DVCfg.internalnode) = +(* init function, assign the bottom to everything *) +let _init_bottom : (DVCfg.elt list -> DVCfg.internalnode) = (fun l -> {internalin = []; internalout = []; internalbetween = (List.init (List.length l) (fun _ -> ([], [])))}) +(* init function, assign the top to everything *) +let init_top (all_variables) : (DVCfg.elt list -> DVCfg.internalnode) = + (fun l -> {internalin = all_variables; + internalout = all_variables; + internalbetween = (List.init (List.length l) + (fun _ -> (all_variables, all_variables)))}) + (* piece of code that computes vout for the whole block, not used, use lub below *) @@ -171,8 +197,13 @@ let update (t: DVCfg.t) (node: Cfg.Node.t) : DVCfg.internalnode = let compute_defined_variables (cfg: RISCCfg.t) : DVCfg.t = + let all_variables = List.fold_left + (fun acc (_, code) -> Utility.unique_union acc (variables_all code)) + [] + (Cfg.NodeMap.to_list cfg.content) + in DVCfg.from_cfg cfg - |> DVCfg.fixed_point ~init:init ~update:update + |> DVCfg.fixed_point ~init:(init_top all_variables) ~update:update From f1b4c3a17ddb90bf7b266c99610703f496758fae Mon Sep 17 00:00:00 2001 From: elvis Date: Sat, 21 Dec 2024 02:16:04 +0100 Subject: [PATCH 12/29] Live Variables --- bin/main.ml | 33 ++++--- lib/miniImp/definedVariables.ml | 59 ++++--------- lib/miniImp/definedVariables.mli | 4 +- lib/miniImp/dune | 2 +- lib/miniImp/liveVariables.ml | 145 +++++++++++++++++++++++++++++++ lib/miniImp/liveVariables.mli | 18 ++++ lib/utility/utility.ml | 7 +- lib/utility/utility.mli | 1 + 8 files changed, 209 insertions(+), 60 deletions(-) create mode 100644 lib/miniImp/liveVariables.ml create mode 100644 lib/miniImp/liveVariables.mli diff --git a/bin/main.ml b/bin/main.ml index 21c2600..03a8a0f 100644 --- a/bin/main.ml +++ b/bin/main.ml @@ -5,12 +5,15 @@ let colorred s = let () = let program = " -def main with input n output out as - for (x := 2, x < 0, x := 2) do ( - y := x + 3; - x := y; - ); - out := 1 - y; +def main with input c output m as + a := 0; + b := a + 1; + c := c + b; + a := b * 2; + if a < 3 then + c := 4 + else + c := 6 " in @@ -34,16 +37,22 @@ def main with input n output out as let analysiscfg = DefinedVariables.compute_defined_variables convertedrisccfg in - Printf.printf "%s\n%a" (colorred "Analysis CFG is") DefinedVariables.DVCfg.pp analysiscfg; + (* Printf.printf "%s\n%a" (colorred "Analysis CFG is") DefinedVariables.DVCfg.pp analysiscfg; *) - Printf.printf "%s%b\n" (colorred "Analysis CFG defined variables: ") (DefinedVariables.check_defined_variables analysiscfg); - Printf.printf "%s\n" (colorred "Undefined Variables are:"); - List.iter (fun v -> Printf.printf "%a, " DefinedVariables.Variable.pp v) (DefinedVariables.undefined_variables analysiscfg); - Printf.printf "\n"; + (* Printf.printf "%s\n" (colorred "Undefined Variables are:"); *) + (* List.iter (fun v -> Printf.printf "%a, " DefinedVariables.Variable.pp v) (DefinedVariables.check_undefined_variables analysiscfg); *) + (* Printf.printf "\n"; *) let convertedrisccfg = DefinedVariables.compute_cfg analysiscfg in - Printf.printf "%s\n%a" (colorred "Converted RISC after analysis CFG is") CfgRISC.RISCCfg.pp convertedrisccfg; + (* Printf.printf "%s\n%a" (colorred "Converted RISC after analysis CFG is") CfgRISC.RISCCfg.pp convertedrisccfg; *) + + + let analysiscfg = LiveVariables.compute_live_variables convertedrisccfg in + + Printf.printf "%s\n%a" (colorred "Analysis CFG is") LiveVariables.DVCfg.pp analysiscfg; + + let convertedrisccfg = LiveVariables.optimize_cfg analysiscfg |> LiveVariables.compute_cfg in (* ---------------------------------- *) diff --git a/lib/miniImp/definedVariables.ml b/lib/miniImp/definedVariables.ml index 0c2a86b..1b39ac5 100644 --- a/lib/miniImp/definedVariables.ml +++ b/lib/miniImp/definedVariables.ml @@ -207,46 +207,8 @@ let compute_defined_variables (cfg: RISCCfg.t) : DVCfg.t = -let check_defined_variables (dvcfg: DVCfg.t) : bool = - let helper node (dvcfg: DVCfg.t) = - let code = match Cfg.NodeMap.find_opt node dvcfg.t.content with - None -> [] - | Some c -> c - in - let internalvar = Cfg.NodeMap.find node dvcfg.internalvar in - let vua = variables_used_all code in - - let outvar = (* is true if we are in the last node and the out variable is - not in vout, so its true if the out variable is not - defined *) - match (Option.equal (=) (Some node) dvcfg.t.terminal, - dvcfg.t.inputOutputVar, - internalvar.internalout) with - | (true, Some (_, outvar), vout) -> - not (List.mem outvar vout) - | (_, _, _) -> - false - in - - if Utility.inclusion vua (internalvar.internalin) then - not outvar - else - (* the variable might be defined inside the block, so check all vin and - return true only if all variables are properly defined *) - let vuabetween = List.map variables_used code in - let check = List.fold_left - (fun acc (codevars, (vin, _vout)) -> - acc && (Utility.inclusion codevars vin)) - true - (List.combine vuabetween internalvar.internalbetween) - in - check && (not outvar) - in - Cfg.NodeSet.fold (fun node acc -> acc && (helper node dvcfg)) dvcfg.t.nodes true - - -let undefined_variables (dvcfg: DVCfg.t) : Variable.t list = - let helper (node: Cfg.Node.t) (dvcfg: DVCfg.t) = +let check_undefined_variables (dvcfg: DVCfg.t) : Variable.t list option = + let helper (node: Cfg.Node.t) (dvcfg: DVCfg.t) : Variable.t list option = let code = match Cfg.NodeMap.find_opt node dvcfg.t.content with None -> [] | Some c -> c @@ -267,7 +229,8 @@ let undefined_variables (dvcfg: DVCfg.t) : Variable.t list = in if Utility.inclusion vua (internalvar.internalin) then - match outvar with None -> [] | Some outvar -> [outvar] + match outvar with None -> None + | Some outvar -> Some [outvar] else (* the variable might be defined inside the block, so check all vin and return true only if all variables are properly defined *) @@ -278,9 +241,19 @@ let undefined_variables (dvcfg: DVCfg.t) : Variable.t list = [] (List.combine vuabetween internalvar.internalbetween) in - match outvar with None -> undef_vars | Some outvar -> outvar :: undef_vars + match outvar, undef_vars with + None, [] -> None + | None, undef_vars -> Some undef_vars + | Some outvar, [] -> Some [outvar] + | Some outvar, undef_vars -> Some (outvar :: undef_vars) in - Cfg.NodeSet.fold (fun node acc -> acc @ (helper node dvcfg)) dvcfg.t.nodes [] + Cfg.NodeSet.fold (fun node acc -> + match acc, (helper node dvcfg) with + None, None -> None + | None, Some x -> Some x + | Some acc, None -> Some acc + | Some acc, Some x -> Some (acc @ x) + ) dvcfg.t.nodes None let compute_cfg (dvcfg: DVCfg.t) : RISCCfg.t = diff --git a/lib/miniImp/definedVariables.mli b/lib/miniImp/definedVariables.mli index bbb1e7c..cc8e4ea 100644 --- a/lib/miniImp/definedVariables.mli +++ b/lib/miniImp/definedVariables.mli @@ -14,6 +14,4 @@ val compute_defined_variables : RISCCfg.t -> DVCfg.t val compute_cfg : DVCfg.t -> RISCCfg.t -val check_defined_variables : DVCfg.t -> bool - -val undefined_variables : DVCfg.t -> Variable.t list +val check_undefined_variables : DVCfg.t -> Variable.t list option diff --git a/lib/miniImp/dune b/lib/miniImp/dune index b7451ca..b3f830a 100644 --- a/lib/miniImp/dune +++ b/lib/miniImp/dune @@ -12,7 +12,7 @@ (public_name miniImp) (modules Lexer Parser Types Semantics CfgImp ReplacePowerMod - CfgRISC DefinedVariables + CfgRISC DefinedVariables LiveVariables RISC RISCSemantics) (libraries analysis utility menhirLib)) diff --git a/lib/miniImp/liveVariables.ml b/lib/miniImp/liveVariables.ml new file mode 100644 index 0000000..cc2b385 --- /dev/null +++ b/lib/miniImp/liveVariables.ml @@ -0,0 +1,145 @@ +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 DVCfg = Dataflow.Make (CfgRISC.RISCSimpleStatements) (Variable) +module DVCeltSet = Set.Make(Variable) + + +let variables_used (instr : DVCfg.elt) : DVCfg.internal list = + let helper (acc: DVCeltSet.t) (instr: DVCfg.elt) = + match instr with + | Nop + | LoadI (_, _) -> + acc + | BRegOp (_, r1, r2, _) -> + DVCeltSet.add r1.index acc |> + DVCeltSet.add r2.index + | BImmOp (_, r1, _, _) + | URegOp (_, r1, _) + | Load (r1, _) + | Store (r1, _) -> + DVCeltSet.add r1.index acc + in + + helper DVCeltSet.empty instr |> DVCeltSet.to_list + +let _variables_used_all (instructions : DVCfg.elt list) : DVCfg.internal list = + List.fold_left (fun (acc: DVCeltSet.t) (instr: DVCfg.elt) -> + DVCeltSet.union acc (variables_used instr |> DVCeltSet.of_list) + ) DVCeltSet.empty instructions |> DVCeltSet.to_list + + +let variables_defined (instructions : DVCfg.elt) : DVCfg.internal list = + let helper (acc: DVCeltSet.t) (instr: DVCfg.elt) = + match instr with + | Nop -> acc + | BRegOp (_, _, _, r3) + | BImmOp (_, _, _, r3) + | URegOp (_, _, r3) + | Load (_, r3) + | LoadI (_, r3) + | Store (_, r3) -> + DVCeltSet.add r3.index acc + in + + helper DVCeltSet.empty instructions |> DVCeltSet.to_list + +let _variables_defined_all (instructions : DVCfg.elt list) : DVCfg.internal list = + List.fold_left (fun (acc: DVCeltSet.t) (instr: DVCfg.elt) -> + DVCeltSet.union acc (variables_defined instr |> DVCeltSet.of_list) + ) DVCeltSet.empty instructions |> DVCeltSet.to_list + + +(* init function, assign the bottom to everything *) +let init : (DVCfg.elt list -> DVCfg.internalnode) = + (fun l -> {internalin = []; + internalout = []; + internalbetween = (List.init (List.length l) (fun _ -> ([], [])))}) + +let lub (t: DVCfg.t) (node: Cfg.Node.t) : DVCfg.internalnode = + let previnternalvar = Cfg.NodeMap.find node t.internalvar in + let code = match Cfg.NodeMap.find_opt node t.t.content with + None -> [] + | Some c -> c + in + { previnternalvar with + internalbetween = + List.map (fun (prevbtw, code, nextprevbtw) -> + let newin = Utility.unique_union (variables_used code) + (Utility.subtraction (snd prevbtw) (variables_defined code)) + in + match nextprevbtw with + None -> (newin, snd prevbtw) + | Some (newout, _) -> (newin, newout) + ) + (Utility.combine_thrice previnternalvar.internalbetween code + (Utility.pad (List.tl previnternalvar.internalbetween) None (List.length previnternalvar.internalbetween))) + ; + internalin = fst (List.hd previnternalvar.internalbetween); + } + +let lucf (t: DVCfg.t) (node: Cfg.Node.t) : DVCfg.internalnode = + let previnternalvar = Cfg.NodeMap.find node t.internalvar in + if Option.equal (=) (Some node) t.t.terminal then + let outputvarlist = match t.t.inputOutputVar with + Some (_, o) -> [o] + | None -> [] + in + { previnternalvar with + internalout = outputvarlist; + internalbetween = ( + let last_elem = Utility.last_list previnternalvar.internalbetween in + (Utility.drop_last_element_list previnternalvar.internalbetween) @ + [(fst last_elem, outputvarlist)] + ) + } + else + let nextnodes = Cfg.NodeMap.find_opt node t.t.edges in + let newinternalout = match nextnodes with + None -> [] + | Some (node, None) -> (Cfg.NodeMap.find node t.internalvar).internalin + | Some (node1, Some node2) -> + Utility.unique_union + (Cfg.NodeMap.find node1 t.internalvar).internalin + (Cfg.NodeMap.find node2 t.internalvar).internalin + in + { previnternalvar with + internalout = newinternalout; + internalbetween = ( + let last_elem = Utility.last_list previnternalvar.internalbetween in + (Utility.drop_last_element_list previnternalvar.internalbetween) @ + [(fst last_elem, newinternalout)] + ) + } + +let update (t: DVCfg.t) (node: Cfg.Node.t) : DVCfg.internalnode = + let newt = {t with internalvar = (Cfg.NodeMap.add node (lucf t node) t.internalvar)} in + lub newt node + + +let compute_live_variables (cfg: RISCCfg.t) : DVCfg.t = + DVCfg.from_cfg cfg + |> DVCfg.fixed_point ~init:init ~update:update + + +(* just rename the registers that dont share live status *) +let optimize_cfg (t: DVCfg.t) : DVCfg.t = + t + + + +let compute_cfg (dvcfg: DVCfg.t) : RISCCfg.t = + DVCfg.to_cfg dvcfg diff --git a/lib/miniImp/liveVariables.mli b/lib/miniImp/liveVariables.mli new file mode 100644 index 0000000..86fb8f4 --- /dev/null +++ b/lib/miniImp/liveVariables.mli @@ -0,0 +1,18 @@ +open Analysis + +module Variable : sig + type t + val pp : out_channel -> t -> unit +end + +module RISCCfg = CfgRISC.RISCCfg + +module DVCfg : Dataflow.C with type elt = CfgRISC.RISCSimpleStatements.t + and type internal = Variable.t + + +val compute_live_variables : RISCCfg.t -> DVCfg.t + +val optimize_cfg : DVCfg.t -> DVCfg.t + +val compute_cfg : DVCfg.t -> RISCCfg.t diff --git a/lib/utility/utility.ml b/lib/utility/utility.ml index 758ba17..52cad39 100644 --- a/lib/utility/utility.ml +++ b/lib/utility/utility.ml @@ -101,6 +101,11 @@ let unique_intersection la lb = aux la lb [] |> unique +let drop_last_element_list = + function + | [] -> [] + | l -> l |> List.rev |> List.tl |> List.rev + (* Complicated way to drop the last element and add a new option element to the beginning *) let prev l a = @@ -108,7 +113,7 @@ let prev l a = | [] -> [a] | _ -> - a :: (List.map (fun x -> Some x) (l |> List.rev |> List.tl |> List.rev)) + a :: (List.map (fun x -> Some x) (drop_last_element_list l)) let pad l a n = let l = List.map (fun i -> Some i) l in diff --git a/lib/utility/utility.mli b/lib/utility/utility.mli index fe02ef8..9d77eee 100644 --- a/lib/utility/utility.mli +++ b/lib/utility/utility.mli @@ -19,6 +19,7 @@ val unique : 'a list -> 'a list val unique_union : 'a list -> 'a list -> 'a list val unique_intersection : 'a list -> 'a list -> 'a list +val drop_last_element_list : 'a list -> 'a list val prev : 'a list -> 'a option -> 'a option list val pad : 'a list -> 'a option -> int -> 'a option list From 3be05222ab8ed2b1583a74d29f804cac0cd7ba27 Mon Sep 17 00:00:00 2001 From: elvis Date: Fri, 27 Dec 2024 21:11:38 +0100 Subject: [PATCH 13/29] Fixes defined variables, fixes live variables, implements reduces registers, fixes risc semantic --- bin/main.ml | 48 ++-- dune-project | 2 +- lib/analysis/Dataflow.ml | 104 +++++--- lib/analysis/dune | 3 +- lib/miniImp/RISC.ml | 10 +- lib/miniImp/RISC.mli | 3 +- lib/miniImp/RISCSemantics.ml | 38 ++- lib/miniImp/definedVariables.ml | 164 ++++++------ lib/miniImp/definedVariables.mli | 1 + lib/miniImp/dune | 1 + lib/miniImp/liveVariables.ml | 242 +++++++++++++----- lib/miniImp/reduceRegisters.ml | 416 +++++++++++++++++++++++++++++++ lib/miniImp/reduceRegisters.mli | 3 + lib/utility/utility.ml | 38 ++- lib/utility/utility.mli | 7 +- 15 files changed, 866 insertions(+), 214 deletions(-) create mode 100644 lib/miniImp/reduceRegisters.ml create mode 100644 lib/miniImp/reduceRegisters.mli diff --git a/bin/main.ml b/bin/main.ml index 03a8a0f..e0e6c0e 100644 --- a/bin/main.ml +++ b/bin/main.ml @@ -5,15 +5,11 @@ let colorred s = let () = let program = " -def main with input c output m as - a := 0; - b := a + 1; - c := c + b; - a := b * 2; - if a < 3 then - c := 4 - else - c := 6 +def main with input in output out as + out := in; + a := 1; + b := 2; + c := a + a; " in @@ -39,8 +35,12 @@ def main with input c output m as (* Printf.printf "%s\n%a" (colorred "Analysis CFG is") DefinedVariables.DVCfg.pp analysiscfg; *) - (* Printf.printf "%s\n" (colorred "Undefined Variables are:"); *) - (* List.iter (fun v -> Printf.printf "%a, " DefinedVariables.Variable.pp v) (DefinedVariables.check_undefined_variables analysiscfg); *) + (* Printf.printf "%s" (colorred "Undefined Variables are:"); *) + (* ( *) + (* match DefinedVariables.check_undefined_variables analysiscfg with *) + (* | None -> Printf.printf " none"; *) + (* | Some l -> Printf.printf " %a" DefinedVariables.Variable.pplist l; *) + (* ); *) (* Printf.printf "\n"; *) let convertedrisccfg = DefinedVariables.compute_cfg analysiscfg in @@ -48,20 +48,32 @@ def main with input c output m as (* Printf.printf "%s\n%a" (colorred "Converted RISC after analysis CFG is") CfgRISC.RISCCfg.pp convertedrisccfg; *) - let analysiscfg = LiveVariables.compute_live_variables convertedrisccfg in + (* let analysiscfg = LiveVariables.compute_live_variables convertedrisccfg in *) - Printf.printf "%s\n%a" (colorred "Analysis CFG is") LiveVariables.DVCfg.pp analysiscfg; + (* Printf.printf "%s\n%a" (colorred "Live Analysis CFG is") LiveVariables.DVCfg.pp analysiscfg; *) + + (* let convertedrisccfg = LiveVariables.compute_cfg analysiscfg in *) + + (* Printf.printf "%s\n%a" (colorred "Converted RISC with no analysis CFG is") CfgRISC.RISCCfg.pp convertedrisccfg; *) + + + (* let convertedrisccfg = LiveVariables.compute_cfg (LiveVariables.optimize_cfg analysiscfg) in *) + + (* Printf.printf "%s\n%a" (colorred "Converted RISC after analysis CFG is") CfgRISC.RISCCfg.pp convertedrisccfg; *) + + let convertedrisccfg = ReduceRegisters.reduceregisters 4 convertedrisccfg in + + Printf.printf "%s\n%a" (colorred "Converted RISC after reducing registers CFG is") CfgRISC.RISCCfg.pp convertedrisccfg; - let convertedrisccfg = LiveVariables.optimize_cfg analysiscfg |> LiveVariables.compute_cfg in (* ---------------------------------- *) - let _risc = RISC.convert convertedrisccfg in + let risc = RISC.convert convertedrisccfg in - (* Printf.printf "%s\n%a" (colorred "RISC code is") RISC.RISCAssembly.pp risc; *) + Printf.printf "%s\n%a" (colorred "RISC code is") RISC.RISCAssembly.pp risc; - (* let computerisc = RISCSemantics.reduce risc in *) + let computerisc = RISCSemantics.reduce risc in - (* Printf.printf "%s\n%d\n" (colorred "Output of RISC code is") computerisc; *) + Printf.printf "%s\n%d\n" (colorred "Output of RISC code is") computerisc; () diff --git a/dune-project b/dune-project index a984e98..85457a3 100644 --- a/dune-project +++ b/dune-project @@ -12,7 +12,7 @@ (package (name analysis) - (depends ocaml dune)) + (depends ocaml dune utility)) (package (name miniImp) diff --git a/lib/analysis/Dataflow.ml b/lib/analysis/Dataflow.ml index ace70be..9120993 100644 --- a/lib/analysis/Dataflow.ml +++ b/lib/analysis/Dataflow.ml @@ -33,6 +33,16 @@ module Make (M: Cfg.PrintableType) (I: Cfg.PrintableType) = struct internalbetween: (internal list * internal list) list; } + let compareinternalnode (a:internalnode) (b:internalnode) : bool = + match Utility.equality a.internalin b.internalin, + Utility.equality a.internalout b.internalout, + (List.fold_left2 (fun acc (ain, aout) (bin, bout) + -> acc && (Utility.equality ain bin) && (Utility.equality aout bout) + ) true a.internalbetween b.internalbetween) + with + | true, true, true -> true + | _, _, _ -> false + type cfgt = elt Cfg.cfginternal type t = { @@ -40,49 +50,23 @@ module Make (M: Cfg.PrintableType) (I: Cfg.PrintableType) = struct internalvar: internalnode Cfg.NodeMap.t; } + let compareinternal (a: internalnode Cfg.NodeMap.t) (b: internalnode Cfg.NodeMap.t) = + Cfg.NodeMap.fold + (fun node bi acc -> + match Cfg.NodeMap.find_opt node a with + None -> false + | Some ai -> acc && compareinternalnode ai bi + ) b true + let from_cfg (cfg: cfgt) : t = {t = cfg; internalvar = Cfg.NodeMap.empty} let to_cfg ({t; _}: t) : cfgt = t - let fixed_point - ?(init : (elt list -> internalnode) = - (fun _ -> {internalin = []; internalout = []; internalbetween = []})) - ?(update : (t -> Cfg.Node.t -> internalnode) = - (fun t n -> Cfg.NodeMap.find n t.internalvar)) - (t: t) - : t = - (* init function is applied only once to each node content, - the update function takes the node and the whole structure and is - expected to return the updated structure for the appropriate node, - update function is applied to the resulting structure until no change is - observed - *) - let rec helper t = - let newt = - {t with - internalvar = Cfg.NodeMap.mapi (fun n _ -> update t n) t.internalvar} - in - if newt = t then newt else helper newt - in - let content = List.fold_left - (fun cfg node -> Cfg.NodeMap.add node {internalin = []; - internalout = []; - internalbetween = []} cfg) - Cfg.NodeMap.empty - (Cfg.NodeSet.to_list t.t.nodes) - in - let content = Cfg.NodeMap.union - (fun _ket _empty code -> Some code) - content - (Cfg.NodeMap.map init t.t.content) - in - helper { t with internalvar = content } - open Cfg - let pp (ppf: out_channel) (c: t) : unit = + let pp (ppf: out_channel) (c: t) : unit = ( Printf.fprintf ppf "Cfg:\n"; Printf.fprintf ppf "Nodes' ids: "; List.iter (fun (x : Node.t) -> Printf.fprintf ppf "%d " x.id) (NodeSet.to_list c.t.nodes); @@ -148,4 +132,54 @@ module Make (M: Cfg.PrintableType) (I: Cfg.PrintableType) = struct Printf.fprintf ppf "\n"; ) (NodeMap.to_list c.internalvar); Printf.fprintf ppf "\n"; + ) + + + let fixed_point + ?(init : (elt list -> internalnode) = + (fun _ -> {internalin = []; internalout = []; internalbetween = []})) + ?(update : (t -> Cfg.Node.t -> internalnode) = + (fun t n -> Cfg.NodeMap.find n t.internalvar)) + (t: t) + : t = + (* init function is applied only once to each node content, + the update function takes the node and the whole structure and is + expected to return the updated structure for the appropriate node, + update function is applied to the resulting structure until no change is + observed + *) + let rec helper t = + let newt = + {t with + internalvar = Cfg.NodeMap.mapi (fun n _ -> update t n) t.internalvar} + in + if compareinternal newt.internalvar t.internalvar + then newt + else helper newt + in + + let content = + List.fold_left + (fun cfg node -> Cfg.NodeMap.add node {internalin = []; + internalout = []; + internalbetween = []} cfg) + Cfg.NodeMap.empty + (Cfg.NodeSet.to_list t.t.nodes) + in + + let code = (* we add back in the nodes with no code (there is no binding + in the t.t.content map) *) + Cfg.NodeMap.union (fun _n c _empty -> Some c) + t.t.content + (Cfg.NodeMap.of_list + (Cfg.NodeSet.to_list t.t.nodes |> List.map (fun c -> (c, [])))) + in + + let content = Cfg.NodeMap.union + (fun _key _empty code -> Some code) + content + (Cfg.NodeMap.map init code) + in + helper { t with internalvar = content } + end diff --git a/lib/analysis/dune b/lib/analysis/dune index fe21d54..7dd99b4 100644 --- a/lib/analysis/dune +++ b/lib/analysis/dune @@ -1,6 +1,7 @@ (library (name analysis) (public_name analysis) - (modules Cfg Dataflow)) + (modules Cfg Dataflow) + (libraries utility)) (include_subdirs qualified) diff --git a/lib/miniImp/RISC.ml b/lib/miniImp/RISC.ml index 0480db6..93fed0a 100644 --- a/lib/miniImp/RISC.ml +++ b/lib/miniImp/RISC.ml @@ -59,7 +59,8 @@ module RISCAssembly = struct type t = { code : risci list; - inputval: int option + inputval: int option; + inputoutputreg: (register * register) option; } let pp_risci (ppf: out_channel) (v: risci) : unit = @@ -285,4 +286,9 @@ let rec helper let convert (prg: CfgRISC.RISCCfg.t) : RISCAssembly.t = {code = (helper prg (Option.get prg.initial) [] |> fst |> List.append ([Label "main"] : RISCAssembly.risci list)); - inputval = prg.inputVal} + inputval = prg.inputVal; + inputoutputreg = + match prg.inputOutputVar with + None -> None + | Some (i, o) -> Some ({index = i}, {index = o}) + } diff --git a/lib/miniImp/RISC.mli b/lib/miniImp/RISC.mli index 32edc36..fcf163f 100644 --- a/lib/miniImp/RISC.mli +++ b/lib/miniImp/RISC.mli @@ -50,7 +50,8 @@ module RISCAssembly : sig type t = { code : risci list; - inputval: int option + inputval: int option; + inputoutputreg: (register * register) option; } val pp_risci : out_channel -> risci -> unit diff --git a/lib/miniImp/RISCSemantics.ml b/lib/miniImp/RISCSemantics.ml index d371482..db35686 100644 --- a/lib/miniImp/RISCSemantics.ml +++ b/lib/miniImp/RISCSemantics.ml @@ -13,7 +13,8 @@ module RISCArchitecture = struct type t = { code: RISC.RISCAssembly.risci list CodeMap.t; registers: int RegisterMap.t; - memory: int MemoryMap.t + memory: int MemoryMap.t; + outputreg: Register.t; } end @@ -101,7 +102,8 @@ let reduce_instructions (prg: RISCArchitecture.t) (lo: string list) : int = else prg ) - | Nop :: tl -> helper prg tl current_label + | Nop :: tl -> + helper prg tl current_label | BRegOp (brop, r1, r2, r3) :: tl -> ( let n = (match_operator_r brop) (RegisterMap.find {index = r1.index} prg.registers) @@ -136,7 +138,8 @@ let reduce_instructions (prg: RISCArchitecture.t) (lo: string list) : int = ) ) | Load (r1, r3) :: tl -> ( - let n = MemoryMap.find + let n = + MemoryMap.find (RegisterMap.find {index = r1.index} prg.registers) prg.memory in @@ -164,14 +167,29 @@ let reduce_instructions (prg: RISCArchitecture.t) (lo: string list) : int = | Label _ :: tl -> helper prg tl current_label in RegisterMap.find - {index = "out"} + prg.outputreg (helper prg (CodeMap.find "main" prg.code) "main").registers let reduce (prg: RISC.RISCAssembly.t) : int = - reduce_instructions {code = convert prg; - registers = - RegisterMap.singleton - {index = "in"} - (Option.value prg.inputval ~default:0); - memory = MemoryMap.empty} (label_order prg) + reduce_instructions + {code = convert prg; + registers = ( + match prg.inputoutputreg with + | None -> + RegisterMap.singleton + {index = "in"} + (Option.value prg.inputval ~default:0) + | Some (i, _) -> + RegisterMap.singleton + {index = i.index} + (Option.value prg.inputval ~default:0) + ); + memory = MemoryMap.empty; + outputreg = ( + match prg.inputoutputreg with + | None -> {index = "out"} + | Some (_, o) -> {index = o.index} + ) + } + (label_order prg) diff --git a/lib/miniImp/definedVariables.ml b/lib/miniImp/definedVariables.ml index 1b39ac5..7694bb0 100644 --- a/lib/miniImp/definedVariables.ml +++ b/lib/miniImp/definedVariables.ml @@ -50,13 +50,13 @@ let variables_used (instr : DVCfg.elt) : DVCfg.internal list = | Nop | LoadI (_, _) -> acc + | Store (r1, r2) | BRegOp (_, r1, r2, _) -> DVCeltSet.add r1.index acc |> DVCeltSet.add r2.index | BImmOp (_, r1, _, _) | URegOp (_, r1, _) - | Load (r1, _) - | Store (r1, _) -> + | Load (r1, _) -> DVCeltSet.add r1.index acc in @@ -71,24 +71,18 @@ let variables_used_all (instructions : DVCfg.elt list) : DVCfg.internal list = let variables_defined (instructions : DVCfg.elt) : DVCfg.internal list = let helper (acc: DVCeltSet.t) (instr: DVCfg.elt) = match instr with - | Nop -> acc + | Nop + | Store (_, _) -> acc | BRegOp (_, _, _, r3) | BImmOp (_, _, _, r3) | URegOp (_, _, r3) | Load (_, r3) - | LoadI (_, r3) - | Store (_, r3) -> + | LoadI (_, r3) -> DVCeltSet.add r3.index acc in helper DVCeltSet.empty instructions |> DVCeltSet.to_list -let variables_defined_all (instructions : DVCfg.elt list) : DVCfg.internal list = - List.fold_left (fun (acc: DVCeltSet.t) (instr: DVCfg.elt) -> - DVCeltSet.union acc (variables_defined instr |> DVCeltSet.of_list) - ) DVCeltSet.empty instructions |> DVCeltSet.to_list - - (* init function, assign the bottom to everything *) let _init_bottom : (DVCfg.elt list -> DVCfg.internalnode) = @@ -104,91 +98,91 @@ let init_top (all_variables) : (DVCfg.elt list -> DVCfg.internalnode) = (fun _ -> (all_variables, all_variables)))}) -(* piece of code that computes vout for the whole block, not used, - use lub below *) -let _dumb_lub (t: DVCfg.t) (node: Cfg.Node.t) : DVCfg.internalnode = - let previnternalvar = Cfg.NodeMap.find node t.internalvar in - let code = Cfg.NodeMap.find node t.t.content in - { previnternalvar with - internalout = - Utility.unique_union (variables_defined_all code) (previnternalvar.internalin) - } - - -(* We consider only the propagation in the middle elements during the lub. - This incurs in a performance penality, but it is simpler to implement. - Each node is connected to one previus node. -*) let lub (t: DVCfg.t) (node: Cfg.Node.t) : DVCfg.internalnode = let previnternalvar = Cfg.NodeMap.find node t.internalvar in let code = match Cfg.NodeMap.find_opt node t.t.content with None -> [] | Some c -> c in + + let newinternalbetween = ( + List.map + (fun (code, (i, _o)) -> + (i, Utility.unique_union i (variables_defined code))) + (List.combine code previnternalvar.internalbetween) + ) in + + let newinternalout = + match newinternalbetween with + | [] -> previnternalvar.internalin + | _ -> (snd (Utility.last_list newinternalbetween)) + in + { previnternalvar with - internalbetween = - List.mapi (* we don't NEED the index but i = 0 is easier to write than - to check if vinout is None *) - (fun i (ithcode, vinout, ithcodeprev) -> - if i = 0 then - let dvin = previnternalvar.internalin in - (dvin, Utility.unique_union dvin (variables_defined ithcode)) - else ( - let ithcodeprev = match ithcodeprev with - None -> ([], []) - | Some x -> x - in - match vinout with - None -> - ([], variables_defined ithcode) - | Some prevdvbtw -> - (snd prevdvbtw, - Utility.unique_union - (variables_defined ithcode) - (ithcodeprev |> fst) - )) - ) - (* ugly code that zips the three lists that we need to compute each vin - and vout for the middle of the code *) - (Utility.combine_thrice - code - (Utility.pad_opt - (Utility.prev previnternalvar.internalbetween None) None (List.length code)) - (Utility.pad previnternalvar.internalbetween None (List.length code)) - ); - internalout = - match previnternalvar.internalbetween with - [] -> previnternalvar.internalin - | _ -> (snd (Utility.last_list previnternalvar.internalbetween)) - } + internalbetween = newinternalbetween; + internalout = newinternalout } let lucf (t: DVCfg.t) (node: Cfg.Node.t) : DVCfg.internalnode = let previnternalvar = Cfg.NodeMap.find node t.internalvar in + if Option.equal (=) (Some node) t.t.initial then + (* if L is initial set dvin to the "in" register *) + let newinternalin = ( + match t.t.inputOutputVar with + Some (i, _) -> [i] + | None -> [] + ) in + + let newinternalbetween = ( (* set the dvin of each to the previous dvout *) + match previnternalvar.internalbetween with + [] -> [] + | [(_i, o)] -> [(newinternalin, o)] + | (_i, o) :: btwrest -> + (newinternalin, o) :: ( + List.map (fun ((_i, o), (_previ, prevo)) -> (prevo, o)) + (Utility.combine_twice btwrest previnternalvar.internalbetween) + ) + ) in { previnternalvar with - internalin = - match t.t.inputOutputVar with - Some (i, _) -> [i] - | None -> [] - } + internalin = newinternalin; + internalbetween = newinternalbetween } else + (* if L is not initial set dvin to the intersection of the previous node's + dvouts *) let prevnodes = Cfg.NodeMap.find node t.t.reverseEdges in + let newinternalin = ( + match prevnodes with + | [] -> + [] + | [prevnode] -> + (Cfg.NodeMap.find prevnode t.internalvar).internalout + | [prevnode1; prevnode2] -> + Utility.unique_intersection + (Cfg.NodeMap.find prevnode1 t.internalvar).internalout + (Cfg.NodeMap.find prevnode2 t.internalvar).internalout + | prevnode :: restnodes -> + List.fold_left (* intersection of all previous nodes' dvout *) + (fun acc prevnode -> + Utility.unique_intersection + acc + (Cfg.NodeMap.find prevnode t.internalvar).internalout) + (Cfg.NodeMap.find prevnode t.internalvar).internalout + restnodes + ) in + + let newinternalbetween = + match previnternalvar.internalbetween with + [] -> [] + | [(_i, o)] -> [(newinternalin, o)] + | (_i, o) :: btwrest -> + (newinternalin, o) :: ( + List.map (fun ((_i, o), (_previ, prevo)) -> (prevo, o)) + (Utility.combine_twice btwrest previnternalvar.internalbetween) + ) + in { previnternalvar with - internalin = - match prevnodes with - [] -> [] - | [prevnode] -> (Cfg.NodeMap.find prevnode t.internalvar).internalout - | [prevnode1; prevnode2] -> - Utility.unique_intersection - (Cfg.NodeMap.find prevnode1 t.internalvar).internalout - (Cfg.NodeMap.find prevnode2 t.internalvar).internalout - | _ -> - List.fold_left (* intersection of all previous nodes' dvout *) - (fun acc prevnode -> - Utility.unique_intersection acc (Cfg.NodeMap.find prevnode t.internalvar).internalout) - [] - prevnodes - } + internalin = newinternalin; + internalbetween = newinternalbetween } let update (t: DVCfg.t) (node: Cfg.Node.t) : DVCfg.internalnode = @@ -198,10 +192,16 @@ let update (t: DVCfg.t) (node: Cfg.Node.t) : DVCfg.internalnode = let compute_defined_variables (cfg: RISCCfg.t) : DVCfg.t = let all_variables = List.fold_left - (fun acc (_, code) -> Utility.unique_union acc (variables_all code)) + (fun acc (_, code) -> + Utility.unique_union acc (variables_all code)) [] (Cfg.NodeMap.to_list cfg.content) in + let all_variables = + match cfg.inputOutputVar with + | None -> all_variables + | Some (i, o) -> Utility.unique_union all_variables [i;o] + in DVCfg.from_cfg cfg |> DVCfg.fixed_point ~init:(init_top all_variables) ~update:update diff --git a/lib/miniImp/definedVariables.mli b/lib/miniImp/definedVariables.mli index cc8e4ea..be2577d 100644 --- a/lib/miniImp/definedVariables.mli +++ b/lib/miniImp/definedVariables.mli @@ -3,6 +3,7 @@ open Analysis module Variable : sig type t val pp : out_channel -> t -> unit + val pplist : out_channel -> t list -> unit end module RISCCfg = CfgRISC.RISCCfg diff --git a/lib/miniImp/dune b/lib/miniImp/dune index b3f830a..f3e43a6 100644 --- a/lib/miniImp/dune +++ b/lib/miniImp/dune @@ -13,6 +13,7 @@ (modules Lexer Parser Types Semantics CfgImp ReplacePowerMod CfgRISC DefinedVariables LiveVariables + ReduceRegisters RISC RISCSemantics) (libraries analysis utility menhirLib)) diff --git a/lib/miniImp/liveVariables.ml b/lib/miniImp/liveVariables.ml index cc2b385..a721d85 100644 --- a/lib/miniImp/liveVariables.ml +++ b/lib/miniImp/liveVariables.ml @@ -24,45 +24,33 @@ let variables_used (instr : DVCfg.elt) : DVCfg.internal list = | Nop | LoadI (_, _) -> acc - | BRegOp (_, r1, r2, _) -> + | BRegOp (_, r1, r2, _) + | Store (r1, r2) -> DVCeltSet.add r1.index acc |> DVCeltSet.add r2.index | BImmOp (_, r1, _, _) | URegOp (_, r1, _) - | Load (r1, _) - | Store (r1, _) -> + | Load (r1, _) -> DVCeltSet.add r1.index acc in helper DVCeltSet.empty instr |> DVCeltSet.to_list -let _variables_used_all (instructions : DVCfg.elt list) : DVCfg.internal list = - List.fold_left (fun (acc: DVCeltSet.t) (instr: DVCfg.elt) -> - DVCeltSet.union acc (variables_used instr |> DVCeltSet.of_list) - ) DVCeltSet.empty instructions |> DVCeltSet.to_list - - let variables_defined (instructions : DVCfg.elt) : DVCfg.internal list = let helper (acc: DVCeltSet.t) (instr: DVCfg.elt) = match instr with - | Nop -> acc + | Nop + | Store (_, _) -> acc | BRegOp (_, _, _, r3) | BImmOp (_, _, _, r3) | URegOp (_, _, r3) | Load (_, r3) - | LoadI (_, r3) - | Store (_, r3) -> + | LoadI (_, r3) -> DVCeltSet.add r3.index acc in helper DVCeltSet.empty instructions |> DVCeltSet.to_list -let _variables_defined_all (instructions : DVCfg.elt list) : DVCfg.internal list = - List.fold_left (fun (acc: DVCeltSet.t) (instr: DVCfg.elt) -> - DVCeltSet.union acc (variables_defined instr |> DVCeltSet.of_list) - ) DVCeltSet.empty instructions |> DVCeltSet.to_list - - (* init function, assign the bottom to everything *) let init : (DVCfg.elt list -> DVCfg.internalnode) = (fun l -> {internalin = []; @@ -75,58 +63,68 @@ let lub (t: DVCfg.t) (node: Cfg.Node.t) : DVCfg.internalnode = None -> [] | Some c -> c in + + let newinternalbetween = ( + List.map + (fun (code, (_i, o)) -> + (Utility.unique_union + (variables_used code) + (Utility.subtraction o (variables_defined code)), o)) + (Utility.combine_twice code previnternalvar.internalbetween) + ) in + + let newinternalin = + match newinternalbetween with + | [] -> previnternalvar.internalout + | (i, _)::_ -> i + in + { previnternalvar with - internalbetween = - List.map (fun (prevbtw, code, nextprevbtw) -> - let newin = Utility.unique_union (variables_used code) - (Utility.subtraction (snd prevbtw) (variables_defined code)) - in - match nextprevbtw with - None -> (newin, snd prevbtw) - | Some (newout, _) -> (newin, newout) - ) - (Utility.combine_thrice previnternalvar.internalbetween code - (Utility.pad (List.tl previnternalvar.internalbetween) None (List.length previnternalvar.internalbetween))) - ; - internalin = fst (List.hd previnternalvar.internalbetween); - } + internalbetween = newinternalbetween; + internalin = newinternalin; } let lucf (t: DVCfg.t) (node: Cfg.Node.t) : DVCfg.internalnode = let previnternalvar = Cfg.NodeMap.find node t.internalvar in - if Option.equal (=) (Some node) t.t.terminal then - let outputvarlist = match t.t.inputOutputVar with + + let newinternalout = ( + if Option.equal (=) (Some node) t.t.terminal then ( + match t.t.inputOutputVar with Some (_, o) -> [o] | None -> [] - in - { previnternalvar with - internalout = outputvarlist; - internalbetween = ( - let last_elem = Utility.last_list previnternalvar.internalbetween in - (Utility.drop_last_element_list previnternalvar.internalbetween) @ - [(fst last_elem, outputvarlist)] - ) - } - else - let nextnodes = Cfg.NodeMap.find_opt node t.t.edges in - let newinternalout = match nextnodes with - None -> [] - | Some (node, None) -> (Cfg.NodeMap.find node t.internalvar).internalin + ) else ( + let nextnodes = Cfg.NodeMap.find_opt node t.t.edges in + match nextnodes with + | None -> [] + | Some (node, None) -> + (Cfg.NodeMap.find node t.internalvar).internalin | Some (node1, Some node2) -> Utility.unique_union (Cfg.NodeMap.find node1 t.internalvar).internalin (Cfg.NodeMap.find node2 t.internalvar).internalin - in - { previnternalvar with - internalout = newinternalout; - internalbetween = ( - let last_elem = Utility.last_list previnternalvar.internalbetween in - (Utility.drop_last_element_list previnternalvar.internalbetween) @ - [(fst last_elem, newinternalout)] - ) - } + ) + ) in + + let newinternalbetween = ( + match List.rev previnternalvar.internalbetween with + | [] -> [] + | (i, _o) :: btwrest -> + let btwrest = List.rev btwrest in + let newbtwrest = List.map2 + (fun (i, _o) (nexti, _nexto) -> (i, nexti)) + btwrest + (Utility.drop_first_element_list previnternalvar.internalbetween) + in + newbtwrest @ [(i, newinternalout)] + ) in + + { previnternalvar with + internalout = newinternalout; + internalbetween = newinternalbetween; } let update (t: DVCfg.t) (node: Cfg.Node.t) : DVCfg.internalnode = - let newt = {t with internalvar = (Cfg.NodeMap.add node (lucf t node) t.internalvar)} in + let newt = {t with internalvar = (Cfg.NodeMap.add node + (lucf t node) + t.internalvar)} in lub newt node @@ -135,10 +133,136 @@ let compute_live_variables (cfg: RISCCfg.t) : DVCfg.t = |> DVCfg.fixed_point ~init:init ~update:update + +module VariableMap = struct + include Map.Make(Variable) + + let first_empty next start m l = + let bindings = + List.fold_left ( + fun acc x -> + match find_opt x m with + | None -> acc + | Some x -> x :: acc) [] l |> List.sort Variable.compare in + + let rec aux x = + if List.mem x bindings + then aux (next x) + else x + in + aux start + + let first_empty_Variable m l = + let next = fun x -> x |> int_of_string |> (+) 1 |> string_of_int in + let start = "1" in + first_empty next start m l + + let get_mapping m l r = + match find_opt r m with + | None -> ( + let newr = first_empty_Variable m l in + let newm = add r newr m in + (newm, newr) + ) + | Some r -> (m, r) +end + + (* just rename the registers that dont share live status *) let optimize_cfg (t: DVCfg.t) : DVCfg.t = - t + let replace_code ((vin, vout): Variable.t list * Variable.t list) + (a: Variable.t VariableMap.t) + (code: DVCfg.elt) + : (Variable.t VariableMap.t * DVCfg.elt) = + match code with + | Nop -> ( + (a, Nop) + ) + | BRegOp (brop, r1, r2, r3) -> ( + let (newa, newr1) = VariableMap.get_mapping a vin r1.index in + let (newa, newr2) = VariableMap.get_mapping newa vin r2.index in + let (newa, newr3) = VariableMap.get_mapping newa vout r3.index in + (newa, BRegOp (brop, {index = newr1}, {index = newr2}, {index = newr3})) + ) + | BImmOp (biop, r1, i, r3) -> ( + let (newa, newr1) = VariableMap.get_mapping a vin r1.index in + let (newa, newr3) = VariableMap.get_mapping newa vout r3.index in + (newa, BImmOp (biop, {index = newr1}, i, {index = newr3})) + ) + | URegOp (urop, r1, r3) -> ( + let (newa, newr1) = VariableMap.get_mapping a vin r1.index in + let (newa, newr3) = VariableMap.get_mapping newa vout r3.index in + (newa, URegOp (urop, {index = newr1}, {index = newr3})) + ) + | Load (r1, r3) -> ( + let (newa, newr1) = VariableMap.get_mapping a vin r1.index in + let (newa, newr3) = VariableMap.get_mapping newa vout r3.index in + (newa, Load ({index = newr1}, {index = newr3})) + ) + | LoadI (i, r3) -> ( + let (newa, newr3) = VariableMap.get_mapping a vout r3.index in + (newa, LoadI (i, {index = newr3})) + ) + | Store (r1, r3) -> ( + let (newa, newr1) = VariableMap.get_mapping a vin r1.index in + let (newa, newr3) = VariableMap.get_mapping newa vout r3.index in + (newa, Store ({index = newr1}, {index = newr3})) + ) + in + let aux (assignments: Variable.t VariableMap.t) (t: DVCfg.t) (node: Cfg.Node.t) + : (Variable.t VariableMap.t * DVCfg.t) = + let livevars = Cfg.NodeMap.find node t.internalvar in + let code = + match Cfg.NodeMap.find_opt node t.t.content with + | None -> [] + | Some x -> x + in + let newcode, newassignments = + (List.fold_left2 + (fun (acc, assignments) btw code -> + let na, nc = replace_code btw assignments code in + (acc @ [nc], na) + ) + ([], assignments) + livevars.internalbetween + code) + in + let newcontent = Cfg.NodeMap.add + node + newcode + t.t.content + in + + let newt = { t with t = { t.t with content = newcontent } } in + (newassignments, newt) + in + + (* --------- *) + + let assignments = VariableMap.empty in + + let a, newt = + Cfg.NodeSet.fold (* for each node we replace all the variables with the + optimized ones *) + (fun node (ass, t) -> aux ass t node) + t.t.nodes + (assignments, t) + in + + { newt with + t = { newt.t with + inputOutputVar = + match newt.t.inputOutputVar with + None -> None + | Some (i, o) -> ( + match VariableMap.find_opt i a, VariableMap.find_opt o a with + | None, None -> Some (i, o) + | Some i, None -> Some (i, o) + | None, Some o -> Some (i, o) + | Some i, Some o -> Some (i, o) + ) + }} let compute_cfg (dvcfg: DVCfg.t) : RISCCfg.t = diff --git a/lib/miniImp/reduceRegisters.ml b/lib/miniImp/reduceRegisters.ml new file mode 100644 index 0000000..ef9cf4b --- /dev/null +++ b/lib/miniImp/reduceRegisters.ml @@ -0,0 +1,416 @@ +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 + + let helper (acc: int VariableMap.t) (instr: RISCCfg.elt) = + 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 = + 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) -> + Utility.unique_union acc (variables_all_frequency code)) + [] + (Cfg.NodeMap.to_list cfg.content) + in + 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 + 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, + VariableMap.find_opt r1.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, Some r2, None, _, _, Some m3 -> + [BRegOp (brop, {index = r1}, {index = r2}, tmpreg2); + LoadI (m3, tmpreg1); + Store (tmpreg2, tmpreg1)] + | 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})] + | 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 + + List.map (fun x -> + Printf.printf "Converting: %a\n" CfgRISC.RISCSimpleStatements.pp x; + let tmp = aux x in + Printf.printf "Into: %a\n\n" CfgRISC.RISCSimpleStatements.pplist tmp; + tmp + ) code |> List.concat + in + + + let aux (cfg: RISCCfg.t) all_variables = + (* 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 + + Printf.printf "Most freq mapping:\n"; + List.iter (fun (a, b) -> Printf.printf "%s -> %s\n" a b) (VariableMap.to_list most_frequent_mapping); + Printf.printf "Least freq mapping:\n"; + List.iter (fun (a, b) -> Printf.printf "%s -> mem %d\n" a b) (VariableMap.to_list least_frequent_mapping); + + (* 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 + + + ( if List.length all_variables < n + then cfg + else aux cfg all_variables ) diff --git a/lib/miniImp/reduceRegisters.mli b/lib/miniImp/reduceRegisters.mli new file mode 100644 index 0000000..5e04d8d --- /dev/null +++ b/lib/miniImp/reduceRegisters.mli @@ -0,0 +1,3 @@ +module RISCCfg = CfgRISC.RISCCfg + +val reduceregisters : int -> RISCCfg.t -> RISCCfg.t diff --git a/lib/utility/utility.ml b/lib/utility/utility.ml index 52cad39..f4d3489 100644 --- a/lib/utility/utility.ml +++ b/lib/utility/utility.ml @@ -52,6 +52,7 @@ let rec fromIntToString (alphabet: string) (x: int) : string = |> String.make 1) +(* true if every element of la is in lb *) let inclusion la lb = let rec aux la = function @@ -63,6 +64,11 @@ let inclusion la lb = in aux lb la +(* true if lb includes la and la includes lb *) +let equality la lb = + inclusion la lb && inclusion lb la + +(* computes the result of la \setminus lb *) let subtraction la lb = let rec aux la = function @@ -89,23 +95,40 @@ let unique l = let unique_union la lb = unique (la @ lb) +(* returns all elements both in la and in lb *) let unique_intersection la lb = - let rec aux la lb acc = + let rec aux la acc = match la with [] -> acc | a::la -> if List.mem a lb - then aux la lb (a::acc) - else aux la lb acc + then aux la (a::acc) + else aux la acc in - aux la lb [] |> unique + aux la [] |> unique +(* returns a list with at most n items and the rest in the second *) +let rec take (n: int) (l: 'a list) : ('a list * 'a list) = + match n with + | 0 -> ([], l) + | n -> + match l with + | [] -> ([], []) + | i::ls -> + let (t1, t2) = (take (n - 1) ls) in + ((i :: t1), (t2)) +(* returns the list without the last element *) let drop_last_element_list = function | [] -> [] | l -> l |> List.rev |> List.tl |> List.rev +let drop_first_element_list = + function + | [] -> [] + | _::l -> l + (* Complicated way to drop the last element and add a new option element to the beginning *) let prev l a = @@ -152,6 +175,13 @@ let add_to_last_list (la: 'a list list) (a: 'a) : 'a list list = in aux la a +let rec combine_twice la lb = + match (la, lb) with + | [], [] -> [] + | [a], [b] -> [a, b] + | a::la, b::lb -> (a, b) :: (combine_twice la lb) + | _ -> [] + let rec combine_thrice la lb lc = match (la, lb, lc) with | [], [], [] -> [] diff --git a/lib/utility/utility.mli b/lib/utility/utility.mli index 9d77eee..76e7593 100644 --- a/lib/utility/utility.mli +++ b/lib/utility/utility.mli @@ -13,13 +13,17 @@ val int_not : int -> int val fromIntToString : string -> int -> string val inclusion : 'a list -> 'a list -> bool +val equality : 'a list -> 'a list -> bool val subtraction : 'a list -> 'a list -> 'a list val unique : 'a list -> 'a list val unique_union : 'a list -> 'a list -> 'a list val unique_intersection : 'a list -> 'a list -> 'a list -val drop_last_element_list : 'a list -> 'a list +val take : int -> 'a list -> ('a list * 'a list) + +val drop_last_element_list : 'a list -> 'a list +val drop_first_element_list : 'a list -> 'a list val prev : 'a list -> 'a option -> 'a option list val pad : 'a list -> 'a option -> int -> 'a option list @@ -29,4 +33,5 @@ val combine : 'a list -> 'b option list -> ('a * 'b) option list val last_list : 'a list -> 'a val add_to_last_list : 'a list list -> 'a -> 'a list list +val combine_twice : 'a list -> 'b list -> ('a * 'b) list val combine_thrice : 'a list -> 'b list -> 'c list -> ('a * 'b * 'c) list From 9bcc88e01663de47698f1e816bca0e46844ce95f Mon Sep 17 00:00:00 2001 From: elvis Date: Sat, 11 Jan 2025 20:32:11 +0100 Subject: [PATCH 14/29] Fixes for RISC evaluation --- bin/dune | 9 +++ bin/miller-rabin.miniimp | 2 +- bin/miniImpInterpreter.ml | 3 +- bin/miniImpInterpreterReg.ml | 119 +++++++++++++++++++++++++++++++++ bin/test.miniimp | 25 +++++++ lib/analysis/Cfg.ml | 35 ++++++---- lib/analysis/Dataflow.ml | 6 +- lib/miniImp/RISC.ml | 14 ++-- lib/miniImp/reduceRegisters.ml | 53 ++++++++------- lib/utility/utility.ml | 98 ++++++++++----------------- lib/utility/utility.mli | 12 +--- 11 files changed, 261 insertions(+), 115 deletions(-) create mode 100644 bin/miniImpInterpreterReg.ml create mode 100644 bin/test.miniimp diff --git a/bin/dune b/bin/dune index 9f2f5b0..4f912b5 100644 --- a/bin/dune +++ b/bin/dune @@ -27,3 +27,12 @@ (package miniImp) (modes byte exe) ) + +(executable + (name miniImpInterpreterReg) + (public_name miniImpInterpreterReg) + (libraries miniImp + clap) + (package miniImp) + (modes byte exe) + ) diff --git a/bin/miller-rabin.miniimp b/bin/miller-rabin.miniimp index 5727cb6..355d16b 100644 --- a/bin/miller-rabin.miniimp +++ b/bin/miller-rabin.miniimp @@ -1,7 +1,6 @@ def main with input n output result as if (n % 2) == 0 then result := 1 else ( - result := 0; s := 0; while (0 == ((n - 1) / (2 ^ s)) % 2) do ( @@ -11,6 +10,7 @@ def main with input n output result as for (i := 20, i > 0, i := i - 1) do ( a := rand(n - 4) + 2; x := powmod(a, d, n); + y := 0; for (j := 0, j < s, j := j+1) do ( y := powmod(x, 2, n); if (y == 1 && (not x == 1) && (not x == n - 1)) then diff --git a/bin/miniImpInterpreter.ml b/bin/miniImpInterpreter.ml index 999c858..0f938b7 100644 --- a/bin/miniImpInterpreter.ml +++ b/bin/miniImpInterpreter.ml @@ -58,7 +58,8 @@ let () = | Lexer.LexingError msg -> Printf.fprintf stderr "%a: %s\n" print_position lexbuf msg; exit (-1) - | Parser.Error -> Printf.fprintf stderr "%a: syntax error\n" print_position lexbuf; + | Parser.Error -> + Printf.fprintf stderr "%a: syntax error\n" print_position lexbuf; exit (-1) in let return_value = diff --git a/bin/miniImpInterpreterReg.ml b/bin/miniImpInterpreterReg.ml new file mode 100644 index 0000000..7de8e42 --- /dev/null +++ b/bin/miniImpInterpreterReg.ml @@ -0,0 +1,119 @@ +open MiniImp +open Lexing + +(* -------------------------------------------------------------------------- *) +(* Command Arguments *) + +let () = + Clap.description "Interpreter for MiniImp language."; + + let files = Clap.section ~description: "Files to consider." "FILES" in + let values = Clap.section ~description: "Input values." "VALUES" in + + let input = Clap.mandatory_string + ~description: "Input file." + ~placeholder: "FILENAME" + ~section: files + ~long: "input" + ~short: 'i' + () + in + + let registers = Clap.default_int + ~description: "Optional number of registers available." + ~placeholder: "INT" + ~section: values + ~long: "registers" + ~short: 'r' + 4 + in + + let evalb = Clap.flag + ~description: "Optional flag for evaluating the generated risc code." + ~section: values + ~set_long: "eval" + ~set_short: 'e' + false + in + + let inputval = Clap.default_int + ~description: "Optional input value to feed to the program. \ + If not specified it is read from stdin." + ~placeholder: "INT" + ~section: values + ~long: "value" + ~short: 'v' + 0 + in + + let output = Clap.optional_string + ~description: "Output file. If not specified output is printed on stdout." + ~placeholder: "FILENAME" + ~section: files + ~long: "output" + ~long_synonyms: ["out"; "result"] + ~short: 'o' + () + in + + Clap.close (); + +(* -------------------------------------------------------------------------- *) +(* Interpreter *) + + let print_position outx lexbuf = + let pos = lexbuf.lex_curr_p in + Printf.fprintf outx "Encountered \"%s\" at %s:%d:%d" + (Lexing.lexeme lexbuf) pos.pos_fname + pos.pos_lnum (pos.pos_cnum - pos.pos_bol + 1) + in + + let interpret_file inch (registers: int) outch = + let lexbuf = Lexing.from_channel inch in + let program = + try Parser.prg Lexer.read lexbuf with + | Lexer.LexingError msg -> + Printf.fprintf stderr "%a: %s\n" print_position lexbuf msg; + exit (-1) + | Parser.Error -> + Printf.fprintf stderr "%a: syntax error\n" print_position lexbuf; + exit (-1) + in + let return_value = + program |> + CfgImp.convert_io inputval |> + CfgRISC.convert + in + + let () = ( + match DefinedVariables.compute_defined_variables return_value |> + DefinedVariables.check_undefined_variables + with + | None -> () + | Some l -> + Printf.printf "Error: undefined variables: %a\n" + DefinedVariables.Variable.pplist l; + exit (-1) + ) in + + let return_value = + return_value |> + LiveVariables.compute_live_variables |> + LiveVariables.optimize_cfg |> + LiveVariables.compute_cfg |> + ReduceRegisters.reduceregisters registers |> + RISC.convert + in + + if not evalb + then Printf.fprintf outch "%a\n" RISC.RISCAssembly.pp return_value + else Printf.fprintf outch "%d\n" (RISCSemantics.reduce return_value) + in + + let inx = In_channel.open_text input in + let outx = match output with + None -> stdout + | Some f -> Out_channel.open_text f + in + + interpret_file inx registers outx; diff --git a/bin/test.miniimp b/bin/test.miniimp new file mode 100644 index 0000000..355d16b --- /dev/null +++ b/bin/test.miniimp @@ -0,0 +1,25 @@ +def main with input n output result as + if (n % 2) == 0 then result := 1 + else ( + result := 0; + s := 0; + while (0 == ((n - 1) / (2 ^ s)) % 2) do ( + s := s + 1 + ); + d := ((n - 1) / 2 ^ s); + for (i := 20, i > 0, i := i - 1) do ( + a := rand(n - 4) + 2; + x := powmod(a, d, n); + y := 0; + for (j := 0, j < s, j := j+1) do ( + y := powmod(x, 2, n); + if (y == 1 && (not x == 1) && (not x == n - 1)) then + result := 1; + else + skip; + x := y; + ); + if not y == 1 then result := 1; + else skip; + ) + ) diff --git a/lib/analysis/Cfg.ml b/lib/analysis/Cfg.ml index 55b2a5f..c79e20a 100644 --- a/lib/analysis/Cfg.ml +++ b/lib/analysis/Cfg.ml @@ -20,6 +20,7 @@ end module NodeMap = struct include Map.Make(Node) + (* adds the input to the tail of the list for the associated node *) let add_to_list_last x data m = let add = function None -> Some [data] | Some l -> Some (l @ [data]) in @@ -80,12 +81,14 @@ module Make (M: PrintableType) = struct nodes = NodeSet.union cfg1.nodes cfg2.nodes |> NodeSet.add entryNode |> NodeSet.add exitNode; - edges = NodeMap.union (fun _ -> failwith "Failed merging edges of cfg.") + edges = NodeMap.union + (fun _ -> failwith "Failed merging edges of cfg.") cfg1.edges cfg2.edges |> NodeMap.add entryNode (cfg1initial, Some cfg2initial) |> NodeMap.add cfg1terminal (exitNode, None) |> NodeMap.add cfg2terminal (exitNode, None); - reverseEdges = NodeMap.union (fun _ -> failwith "Failed merging edges of cfg.") + reverseEdges = NodeMap.union + (fun _ -> failwith "Failed merging edges of cfg.") cfg1.reverseEdges cfg2.reverseEdges |> NodeMap.add_to_list cfg1initial entryNode |> NodeMap.add_to_list cfg2initial entryNode |> @@ -95,7 +98,8 @@ module Make (M: PrintableType) = struct inputOutputVar = cfg1.inputOutputVar; initial = Some entryNode; terminal = Some exitNode; - content = NodeMap.union (fun _ -> failwith "Failed merging code of cfg.") + content = NodeMap.union + (fun _ -> failwith "Failed merging code of cfg.") cfg1.content cfg2.content } @@ -110,17 +114,20 @@ module Make (M: PrintableType) = struct let cfg2terminal = Option.get cfg2.terminal in { empty = false; nodes = NodeSet.union cfg1.nodes cfg2.nodes; - edges = NodeMap.union (fun _ -> failwith "Failed merging edges of cfg.") - cfg1.edges cfg2.edges |> + edges = NodeMap.union + (fun _ -> failwith "Failed merging edges of cfg.") + cfg1.edges cfg2.edges |> NodeMap.add cfg1terminal (cfg2initial, None); - reverseEdges = NodeMap.union (fun _ -> failwith "Failed merging edges of cfg.") + reverseEdges = NodeMap.union + (fun _ -> failwith "Failed merging edges of cfg.") cfg1.reverseEdges cfg2.reverseEdges |> NodeMap.add_to_list cfg2initial cfg1terminal; inputVal = cfg1.inputVal; inputOutputVar = cfg1.inputOutputVar; initial = Some cfg1initial; terminal = Some cfg2terminal; - content = NodeMap.union (fun _ -> failwith "Failed merging code of cfg.") + content = NodeMap.union + (fun _ -> failwith "Failed merging code of cfg.") cfg1.content cfg2.content } @@ -147,14 +154,18 @@ module Make (M: PrintableType) = struct let pp (ppf) (c: t) : unit = Printf.fprintf ppf "Nodes' ids: "; - List.iter (fun (x : Node.t) -> Printf.fprintf ppf "%d " x.id) (NodeSet.to_list c.nodes); + List.iter + (fun (x : Node.t) -> Printf.fprintf ppf "%d " x.id) + (NodeSet.to_list c.nodes); Printf.fprintf ppf "\n"; Printf.fprintf ppf "Nodes' edges:\n"; - List.iter (fun ((n, (a, b)) : (Node.t * (Node.t * Node.t option))) : unit -> - match b with None -> Printf.fprintf ppf "\t%d -> %d\n" n.id a.id - | Some b -> Printf.fprintf ppf "\t%d -> %d, %d\n" n.id a.id b.id - ) (NodeMap.to_list c.edges); + List.iter + (fun ((n, (a, b)) : (Node.t * (Node.t * Node.t option))) : unit -> + match b with + None -> Printf.fprintf ppf "\t%d -> %d\n" n.id a.id + | Some b -> Printf.fprintf ppf "\t%d -> %d, %d\n" n.id a.id b.id ) + (NodeMap.to_list c.edges); Printf.fprintf ppf "\n"; Printf.fprintf ppf "Nodes' back edges:\n"; diff --git a/lib/analysis/Dataflow.ml b/lib/analysis/Dataflow.ml index 9120993..ef14743 100644 --- a/lib/analysis/Dataflow.ml +++ b/lib/analysis/Dataflow.ml @@ -18,7 +18,11 @@ module type C = sig val from_cfg : cfgt -> t val to_cfg : t -> cfgt - val fixed_point : ?init:(elt list -> internalnode) -> ?update:(t -> Cfg.Node.t -> internalnode) -> t -> t + val fixed_point : + ?init:(elt list -> internalnode) -> + ?update:(t -> Cfg.Node.t -> internalnode) -> + t -> + t val pp : out_channel -> t -> unit end diff --git a/lib/miniImp/RISC.ml b/lib/miniImp/RISC.ml index 93fed0a..2858078 100644 --- a/lib/miniImp/RISC.ml +++ b/lib/miniImp/RISC.ml @@ -116,9 +116,13 @@ module RISCAssembly = struct let pp (ppf: out_channel) (t: t) : unit = Printf.fprintf ppf "Input Val: "; - match t.inputval with - None -> Printf.fprintf ppf "None\n" - | Some i -> Printf.fprintf ppf "Some %d\n" i; + ( match t.inputval with + None -> Printf.fprintf ppf "None\n" + | Some i -> Printf.fprintf ppf "Some %d\n" i ); + Printf.fprintf ppf "Input/Output Registers: "; + ( match t.inputoutputreg with + None -> Printf.fprintf ppf "None\n" + | Some (i, o) -> Printf.fprintf ppf "[i: Some r%s, o: Some r%s]\n" i.index o.index); Printf.fprintf ppf "Code:\n"; List.iter (pp_risci ppf) t.code end @@ -225,7 +229,7 @@ let rec helper in match nextnodes with | Some (nextnode1, None) -> - let res, vis = (helper prg nextnode1) (currentnode :: alreadyVisited) in + let res, vis = (helper prg nextnode1 (currentnode :: alreadyVisited)) in (currentcode @ res, vis) | Some (nextnode1, Some nextnode2) -> ( let ncs = nextCommonSuccessor prg nextnode1 nextnode2 in @@ -248,6 +252,7 @@ let rec helper | BImmOp (_, _, _, r) | URegOp (_, _, r) | Load (_, r) + | Store (r, _) | LoadI (_, r) -> (([Label label1] : RISCAssembly.risci list) @ currentcode @ ([CJump (r, label2, label3); Label label2] : RISCAssembly.risci list) @ @@ -269,6 +274,7 @@ let rec helper | BImmOp (_, _, _, r) | URegOp (_, _, r) | Load (_, r) + | Store (r, _) | LoadI (_, r) -> (currentcode @ ([CJump (r, label1, label2); Label label1] : RISCAssembly.risci list) @ res1 @ diff --git a/lib/miniImp/reduceRegisters.ml b/lib/miniImp/reduceRegisters.ml index ef9cf4b..81d0edc 100644 --- a/lib/miniImp/reduceRegisters.ml +++ b/lib/miniImp/reduceRegisters.ml @@ -20,7 +20,7 @@ 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 - let helper (acc: int VariableMap.t) (instr: RISCCfg.elt) = + let helper (acc: int VariableMap.t) (instr: RISCCfg.elt) : int VariableMap.t = match instr with | Nop -> acc @@ -41,9 +41,12 @@ let variables_frequency (instr : RISCCfg.elt) : (Variable.t * int) list = helper VariableMap.empty instr |> VariableMap.to_list let variables_all_frequency (instructions : RISCCfg.elt list) : (Variable.t * int) list = - 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 + 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 = @@ -52,10 +55,11 @@ let reduceregisters (n: int) (cfg: RISCCfg.t) : RISCCfg.t = (* we get all the variables with associated frequency (only syntactic use) *) let all_variables = List.fold_left (fun acc (_, code) -> - Utility.unique_union acc (variables_all_frequency code)) + Utility.unique_union_assoc (fun _n x y -> x + y) acc (variables_all_frequency code)) [] (Cfg.NodeMap.to_list cfg.content) in + let all_variables = match cfg.inputOutputVar with | None -> all_variables @@ -65,6 +69,7 @@ let reduceregisters (n: int) (cfg: RISCCfg.t) : RISCCfg.t = | Some f -> (i, f+1) :: (List.remove_assoc i all_variables) ) in + let all_variables = match cfg.inputOutputVar with | None -> all_variables @@ -95,15 +100,11 @@ let reduceregisters (n: int) (cfg: RISCCfg.t) : RISCCfg.t = VariableMap.find_opt r2.index remappedregisters, VariableMap.find_opt r3.index remappedregisters, VariableMap.find_opt r1.index memorymap, - VariableMap.find_opt r1.index memorymap, + 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, Some r2, None, _, _, Some m3 -> - [BRegOp (brop, {index = r1}, {index = r2}, tmpreg2); - LoadI (m3, tmpreg1); - Store (tmpreg2, tmpreg1)] | Some r1, None, Some r3, _, Some m2, _ -> [LoadI (m2, tmpreg2); Load (tmpreg2, tmpreg2); @@ -118,6 +119,22 @@ let reduceregisters (n: int) (cfg: RISCCfg.t) : RISCCfg.t = LoadI (m2, tmpreg2); Load (tmpreg2, tmpreg2); BRegOp (brop, tmpreg1, tmpreg2, {index = r3})] + | 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); @@ -248,16 +265,11 @@ let reduceregisters (n: int) (cfg: RISCCfg.t) : RISCCfg.t = ) in - List.map (fun x -> - Printf.printf "Converting: %a\n" CfgRISC.RISCSimpleStatements.pp x; - let tmp = aux x in - Printf.printf "Into: %a\n\n" CfgRISC.RISCSimpleStatements.pplist tmp; - tmp - ) code |> List.concat + List.map aux code |> List.concat in - let aux (cfg: RISCCfg.t) all_variables = + 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 @@ -279,11 +291,6 @@ let reduceregisters (n: int) (cfg: RISCCfg.t) : RISCCfg.t = |> VariableMap.of_list in - Printf.printf "Most freq mapping:\n"; - List.iter (fun (a, b) -> Printf.printf "%s -> %s\n" a b) (VariableMap.to_list most_frequent_mapping); - Printf.printf "Least freq mapping:\n"; - List.iter (fun (a, b) -> Printf.printf "%s -> mem %d\n" a b) (VariableMap.to_list least_frequent_mapping); - (* 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 *) @@ -411,6 +418,6 @@ let reduceregisters (n: int) (cfg: RISCCfg.t) : RISCCfg.t = in - ( if List.length all_variables < n + ( if List.length all_variables <= n then cfg else aux cfg all_variables ) diff --git a/lib/utility/utility.ml b/lib/utility/utility.ml index f4d3489..df9e491 100644 --- a/lib/utility/utility.ml +++ b/lib/utility/utility.ml @@ -41,15 +41,15 @@ let int_not a = if a > 0 then 0 else 1 (* converts an integer to a list of chars such that it is pretty and linear *) -let rec fromIntToString (alphabet: string) (x: int) : string = - let base = String.length alphabet in - if x < 0 then - "" - else if x < base then - String.get alphabet x |> String.make 1 - else - (fromIntToString (alphabet) (x/base - 1)) ^ (String.get alphabet (x mod base) - |> String.make 1) +(* let rec fromIntToString (alphabet: string) (x: int) : string = *) +(* let base = String.length alphabet in *) +(* if x < 0 then *) +(* "" *) +(* else if x < base then *) +(* String.get alphabet x |> String.make 1 *) +(* else *) +(* (fromIntToString (alphabet) (x/base - 1)) ^ (String.get alphabet (x mod base) *) +(* |> String.make 1) *) (* true if every element of la is in lb *) @@ -93,7 +93,7 @@ let unique l = (* returns the unique elements of the concat of the lists *) let unique_union la lb = - unique (la @ lb) + la @ lb |> unique (* returns all elements both in la and in lb *) let unique_intersection la lb = @@ -107,6 +107,24 @@ let unique_intersection la lb = in aux la [] |> unique +(* given two lists of associations combines them and if an item is the same, + a provided function is applied to the associated values to create the new + association *) +let unique_union_assoc f l1 l2 = + let rec aux l acc = + match l with + | [] -> + acc + | (h1, h2) :: t -> + ( match List.find_opt (fun (a, _) -> a = h1) acc with + | None -> aux t ((h1, h2) :: acc) + | Some (_h1, h3) -> aux + t + ((h1, f h1 h2 h3) :: (List.remove_assoc h1 acc)) ) + in + aux l2 (aux l1 []) + + (* returns a list with at most n items and the rest in the second *) let rec take (n: int) (l: 'a list) : ('a list * 'a list) = match n with @@ -118,73 +136,27 @@ let rec take (n: int) (l: 'a list) : ('a list * 'a list) = let (t1, t2) = (take (n - 1) ls) in ((i :: t1), (t2)) -(* returns the list without the last element *) -let drop_last_element_list = - function - | [] -> [] - | l -> l |> List.rev |> List.tl |> List.rev - +(* takes a list and returns the same list without the first element; + different from List.tl since returns the empty list if there are not enough + items*) let drop_first_element_list = function | [] -> [] | _::l -> l -(* Complicated way to drop the last element and add a new option element to the - beginning *) -let prev l a = - match l with - | [] -> - [a] - | _ -> - a :: (List.map (fun x -> Some x) (drop_last_element_list l)) - -let pad l a n = - let l = List.map (fun i -> Some i) l in - if List.length l < n - then - l @ (List.init (n - List.length l) (fun _ -> a)) - else - l - -let pad_opt l a n = - if List.length l < n - then - l @ (List.init (n - List.length l) (fun _ -> a)) - else - l - -let combine la lb = - List.map2 (fun a b -> - match b with - None -> None - | Some b -> Some (a, b) - ) la lb - +(* retuns the last element of a list *) let rec last_list l = match l with [] -> failwith "Utility.last_list, not enough items" | [a] -> a | _::ll -> last_list ll -let add_to_last_list (la: 'a list list) (a: 'a) : 'a list list = - let rec aux la a = - match la with - [] -> [[a]] - | [l] -> [a :: l] - | l::la -> l :: (aux la a) - in - aux la a - +(* combines two lists into a list of tuples; different from List.combine since + lengths do not need to be equal, the functions return a list with length + equal to the minimum of the input lists *) let rec combine_twice la lb = match (la, lb) with | [], [] -> [] | [a], [b] -> [a, b] | a::la, b::lb -> (a, b) :: (combine_twice la lb) | _ -> [] - -let rec combine_thrice la lb lc = - match (la, lb, lc) with - | [], [], [] -> [] - | [a], [b], [c] -> [a, b, c] - | a::la, b::lb, c::lc -> (a, b, c) :: (combine_thrice la lb lc) - | _ -> [] diff --git a/lib/utility/utility.mli b/lib/utility/utility.mli index 76e7593..3a41b33 100644 --- a/lib/utility/utility.mli +++ b/lib/utility/utility.mli @@ -10,28 +10,20 @@ val int_more : int -> int -> int val int_more_eq : int -> int -> int val int_not : int -> int -val fromIntToString : string -> int -> string +(* val fromIntToString : string -> int -> string *) val inclusion : 'a list -> 'a list -> bool val equality : 'a list -> 'a list -> bool val subtraction : 'a list -> 'a list -> 'a list -val unique : 'a list -> 'a list val unique_union : 'a list -> 'a list -> 'a list val unique_intersection : 'a list -> 'a list -> 'a list +val unique_union_assoc : ('a -> 'b -> 'b -> 'b) -> ('a * 'b) list -> ('a * 'b) list -> ('a * 'b) list val take : int -> 'a list -> ('a list * 'a list) -val drop_last_element_list : 'a list -> 'a list val drop_first_element_list : 'a list -> 'a list -val prev : 'a list -> 'a option -> 'a option list -val pad : 'a list -> 'a option -> int -> 'a option list -val pad_opt : 'a option list -> 'a option -> int -> 'a option list - -val combine : 'a list -> 'b option list -> ('a * 'b) option list val last_list : 'a list -> 'a -val add_to_last_list : 'a list list -> 'a -> 'a list list val combine_twice : 'a list -> 'b list -> ('a * 'b) list -val combine_thrice : 'a list -> 'b list -> 'c list -> ('a * 'b * 'c) list From 9b48cc005f3edd4f35d4791cd54902c979a522a5 Mon Sep 17 00:00:00 2001 From: elvis Date: Sat, 11 Jan 2025 20:37:08 +0100 Subject: [PATCH 15/29] Updating gitignore for latex files --- .gitignore | 307 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 307 insertions(+) diff --git a/.gitignore b/.gitignore index bdbf52d..ee6b615 100644 --- a/.gitignore +++ b/.gitignore @@ -109,3 +109,310 @@ Network Trash Folder Temporary Items .apdisk +## Core latex/pdflatex auxiliary files: +*.aux +*.lof +*.log +*.lot +*.fls +*.out +*.toc +*.fmt +*.fot +*.cb +*.cb2 +.*.lb + +## Intermediate documents: +*.dvi +*.xdv +*-converted-to.* +# these rules might exclude image files for figures etc. +# *.ps +# *.eps +# *.pdf + +## Generated if empty string is given at "Please type another file name for output:" +.pdf + +## Bibliography auxiliary files (bibtex/biblatex/biber): +*.bbl +*.bbl-SAVE-ERROR +*.bcf +*.blg +*-blx.aux +*-blx.bib +*.run.xml + +## Build tool auxiliary files: +*.fdb_latexmk +*.synctex +*.synctex(busy) +*.synctex.gz +*.synctex.gz(busy) +*.pdfsync +*.rubbercache +rubber.cache + +## Build tool directories for auxiliary files +# latexrun +latex.out/ + +## Auxiliary and intermediate files from other packages: +# algorithms +*.alg +*.loa + +# achemso +acs-*.bib + +# amsthm +*.thm + +# beamer +*.nav +*.pre +*.snm +*.vrb + +# changes +*.soc + +# comment +*.cut + +# cprotect +*.cpt + +# elsarticle (documentclass of Elsevier journals) +*.spl + +# endnotes +*.ent + +# fixme +*.lox + +# feynmf/feynmp +*.mf +*.mp +*.t[1-9] +*.t[1-9][0-9] +*.tfm + +#(r)(e)ledmac/(r)(e)ledpar +*.end +*.?end +*.[1-9] +*.[1-9][0-9] +*.[1-9][0-9][0-9] +*.[1-9]R +*.[1-9][0-9]R +*.[1-9][0-9][0-9]R +*.eledsec[1-9] +*.eledsec[1-9]R +*.eledsec[1-9][0-9] +*.eledsec[1-9][0-9]R +*.eledsec[1-9][0-9][0-9] +*.eledsec[1-9][0-9][0-9]R + +# glossaries +*.acn +*.acr +*.glg +*.glo +*.gls +*.glsdefs +*.lzo +*.lzs +*.slg +*.slo +*.sls + +# uncomment this for glossaries-extra (will ignore makeindex's style files!) +# *.ist + +# gnuplot +*.gnuplot +*.table + +# gnuplottex +*-gnuplottex-* + +# gregoriotex +*.gaux +*.glog +*.gtex + +# htlatex +*.4ct +*.4tc +*.idv +*.lg +*.trc +*.xref + +# hypdoc +*.hd + +# hyperref +*.brf + +# knitr +*-concordance.tex +# TODO Uncomment the next line if you use knitr and want to ignore its generated tikz files +# *.tikz +*-tikzDictionary + +# listings +*.lol + +# luatexja-ruby +*.ltjruby + +# makeidx +*.idx +*.ilg +*.ind + +# minitoc +*.maf +*.mlf +*.mlt +*.mtc[0-9]* +*.slf[0-9]* +*.slt[0-9]* +*.stc[0-9]* + +# minted +_minted* +*.pyg + +# morewrites +*.mw + +# newpax +*.newpax + +# nomencl +*.nlg +*.nlo +*.nls + +# pax +*.pax + +# pdfpcnotes +*.pdfpc + +# sagetex +*.sagetex.sage +*.sagetex.py +*.sagetex.scmd + +# scrwfile +*.wrt + +# svg +svg-inkscape/ + +# sympy +*.sout +*.sympy +sympy-plots-for-*.tex/ + +# pdfcomment +*.upa +*.upb + +# pythontex +*.pytxcode +pythontex-files-*/ + +# tcolorbox +*.listing + +# thmtools +*.loe + +# TikZ & PGF +*.dpth +*.md5 +*.auxlock + +# titletoc +*.ptc + +# todonotes +*.tdo + +# vhistory +*.hst +*.ver + +# easy-todo +*.lod + +# xcolor +*.xcp + +# xmpincl +*.xmpi + +# xindy +*.xdy + +# xypic precompiled matrices and outlines +*.xyc +*.xyd + +# endfloat +*.ttt +*.fff + +# Latexian +TSWLatexianTemp* + +## Editors: +# WinEdt +*.bak +*.sav + +# Texpad +.texpadtmp + +# LyX +*.lyx~ + +# Kile +*.backup + +# gummi +.*.swp + +# KBibTeX +*~[0-9]* + +# TeXnicCenter +*.tps + +# auto folder when using emacs and auctex +./auto/* +*.el + +# expex forward references with \gathertags +*-tags.tex + +# standalone packages +*.sta + +# Makeindex log files +*.lpz + +# xwatermark package +*.xwm + +# REVTeX puts footnotes in the bibliography by default, unless the nofootinbib +# option is specified. Footnotes are the stored in a file with suffix Notes.bib. +# Uncomment the next line to have this generated file ignored. +#*Notes.bib \ No newline at end of file From 11adaa5103f3915f4d0582cfb2bb27574f3a51cd Mon Sep 17 00:00:00 2001 From: elvis Date: Wed, 15 Jan 2025 00:09:58 +0100 Subject: [PATCH 16/29] First draft report, removing exercises --- bin/dune | 12 - bin/main.ml | 79 ------- bin/test.miniimp | 25 --- dune-project | 4 - lib/exercises/dune | 5 - lib/exercises/exercises.ml | 109 --------- lib/exercises/exercises.mli | 60 ----- report/document.bib | 0 report/document.pdf | Bin 0 -> 200986 bytes report/document.tex | 199 +++++++++++++++++ report/report.tex | 425 ++++++++++++++++++++++++++++++++++++ 11 files changed, 624 insertions(+), 294 deletions(-) delete mode 100644 bin/main.ml delete mode 100644 bin/test.miniimp delete mode 100644 lib/exercises/dune delete mode 100644 lib/exercises/exercises.ml delete mode 100644 lib/exercises/exercises.mli create mode 100644 report/document.bib create mode 100644 report/document.pdf create mode 100644 report/document.tex create mode 100644 report/report.tex diff --git a/bin/dune b/bin/dune index 4f912b5..e630ca1 100644 --- a/bin/dune +++ b/bin/dune @@ -1,15 +1,3 @@ -(executable - (name main) - (public_name main) - (libraries exercises - miniImp - miniFun - analysis - utility) - (package miniImp) - (modes byte exe) -) - (executable (name miniFunInterpreter) (public_name miniFunInterpreter) diff --git a/bin/main.ml b/bin/main.ml deleted file mode 100644 index e0e6c0e..0000000 --- a/bin/main.ml +++ /dev/null @@ -1,79 +0,0 @@ -open MiniImp - -let colorred s = - "\027[31m" ^ s ^ "\027[0m" - -let () = - let program = " -def main with input in output out as - out := in; - a := 1; - b := 2; - c := a + a; -" - in - - (* Printf.printf "%s\n%s\n" (colorred "Program is") program; *) - - let get_result x = Lexing.from_string x |> Parser.prg Lexer.lex in - - let p = get_result program in - - (* Format.printf "%s\n%a\n@?" (colorred "AST is") Types.pp_p_exp p; *) - - let convertedcfg = CfgImp.convert_io 10 p in - - (* Printf.printf "%s\n%a" (colorred "Converted CFG is") CfgImp.SSCfg.pp convertedcfg; *) - - let convertedrisccfg = CfgRISC.convert convertedcfg in - - Printf.printf "%s\n%a" (colorred "Converted RISC CFG is") CfgRISC.RISCCfg.pp convertedrisccfg; - - (* ---------------------------------- *) - - let analysiscfg = DefinedVariables.compute_defined_variables convertedrisccfg in - - (* Printf.printf "%s\n%a" (colorred "Analysis CFG is") DefinedVariables.DVCfg.pp analysiscfg; *) - - (* Printf.printf "%s" (colorred "Undefined Variables are:"); *) - (* ( *) - (* match DefinedVariables.check_undefined_variables analysiscfg with *) - (* | None -> Printf.printf " none"; *) - (* | Some l -> Printf.printf " %a" DefinedVariables.Variable.pplist l; *) - (* ); *) - (* Printf.printf "\n"; *) - - let convertedrisccfg = DefinedVariables.compute_cfg analysiscfg in - - (* Printf.printf "%s\n%a" (colorred "Converted RISC after analysis CFG is") CfgRISC.RISCCfg.pp convertedrisccfg; *) - - - (* let analysiscfg = LiveVariables.compute_live_variables convertedrisccfg in *) - - (* Printf.printf "%s\n%a" (colorred "Live Analysis CFG is") LiveVariables.DVCfg.pp analysiscfg; *) - - (* let convertedrisccfg = LiveVariables.compute_cfg analysiscfg in *) - - (* Printf.printf "%s\n%a" (colorred "Converted RISC with no analysis CFG is") CfgRISC.RISCCfg.pp convertedrisccfg; *) - - - (* let convertedrisccfg = LiveVariables.compute_cfg (LiveVariables.optimize_cfg analysiscfg) in *) - - (* Printf.printf "%s\n%a" (colorred "Converted RISC after analysis CFG is") CfgRISC.RISCCfg.pp convertedrisccfg; *) - - let convertedrisccfg = ReduceRegisters.reduceregisters 4 convertedrisccfg in - - Printf.printf "%s\n%a" (colorred "Converted RISC after reducing registers CFG is") CfgRISC.RISCCfg.pp convertedrisccfg; - - - (* ---------------------------------- *) - - let risc = RISC.convert convertedrisccfg in - - Printf.printf "%s\n%a" (colorred "RISC code is") RISC.RISCAssembly.pp risc; - - let computerisc = RISCSemantics.reduce risc in - - Printf.printf "%s\n%d\n" (colorred "Output of RISC code is") computerisc; - - () diff --git a/bin/test.miniimp b/bin/test.miniimp deleted file mode 100644 index 355d16b..0000000 --- a/bin/test.miniimp +++ /dev/null @@ -1,25 +0,0 @@ -def main with input n output result as - if (n % 2) == 0 then result := 1 - else ( - result := 0; - s := 0; - while (0 == ((n - 1) / (2 ^ s)) % 2) do ( - s := s + 1 - ); - d := ((n - 1) / 2 ^ s); - for (i := 20, i > 0, i := i - 1) do ( - a := rand(n - 4) + 2; - x := powmod(a, d, n); - y := 0; - for (j := 0, j < s, j := j+1) do ( - y := powmod(x, 2, n); - if (y == 1 && (not x == 1) && (not x == n - 1)) then - result := 1; - else - skip; - x := y; - ); - if not y == 1 then result := 1; - else skip; - ) - ) diff --git a/dune-project b/dune-project index 85457a3..b497fd4 100644 --- a/dune-project +++ b/dune-project @@ -21,7 +21,3 @@ (package (name miniFun) (depends ocaml dune utility)) - -(package - (name exercises) - (depends ocaml dune)) diff --git a/lib/exercises/dune b/lib/exercises/dune deleted file mode 100644 index 280c335..0000000 --- a/lib/exercises/dune +++ /dev/null @@ -1,5 +0,0 @@ -(library - (name exercises) - (public_name exercises)) - -(include_subdirs qualified) \ No newline at end of file diff --git a/lib/exercises/exercises.ml b/lib/exercises/exercises.ml deleted file mode 100644 index 895bde5..0000000 --- a/lib/exercises/exercises.ml +++ /dev/null @@ -1,109 +0,0 @@ -type a_exp = - Aval of int - | Plus of a_exp * a_exp - | Minus of a_exp * a_exp - | Times of a_exp * a_exp - | Of_bool of b_exp -and b_exp = - Bval of bool - | And of b_exp * b_exp - | Or of b_exp * b_exp - | Not of b_exp - | Minor of a_exp * a_exp - - -let rec eval_a_exp node = - match node with - Aval (i) -> i - | Plus (i, j) -> (eval_a_exp i) + (eval_a_exp j) - | Minus (i, j) -> (eval_a_exp i) - (eval_a_exp j) - | Times (i, j) -> (eval_a_exp i) * (eval_a_exp j) - | Of_bool b -> if (eval_b_exp b) then 1 else 0 -and eval_b_exp node = - match node with - Bval (b) -> b - | And (a, b) -> (eval_b_exp a) && (eval_b_exp b) - | Or (a, b) -> (eval_b_exp a) || (eval_b_exp b) - | Not b -> not (eval_b_exp b) - | Minor (i, j) -> (eval_a_exp i) < (eval_a_exp j) - -type 'a my_tree = - Leaf of 'a - | Node of ('a my_tree) list - -let mod_list y = - (List.fold_left - (fun acc x -> - match acc with - | [a] when ((List.hd a) = x) -> [x :: a] - | a :: tl when ((List.hd a) = x) -> (x :: a) :: tl - | _ -> [x] :: acc) - [] - y) - |> List.rev - -(* -------------------------------------------------------------------------- *) - -let to_tup f g = - fun x -> match x with - (a, b) -> (f a, g b) - -let partialsum l = - snd (List.fold_left_map (fun acc x -> (acc+x, acc+x)) 0 l) - -type label = - string - -type 'a finite_state_automata = { - l: label; - next: ('a finite_state_automata * 'a list) list; - final: bool; -} - -let rec check_included input fsa = - match input with - [] -> fsa.final - | a::rest -> ( - match List.find_opt (fun x -> List.mem a (snd x)) fsa.next with - None -> false - | Some x -> check_included rest (fst x) - ) - - -(* -------------------------------------------------------------------------- *) - -module StringMap = Map.Make(String) - -type fsa = { - vertices: bool StringMap.t; - edges: (string * char) StringMap.t; - state: string; -} - -let ex8 (instr: char list) (infsa: fsa) = - let rec helper_ex8 (i: char list) (ifsa: fsa) (current: string) = - match i with - [] -> ( - match StringMap.find_opt current ifsa.vertices with - None -> false - | Some b -> b - ) - | a::rest -> ( - match StringMap.find_first_opt (fun _ -> true) (StringMap.filter (fun x (_, y) -> x = current && y = a) ifsa.edges) with - None -> false - | Some (_, (outedge, _)) -> helper_ex8 rest ifsa outedge - ) - in helper_ex8 instr infsa infsa.state - -type binary_tree = - Node of binary_tree * binary_tree - | Leaf of int - -let ex9 b = - let rec helper_ex9 b' n = - match b' with - Leaf a -> a + n - | Node (r, l) -> (helper_ex9 r (helper_ex9 l n)) - in helper_ex9 b 0 - -(* -------------------------------------------------------------------------- *) diff --git a/lib/exercises/exercises.mli b/lib/exercises/exercises.mli deleted file mode 100644 index 7355881..0000000 --- a/lib/exercises/exercises.mli +++ /dev/null @@ -1,60 +0,0 @@ -type a_exp = - Aval of int - | Plus of a_exp * a_exp - | Minus of a_exp * a_exp - | Times of a_exp * a_exp - | Of_bool of b_exp -and b_exp = - Bval of bool - | And of b_exp * b_exp - | Or of b_exp * b_exp - | Not of b_exp - | Minor of a_exp * a_exp - - -val eval_a_exp: a_exp -> int -val eval_b_exp: b_exp -> bool - -type 'a my_tree = - Leaf of 'a - | Node of ('a my_tree) list - -val mod_list: 'a list -> 'a list list - -(* --------------------------------------------------------------------------- *) - -val to_tup : ('a -> 'b) -> ('c -> 'd) -> (('a * 'c) -> ('b * 'd)) - -val partialsum : int list -> int list - -type label = - string - -type 'a finite_state_automata = { - l: label; - next: ('a finite_state_automata * 'a list) list; - final: bool; -} - -val check_included : 'a list -> 'a finite_state_automata -> bool - -(* -------------------------------------------------------------------------- *) - -module StringMap : Map.S with type key = string - -type fsa = { - vertices: bool StringMap.t; - edges: (string * char) StringMap.t; - state: string; -} - -val ex8 : char list -> fsa -> bool - - -type binary_tree = - Node of binary_tree * binary_tree - | Leaf of int - -val ex9 : binary_tree -> int - -(* -------------------------------------------------------------------------- *) diff --git a/report/document.bib b/report/document.bib new file mode 100644 index 0000000..e69de29 diff --git a/report/document.pdf b/report/document.pdf new file mode 100644 index 0000000000000000000000000000000000000000..1974e01e979adffb502935aedb2611312141745b GIT binary patch literal 200986 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�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∨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

\(\defeq\) `def main with input' `output' as + + \(\defeq\) skip + \alt{} `:=' + \alt{} `;' + \alt{} `if' `then' `else' + \alt{} `while' `do' + \alt{} `for' `(' `,' `,' `)' `do' + + \(\defeq\) | `\&\&' | `||' | `not' + \alt{} `<' | `<=' | `>' | `>=' + \alt{} `==' + + \(\defeq\) | | `+' | `-' | `*' | `/' a + \alt{} `\%' | `^' | `powmod' `(' `,' `,' `)' | `rand' `(' `)' + \end{grammar} + + Where \texttt{\%} is the modulo operator and \texttt{a a \% a} is the powermod operator; + the variables are all integers, \texttt{n} is an integer and \texttt{v} is a boolean litteral. + + The additional arithmetic expressions' semantics are implemented in a similar manner as with the other. + + The sematic of \texttt{for} is as follows: + + \begin{center} + \inference[\texttt{for}] + {\langle\sigma, c_1\rangle \to_c \sigma_1 & \langle\sigma_1, \texttt{while } b \texttt{ do } c_3 \texttt{; } c_2 \rangle \to_c \sigma_2} % chktex 1 + {\langle\sigma, \texttt{for} \texttt{(} c_1 \texttt{, } b \texttt{, } c_2 \texttt{)} \texttt{ do } c_3 \rangle \to_c \sigma_2} % chktex 1 chktex 9 + \end{center} + + but the implementation exploits the structure and doesn't simply rewrite the for loop as a while loop. + \end{subsection} + + \begin{subsection}{MiniFun Semantics} + The semantic of the MiniFun language is implemented in the \href{../lib/miniFun/Semantics.mli}{Semantics.mli} and \href{../lib/miniFun/Semantics.ml}{Semantics.ml} file. + A \texttt{reduce} function is provided that transforms the AST into the avluated value or an error. + The AST type is defined in \href{../lib/miniFun/Types.mli}{Types.mli} and in \href{../lib/miniFun/Types.ml}{Types.ml}. + + A program \texttt{t} is defined as follows: + \begin{grammar} + \(\defeq\) | | | `(' `,' `)' + \alt{} `fun' `:' `=>' | | | % chktex 38 + \alt{} `powmod' `(' `,' `,' `)' + \alt{} `rand' `(' `)' | + \alt{} `if' `then' `else' + \alt{} `let' `=' `in' + \alt{} `let' `rec' `: ' `=' `in' + + \(\defeq\) `not' | `fst' | `scn' + + \(\defeq\) `+' | `-' | `*' | `/' | `\%' | `^' | `\&\&' | `||' | `==' + \alt{} `<' | `<=' | `>' | `>=' + \end{grammar} + + As reflected in the grammar, tuples have been implemented and the unary functions fst and scn return respectively the first element of the tuple and the second. + \end{subsection} +\end{section} + + +\begin{section}{Types for MiniFun} + A type \(\tau\) is defined as either {\it int}, {\it bool}, a touple or a function. + + \begin{equation*} + \tau \defeq {\it int\/}\ \vert\ {\it bool\/}\ \vert\ (\tau,\tau)\ \vert\ \tau \to \tau + \end{equation*} + + The deduction rules regarding tuples are similar to those for functions: + + \begin{center} + \inference[\texttt{Tuple}] + {\Gamma \vdash t_1 \triangleright \tau_1 & \Gamma \vdash t_2 \triangleright \tau_2} % chktex 1 + {\Gamma \vdash (t_1, t_2) \triangleright \tau_1 * \tau_2} % chktex 1 + \end{center} + + \begin{center} + \inference[\texttt{Fst}] + {\Gamma \vdash t_1 \triangleright \tau_1 } % chktex 1 + {\Gamma \vdash \texttt{fst} (t_1, t_2) \triangleright \tau_1} % chktex 1 + \end{center} + + \begin{center} + \inference[\texttt{Snd}] + {\Gamma \vdash t_2 \triangleright \tau_2 } % chktex 1 + {\Gamma \vdash \texttt{snd} (t_1, t_2) \triangleright \tau_2} % chktex 1 + \end{center} + + The rules for function declaration with type annotations are thus: + + \begin{center} + \inference[\texttt{Fun}] + {\Gamma[x \mapsto \tau] \vdash t \triangleright \tau'} % chktex 1 + {\Gamma \vdash \texttt{fun} x \texttt{:} \tau \to \tau' \texttt{=>} t \triangleright \tau \to \tau'} % chktex 1 + \end{center} + + \begin{center} + \inference[\texttt{FunRec}] + {\Gamma[f \mapsto \tau \to \tau'; x \mapsto \tau] \vdash t_1 \triangleright \tau' & \Gamma[f \mapsto \tau \to \tau'] \vdash t_2 \triangleright \tau''} % chktex 1 + {\Gamma \vdash \texttt{let rec} f x \texttt{:} \tau \to \tau' \texttt{ = } t_1 \texttt{ in } t_2 \triangleright \tau''} % chktex 1 + \end{center} + + In the files \href{../lib/miniFun/TypeChecker.mli}{TypeChecker.mli} and \href{../lib/miniFun/TypeChecker.ml}{TypeChecker.ml} there is the implementation of the deduction rules, but returns either the valid type of the expression or an error instead of simply the required option type of the valid type. +\end{section} + +\begin{section}{Parsing} + \begin{subsection}{MiniImp} + Operators listed in order of precedence from highest to lowest: + + \begin{center} + \begin{tblr}{colspec={|c|c|}, rowspec={|Q|QQQQQQQ|}} + Operator & Associativity \\ + while & left \\ + \^{} & right \\ + * / mod & left \\ + not & {-} \\ + + {-} \(\vert\vert\) \&\& & left \\ + if & left \\ + {;} & left \\ + \end{tblr} + \end{center} + + The expressions \(c_1 \texttt{;} c_2\) and \(c_1 \texttt{;}\) are both recognized and give respectively \(\texttt{SEQUENCE(} c_1 \texttt{,} c_2 \texttt{)}\) % chktex 9 + and \(c_1\), such that semicolons can be placed always at the end of a command. + + Integers with a preceding minus sign can be interpreted as the opposite integer, with obviously lower precedence than the binary operator minus. + \end{subsection} + + + \begin{subsection}{MiniFun} + A decision was made to interpret \texttt{\textbackslash}, \texttt{lambda} and \texttt{fun} all as the start of the definition of a function just for ease of typing. They are associated to the same token \texttt{LAMBDA}. + + Operators listed in order of precedence from highest to lowest: + + \begin{center} + \begin{tblr}{colspec={|c|c|}, rowspec={|Q|QQQQQQQQQQQQ|}} + Operator & Associativity \\ + function application & right \\ + let & left \\ + fun & left \\ + fst snd & left \\ + not rand & {-} \\ + \^{} & right \\ + * / mod & left \\ + + {--} & left \\ + == {\(<\)} {\(\leq\)} {\(>\)} {\(\geq\)} & left \\ + \(\vert\vert\) \&\& & left \\ + powmod & left \\ + \(\lambda\) if let letrec & left \\ + \end{tblr} + \end{center} + + Tuples require parentesis in their definition, but the tuple type does not since there is no ambiguity. The symbol \texttt{->} that defines the function type is right associative and has lowest precedence. + \end{subsection} + + \begin{subsection}{Interpreters} + Both MiniImp and MiniFun have each an interpreter (\href{../bin/miniFunInterpreter.ml}{miniFunInterpreter.ml} and \href{../bin/miniFunInterpreter.ml}{miniFunInterpreter.ml}) that uses the package \href{https://opam.ocaml.org/packages/clap/}{Clap} to parse command line arguments and generate help pages. + + The input to the program can be supplied both in stdin or as a command parameter after \texttt{-v}. The MiniFun interpreter also check the types before computing the output of the program and returns an error in case the types mismatch. + \end{subsection} +\end{section} + + +\begin{section}{Control Flow Graph} + The control flow graph data structure is implemented in the analysis library in the files \href{../lib/analysis/Cfg.ml}{Cfg.ml} and \href{../lib/analysis/Cfg.mli}{Cfg.mli}. + + Each node contains only an id to distinguish from others. + + The control flow structure is composed of a flag to know if it is empty or contains nodes and the set of all contained nodes. + Since each node can only have at maximum 2 nodes as next nodes, the data structure contains a map from each node to a tuple of the two nodes or to a node. + The structure also contains the back edges of each node implemented as a map from each node to a list of nodes, the input value, the variables that are the input and ouput, the initial node and the terminal node. + Finally there is a map from each node to a list of generic elements that in our case are simple statements. + + \begin{subsection}{MiniImp Simple Statement} + MiniImp Simple Statements \(t\) is defined as follows: + + \begin{grammar} + \(\defeq\) skip | `:=' | `{?}' + + \(\defeq\) | `\&\&' | `||' | `not' + \alt{} `==' | `<' | `<=' | `>' | `>=' + + \(\defeq\) | | `+' | `-' | `*' | `/' + \alt{} `mod' | `^' | `rand' + \end{grammar} + + The implemented cfg is neither minimal nor maximal, but can be either or both for some programs. In particular each node as associated a list of statements and sequence of statements in the AST is put, if possible, in the same node. + + \texttt{?} is only allowed as the last element of the list of statemets associated with a node and a node has associated a \texttt{?} if and only if they have two next nodes. + + The for loop is translated as: + + \begin{center} + \begin{tikzpicture}[ + node/.style = {draw,rounded corners,blur shadow, fill=white,align=center}, + box/.style={rectangle,draw,inner sep=10pt} + ] + \node[node] at (0, 1.3) (b11) {$i_1^1$}; + \node[node] at (0, 0) (b12) {$f_1^1$}; + \node[box,fit = (b11) (b12)] (externalbox1) {}; + + \node[node, opacity=0] at (3, 1.3) (bb1) {$i_1^b$}; + \node[node, opacity=0] at (3, 0) (bb2) {$f_1^b$}; + \node[node] at (3, 0.65) (bball) {$i^b$}; + \node[box,fit = (bb1) (bb2)] (externalboxb) {}; + + \node[node] at (6, 1.3) (b21) {$i_1^2$}; + \node[node] at (6, 0) (b22) {$f_1^2$}; + \node[box,fit = (b21) (b22)] (externalbox2) {}; + + \node[node] at (9, 1.3) (b31) {$i_1^3$}; + \node[node] at (9, 0) (b32) {$f_1^3$}; + \node[box,fit = (b31) (b32)] (externalbox3) {}; + + \node[fit = (externalbox1) (externalbox2) (externalbox3) (externalboxb)] (boxall) {}; + + \begin{scope}[rounded corners,-latex] + \path (externalbox1) edge (b11.north); + \path[dashed] (b11) edge (b12); + \path (b12) edge (externalbox1); + + \path (externalboxb) edge (bball.north); + \path (bball) edge (externalboxb.south); + + \path (externalbox2) edge (b21.north); + \path[dashed] (b21) edge (b22); + \path (b22) edge (externalbox2); + + \path (externalbox3) edge (b31.north); + \path[dashed] (b31) edge (b32); + \path (b32) edge (externalbox3); + \end{scope} + + \node[above] at (boxall.north) {\texttt{for (}\(c_1\)\texttt{,} \(b\)\texttt{,} \(c_2\)\texttt{) do} \(c_3\)}; + \node[left] at (externalbox1.west) {\(c_1\):}; + \node[left] at (externalboxb.west) {\(b\):}; + \node[left] at (externalbox2.west) {\(c_2\):}; + \node[left] at (externalbox3.west) {\(c_3\):}; + + \node[node] at (4.5, -2.7) (b11) {$i_1^1$}; + \node[node] at (4.5, -4) (b12) {$f_1^1$}; + + \node[node] at (4.5, -5.3) (guard) {$i^b$}; + + \node[node] at (4.5, -6.6) (b31) {$i_1^3$}; + \node[node] at (4.5, -7.9) (b32) {$f_1^3$}; + + \node[node] at (4.5, -9.2) (b21) {$i_1^2$}; + \node[node] at (4.5, -10.5) (b22) {$f_1^2$}; + + \node[node] at (4.5, -11.8) (exitnode) {\texttt{skip}}; + + \node[box, fit = (b11) (b12) (guard) (b31) (b32) (b21) (b22) (exitnode), + inner sep=15pt] (externalboxall) {}; + + \begin{scope}[rounded corners,-latex] + \path[dashed] (b11) edge (b12); + \path[dashed] (b21) edge (b22); + \path[dashed] (b31) edge (b32); + + \path (b12) edge (guard); + \path (guard) edge (b31); + + \path (b32) edge (b21); + \path (b22.135) edge[bend left=20] (guard.220); + + \path (guard.-40) edge[bend left=15] (exitnode.35); + + \path (externalboxall.north) edge (b11.north); + \path (exitnode.south) edge (externalboxall.south); + \end{scope} + + \node[above] at (externalboxall.north) {becomes:}; + \end{tikzpicture} + \end{center} + + We highlight the fact that the operation powermod is absent in the grammar of simple statements. In fact all powermod are replaced in the AST before translating into CFG with the function \texttt{rewrite_instructions} in \href{../lib/miniImp/replacePowerMod.ml}{replacePowerMod.ml} and \href{../lib/miniImp/replacePowerMod.mli}{replacePowerMod.mli}. + + \texttt{powmod(}\(a_1\)\texttt{, }\(a_2\)\texttt{, }\(a_3\)\texttt{)} % chktex 9 chktex 36 + is translated into: + + \begin{lstlisting} +pow := £\(a_1\)£; +exp := £\(a_2\)£; +mod := £\(a_3\)£; +res := 1; +if exp < 0 then + exp := 0 - exp; +else + skip; +while exp > 0 do ( + if 1 = exp % 2 then + res := (res * pow) % mod; + else + skip; + + pow := (pow * pow) % mod; + exp := exp / 2; +) +\end{lstlisting} + + The variables \texttt{pow}, \texttt{exp}, \texttt{mod} and \texttt{res} are all fresh and the value of res is then substituted into powmod place. This might need some more scope than only the expression since \texttt{powmod} may be included in a \texttt{if} guard, thus it is placed before the \texttt{if}; in case it is in the guard of a \texttt{while} or a \texttt{for} loop it is also updated at the end of the body. + + The reason for substituting \texttt{powmod} in the AST is that we would need to add nodes to form the \texttt{if} and \texttt{while} and it would prove more difficult. + \end{subsection} +\end{section} + +\begin{section}{Intermediate Code Generation} + \begin{subsection}{MiniRISC CFG} + In the files \href{../lib/miniImp/CfgRISC.ml}{CfgRISC.ml} and \href{../lib/miniImp/CfgRISC.mli}{CfgRISC.mli} the CFG generated from the AST gets translated into intermediate code with the following MiniRISC simple statements: + + \begin{grammar}\label{grammar:MiniRISC} + \(\defeq\) Nop + \alt{} BRegOp \(\Rightarrow\) + \alt{} BImmOp \(\Rightarrow\) + \alt{} URegOp \(\Rightarrow\) + \alt{} Load \(\Rightarrow\) + \alt{} LoadI \(\Rightarrow\) + \alt{} Store \(\Rightarrow\) + + \(\defeq\) Add | Sub | Mult | Div | Mod | Pow | And | Or + \alt{} Eq | Less | LessEq | More | MoreEq + + \(\defeq\) AddI | SubI | MultI | DivI | ModI | PowI | AndI | OrI + \alt{} EqI | LessI | LessEqI | MoreI | MoreEqI + + \(\defeq\) Not | Copy | Rand + \end{grammar} + + Since we stride towards shorter code and less instructions, we would prefer to use the \texttt{biop} version of each operation whenever possible. So for some operations that are commutative if the first term is the immediate value we swap the terms and use the \texttt{biop} variant instead of loading the value into a register and using the register for the calculation. Also some operations like \texttt{>} and \texttt{<} are opposite, so to invert the order we need to use the other \texttt{biop} version. + The input variable and the output variable are also mapped to \texttt{in} and \texttt{out} registers, while all other variables are given fresh registers. + \end{subsection} + + \begin{subsection}{MiniRISC} + The MiniRISC CFG is finally tranlated into MiniRISC intermediate code by the function \texttt{convert} in the files \href{../lib/miniImp/RISC.ml}{RISC.ml} and \href{../lib/miniImp/RISC.mli}{RISC.mli}. + The grammar of MiniRISC is analogous to the one for \hyperref[grammar:MiniRISC]{MiniRISC Simple Statements}: + + \begin{grammar} + \(\defeq\) Nop + \alt{} BRegOp \(\Rightarrow\) + \alt{} BImmOp \(\Rightarrow\) + \alt{} URegOp \(\Rightarrow\) + \alt{} Load \(\Rightarrow\) + \alt{} LoadI \(\Rightarrow\) + \alt{} Store \(\Rightarrow\) + \alt{} Jump + \alt{} CJump + \alt{} Label + + \(\defeq\) Add | Sub | Mult | Div | Mod | Pow | And | Or + \alt{} Eq | Less | LessEq | More | MoreEq + + \(\defeq\) AddI | SubI | MultI | DivI | ModI | PowI | AndI | OrI + \alt{} EqI | LessI | LessEqI | MoreI | MoreEqI + + \(\defeq\) Not | Copy | Rand + \end{grammar} + + where \texttt{l} is a string that uniquely identifies a label. + \end{subsection} + + \begin{subsection}{RISC Semantics} + It is also implemented in the files \href{../lib/miniImp/RISCSemantics.ml}{RISCSemantics.ml} and \href{../lib/miniImp/RISCSemantics.mli}{RISCSemantics.mli} a reduce function, that evaluates MiniRISC code. + The labels are used as is and not replaced by offsets, so the code is translated into a map from labels to code blocks for ease of computation. + \end{subsection} +\end{section} + +\begin{section}{Dataflow Analysis} + A refined CFG structure used for analysis is defined in \href{../lib/analysis/Dataflow.ml}{Dataflow.ml} and \href{../lib/analysis/Dataflow.mli}{Dataflow.mli}. The CFG is supplemented with a map from each node to the support structure that stores the list of defined variables or live variables. Since the CFG is not minimal, there is also a list for each simple statement. A fixed point function then applies the input fuction until the map does not change. Simple structural equality is not appropriate since order in the lists should not matter; an internal function for equality is used. + + \begin{subsection}{Defined Variables} + In the files \href{../lib/miniImp/definedVariables.ml}{definedVariables.ml} and \href{../lib/miniImp/definedVariables.mli}{definedVariables.mli} three functions are defined: \texttt{compute_defined_variables}, \texttt{compute_cfg} and \texttt{check_undefined_variables}. + + \texttt{compute_defined_variables} creates the appropriate structure for the analysis and runs it. It returns the whole analysis structure. + \texttt{compute_cfg} returns the CFG from the analysis data structure; in the case of defined variables analysis the CFG returned is the same as the one in input of \texttt{compute_defined_variables}. + \texttt{check_undefined_variables} returns all variables that might be undefined at time of use. + + Since the greatest fixed point is computed, first all variables are retrived from all code, then assigned to each input and ouput list of variables for each line of code. + + Since it is an approximation some behaviour might not be intuitive. For example: + +\begin{lstlisting} +for (x := 0, x < 10, x := x + 1) do ( + y := rand(x); +); +output := y; +\end{lstlisting} + + will return the register associated with \texttt{y} as undefined since the guard of the for cycle might never be true. + \end{subsection} + + \begin{subsection}{Live Variables} + In the files \href{../lib/miniImp/liveVariables.ml}{liveVariables.ml} and \href{../lib/miniImp/liveVariables.mli}{liveVariables.mli} three functions are defined: \texttt{compute_live_variables}, \texttt{compute_cfg} and \texttt{optimize_cfg}. + + \texttt{compute_live_variables} creates the appropriate structure for the analysis and runs it. It returns the whole analysis structure. + \texttt{compute_cfg} returns the CFG from the analysis data structure. + \texttt{optimize_cfg} applies liveness analysis to reduce the number of registers used; returns the analysis structure (not the RISC CFG). + \end{subsection} +\end{section} + +\begin{section}{Target Code Generation} + In the files \href{../lib/miniImp/reduceRegisters.ml}{reduceRegisters.ml} and \href{../lib/miniImp/reduceRegisters.mli}{reduceRegisters.mli} the function \texttt{reduceregisters} reduces the number of used registers by counting the syntactic occurrence of each variable and partitioning the set keeping the most used as registers. All registers are either renamed or put into memory. It is allowed for the input or output registers to be put in memory, in the latter case some code is added at the end of the program to retrive the value and put into a register (register \texttt{2}). + + \begin{subsection}{MiniImp to MiniRISC compiler} + The file \href{../bin/miniImpInterpreterReg.ml}{miniImpInterpreterReg.ml} compiles from MiniImp to MiniRISC or execute the MiniRISC code. It uses the package \href{https://opam.ocaml.org/packages/clap/}{Clap} to parse command line arguments and generate help pages. + + The input to the program can be supplied both in stdin or as a command parameter after \texttt{-v}. The flags for disabling the check for undefined variables or liveness analysis optimization are \texttt{-u} and \texttt{-l} respectively. + \end{subsection} +\end{section} + +\begin{section}{Running the code} + The project uses the following packages: \href{https://dune.build/}{Dune}, \href{https://gallium.inria.fr/~fpottier/menhir/}{Menhir} and \href{https://github.com/rbardou/clap}{Clap}. They can be installed via \href{https://opam.ocaml.org/}{Opam} with the command \texttt{opam install dune menhir clap}. + To compile the project simply run \texttt{dune build}. To run the test run \texttt{dune runtest}. + In order to execute one of the interpreters run \texttt{dune exec {-}{-} }. To see a list of all options run \texttt{dune exec {-}{-} -h}. A binary version of the executables can also be found in \href{./_build/default/bin/}{./_build/default/bin/}. +\end{section} +%%% Local Variables: +%%% TeX-command-extra-options: "-shell-escape" +%%% TeX-master: "document.tex" +%%% End: From cf0bc41a236c2ea6714437836619cd9d4aee4d84 Mon Sep 17 00:00:00 2001 From: elvis Date: Wed, 15 Jan 2025 00:10:44 +0100 Subject: [PATCH 17/29] Minor modifications, adding comments --- bin/miniFunInterpreter.ml | 3 ++- bin/miniImpInterpreterReg.ml | 35 +++++++++++++++++++----- lib/analysis/Dataflow.ml | 12 ++++++--- lib/analysis/Dataflow.mli | 4 ++- lib/miniFun/Lexer.mll | 1 + lib/miniFun/Parser.mly | 3 +-- lib/miniImp/CfgImp.ml | 16 +++++------ lib/miniImp/CfgImp.mli | 1 - lib/miniImp/CfgRISC.ml | 3 --- lib/miniImp/Parser.mly | 2 +- lib/miniImp/RISCSemantics.ml | 47 +++++++++++++++++++++++++-------- lib/miniImp/definedVariables.ml | 3 +++ lib/miniImp/liveVariables.ml | 4 +-- lib/miniImp/reduceRegisters.ml | 38 +++++++++++++++++++------- lib/miniImp/replacePowerMod.ml | 6 ++--- 15 files changed, 124 insertions(+), 54 deletions(-) diff --git a/bin/miniFunInterpreter.ml b/bin/miniFunInterpreter.ml index 43bea4b..ea20218 100644 --- a/bin/miniFunInterpreter.ml +++ b/bin/miniFunInterpreter.ml @@ -58,7 +58,8 @@ let () = | Lexer.LexingError msg -> Printf.fprintf stderr "%a: %s\n" print_position lexbuf msg; exit (-1) - | Parser.Error -> Printf.fprintf stderr "%a: syntax error\n" print_position lexbuf; + | Parser.Error -> + Printf.fprintf stderr "%a: syntax error\n" print_position lexbuf; exit (-1) in let _ = diff --git a/bin/miniImpInterpreterReg.ml b/bin/miniImpInterpreterReg.ml index 7de8e42..9bbb5c3 100644 --- a/bin/miniImpInterpreterReg.ml +++ b/bin/miniImpInterpreterReg.ml @@ -5,7 +5,7 @@ open Lexing (* Command Arguments *) let () = - Clap.description "Interpreter for MiniImp language."; + Clap.description "Interpreter for MiniImp language to RISC code."; let files = Clap.section ~description: "Files to consider." "FILES" in let values = Clap.section ~description: "Input values." "VALUES" in @@ -36,6 +36,22 @@ let () = false in + let checkundefined = Clap.flag + ~description: "Optional flag for disabling the check for undefined variables." + ~section: values + ~unset_long: "undefined" + ~unset_short: 'u' + true + in + + let optimizereg = Clap.flag + ~description: "Optional flag for disabling optimizing registers with liveness analysis." + ~section: values + ~unset_long: "liveness" + ~unset_short: 'l' + true + in + let inputval = Clap.default_int ~description: "Optional input value to feed to the program. \ If not specified it is read from stdin." @@ -85,7 +101,7 @@ let () = CfgRISC.convert in - let () = ( + if checkundefined then ( match DefinedVariables.compute_defined_variables return_value |> DefinedVariables.check_undefined_variables with @@ -94,13 +110,20 @@ let () = Printf.printf "Error: undefined variables: %a\n" DefinedVariables.Variable.pplist l; exit (-1) - ) in + ) else (); + + let return_value = + if optimizereg then + return_value |> + LiveVariables.compute_live_variables |> + LiveVariables.optimize_cfg |> + LiveVariables.compute_cfg + else + return_value + in let return_value = return_value |> - LiveVariables.compute_live_variables |> - LiveVariables.optimize_cfg |> - LiveVariables.compute_cfg |> ReduceRegisters.reduceregisters registers |> RISC.convert in diff --git a/lib/analysis/Dataflow.ml b/lib/analysis/Dataflow.ml index ef14743..da583f8 100644 --- a/lib/analysis/Dataflow.ml +++ b/lib/analysis/Dataflow.ml @@ -41,7 +41,9 @@ module Make (M: Cfg.PrintableType) (I: Cfg.PrintableType) = struct match Utility.equality a.internalin b.internalin, Utility.equality a.internalout b.internalout, (List.fold_left2 (fun acc (ain, aout) (bin, bout) - -> acc && (Utility.equality ain bin) && (Utility.equality aout bout) + -> acc && + (Utility.equality ain bin) && + (Utility.equality aout bout) ) true a.internalbetween b.internalbetween) with | true, true, true -> true @@ -54,7 +56,7 @@ module Make (M: Cfg.PrintableType) (I: Cfg.PrintableType) = struct internalvar: internalnode Cfg.NodeMap.t; } - let compareinternal (a: internalnode Cfg.NodeMap.t) (b: internalnode Cfg.NodeMap.t) = + let compareinternal a b = Cfg.NodeMap.fold (fun node bi acc -> match Cfg.NodeMap.find_opt node a with @@ -141,7 +143,9 @@ module Make (M: Cfg.PrintableType) (I: Cfg.PrintableType) = struct let fixed_point ?(init : (elt list -> internalnode) = - (fun _ -> {internalin = []; internalout = []; internalbetween = []})) + (fun _ -> {internalin = []; + internalout = []; + internalbetween = []})) ?(update : (t -> Cfg.Node.t -> internalnode) = (fun t n -> Cfg.NodeMap.find n t.internalvar)) (t: t) @@ -150,7 +154,7 @@ module Make (M: Cfg.PrintableType) (I: Cfg.PrintableType) = struct the update function takes the node and the whole structure and is expected to return the updated structure for the appropriate node, update function is applied to the resulting structure until no change is - observed + observed with compareinternal function *) let rec helper t = let newt = diff --git a/lib/analysis/Dataflow.mli b/lib/analysis/Dataflow.mli index f45667f..76e9cfa 100644 --- a/lib/analysis/Dataflow.mli +++ b/lib/analysis/Dataflow.mli @@ -18,7 +18,9 @@ module type C = sig val from_cfg : cfgt -> t val to_cfg : t -> cfgt - val fixed_point : ?init:(elt list -> internalnode) -> ?update:(t -> Cfg.Node.t -> internalnode) -> t -> t + val fixed_point : + ?init:(elt list -> internalnode) -> + ?update:(t -> Cfg.Node.t -> internalnode) -> t -> t val pp : out_channel -> t -> unit end diff --git a/lib/miniFun/Lexer.mll b/lib/miniFun/Lexer.mll index f9ebd40..030df9d 100644 --- a/lib/miniFun/Lexer.mll +++ b/lib/miniFun/Lexer.mll @@ -81,6 +81,7 @@ rule read = parse (lexbuf.Lexing.lex_curr_p.Lexing.pos_lnum) (lexbuf.Lexing.lex_curr_p.Lexing.pos_lnum) ))} + and comments level = parse | "*)" {if level = 0 then read lexbuf diff --git a/lib/miniFun/Parser.mly b/lib/miniFun/Parser.mly index bfdec51..022d618 100644 --- a/lib/miniFun/Parser.mly +++ b/lib/miniFun/Parser.mly @@ -24,7 +24,6 @@ %start prg (* associativity in order of precedence *) -/*%right rightlowest */ %left lowest %right TYPEFUNCTION %left COMMA @@ -35,7 +34,7 @@ %left CMP CMPLESS CMPLESSEQ CMPGREATER CMPGREATEREQ %left PLUS MINUS %left TIMES DIVISION MODULO -%left POWER +%right POWER %right BNOT RAND %left FIRST SECOND %left LAMBDA diff --git a/lib/miniImp/CfgImp.ml b/lib/miniImp/CfgImp.ml index 54f58c6..0428ea3 100644 --- a/lib/miniImp/CfgImp.ml +++ b/lib/miniImp/CfgImp.ml @@ -25,7 +25,6 @@ module SimpleStatements = struct | SimpleDivision of simpleArithmetic * simpleArithmetic | SimpleModulo of simpleArithmetic * simpleArithmetic | SimplePower of simpleArithmetic * simpleArithmetic - | SimplePowerMod of simpleArithmetic * simpleArithmetic * simpleArithmetic | SimpleRand of simpleArithmetic let pp (ppf: out_channel) (c: t) : unit = @@ -55,7 +54,6 @@ module SimpleStatements = struct | SimpleDivision (a1, a2) -> Printf.fprintf ppf "{%a / %a}" helper_a a1 helper_a a2 | SimpleModulo (a1, a2) -> Printf.fprintf ppf "{%a %% %a}" helper_a a1 helper_a a2 | SimplePower (a1, a2) -> Printf.fprintf ppf "{%a ^ %a}" helper_a a1 helper_a a2 - | SimplePowerMod (a1, a2, a3) -> Printf.fprintf ppf "{powmod %a %a %a}" helper_a a1 helper_a a2 helper_a a3 | SimpleRand (a) -> Printf.fprintf ppf "{rand %a}" helper_a a in helper_c ppf c @@ -69,18 +67,19 @@ module SSCfg = Cfg.Make(SimpleStatements) let rec convert_c (prevcfg: SSCfg.t) (prg: Types.c_exp) : SSCfg.t = let open SimpleStatements in match prg with - | Skip -> + | Skip -> (* we preserve the skips *) prevcfg |> SSCfg.addToLastNode SimpleSkip - | Assignment (x, a) -> + | Assignment (x, a) -> (* we simply add the assignment to the terminal node *) prevcfg |> SSCfg.addToLastNode (SimpleAssignment (x, convert_a a)) - | Sequence (c1, c2) -> + | Sequence (c1, c2) -> (* we first convert the first sequence, then the second + using the previous as prevcfg *) let cfg1 = convert_c prevcfg c1 in let cfg2 = convert_c cfg1 c2 in cfg2 - | If (b, c1, c2) -> + | If (b, c1, c2) -> (* constructs two branches with a two new nodes *) let convertedb = convert_b b in let cfg1 = convert_c SSCfg.empty c1 in let cfg2 = convert_c SSCfg.empty c2 in @@ -93,7 +92,7 @@ let rec convert_c (prevcfg: SSCfg.t) (prg: Types.c_exp) : SSCfg.t = NodeMap.add_to_list entrynode (SimpleGuard convertedb) |> NodeMap.add_to_list exitnode (SimpleSkip) } - | While (b, c) -> + | While (b, c) -> (* constructs a loop, needs three new nodes *) let convertedb = convert_b b in let cfg = convert_c SSCfg.empty c in let cfginitial = Option.get cfg.initial in @@ -124,7 +123,8 @@ let rec convert_c (prevcfg: SSCfg.t) (prg: Types.c_exp) : SSCfg.t = NodeMap.add_to_list exitnode (SimpleSkip) } |> SSCfg.concat prevcfg - | For (assignment, guard, increment, body) -> + | For (assignment, guard, increment, body) -> (* constructs a loop, needs + two new nodes *) let cfgassignment = convert_c SSCfg.empty assignment in let convertedguard = convert_b guard in let cfgincrement = convert_c SSCfg.empty increment in diff --git a/lib/miniImp/CfgImp.mli b/lib/miniImp/CfgImp.mli index dfcbfc6..5e933a1 100644 --- a/lib/miniImp/CfgImp.mli +++ b/lib/miniImp/CfgImp.mli @@ -24,7 +24,6 @@ module SimpleStatements : sig | SimpleDivision of simpleArithmetic * simpleArithmetic | SimpleModulo of simpleArithmetic * simpleArithmetic | SimplePower of simpleArithmetic * simpleArithmetic - | SimplePowerMod of simpleArithmetic * simpleArithmetic * simpleArithmetic | SimpleRand of simpleArithmetic val pp : out_channel -> t -> unit diff --git a/lib/miniImp/CfgRISC.ml b/lib/miniImp/CfgRISC.ml index f85fda2..2f86c3d 100644 --- a/lib/miniImp/CfgRISC.ml +++ b/lib/miniImp/CfgRISC.ml @@ -706,9 +706,6 @@ and c_ss_sa (convertedcode @ [BRegOp (Pow, partialresreg1, partialresreg2, register)], m) ) ) - | SimplePowerMod (_a1, _a2, _a3) -> ( - failwith "not implemented" - ) | SimpleRand (a) -> ( let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in diff --git a/lib/miniImp/Parser.mly b/lib/miniImp/Parser.mly index e52eb52..50adf99 100644 --- a/lib/miniImp/Parser.mly +++ b/lib/miniImp/Parser.mly @@ -32,7 +32,7 @@ %left DIVISION %left MODULO %left TIMES -%left POWER +%right POWER %left DO %% diff --git a/lib/miniImp/RISCSemantics.ml b/lib/miniImp/RISCSemantics.ml index db35686..cec0683 100644 --- a/lib/miniImp/RISCSemantics.ml +++ b/lib/miniImp/RISCSemantics.ml @@ -19,6 +19,8 @@ module RISCArchitecture = struct end let convert (prg: RISC.RISCAssembly.t) : RISC.RISCAssembly.risci list CodeMap.t = + (* takes as input a sequence of RISC commands and computes a map to the right + labels for easier execution *) let rec helper (prg: RISC.RISCAssembly.risci list) (current: RISC.RISCAssembly.risci list) @@ -98,7 +100,8 @@ let reduce_instructions (prg: RISCArchitecture.t) (lo: string list) : int = None -> prg (* should never happen *) | Some i -> if i + 1 < (List.length lo) then - helper prg (CodeMap.find (List.nth lo (i+1)) prg.code) (List.nth lo (i+1)) + helper + prg (CodeMap.find (List.nth lo (i+1)) prg.code) (List.nth lo (i+1)) else prg ) @@ -109,32 +112,45 @@ let reduce_instructions (prg: RISCArchitecture.t) (lo: string list) : int = (RegisterMap.find {index = r1.index} prg.registers) (RegisterMap.find {index = r2.index} prg.registers) in - helper {prg with registers = RegisterMap.add {index = r3.index} n prg.registers} tl current_label + helper { prg with + registers = RegisterMap.add {index = r3.index} n prg.registers } + tl current_label ) | BImmOp (biop, r1, i, r3) :: tl -> ( let n = (match_operator_i biop) (RegisterMap.find {index = r1.index} prg.registers) i in - helper {prg with registers = RegisterMap.add {index = r3.index} n prg.registers} tl current_label + helper { prg with + registers = RegisterMap.add {index = r3.index} n prg.registers } + tl current_label ) | URegOp (urop, r1, r3) :: tl -> ( match urop with | Copy -> ( let n = RegisterMap.find {index = r1.index} prg.registers in - helper {prg with registers = RegisterMap.add {index = r3.index} n prg.registers} tl current_label + helper { prg with + registers = + RegisterMap.add {index = r3.index} n prg.registers } + tl current_label ) | Not -> ( let n = Utility.int_not (RegisterMap.find {index = r1.index} prg.registers) in - helper {prg with registers = RegisterMap.add {index = r3.index} n prg.registers} tl current_label + helper { prg with + registers = + RegisterMap.add {index = r3.index} n prg.registers } + tl current_label ) | Rand -> ( let n = Random.int (RegisterMap.find {index = r1.index} prg.registers) in - helper {prg with registers = RegisterMap.add {index = r3.index} n prg.registers} tl current_label + helper { prg with + registers = + RegisterMap.add {index = r3.index} n prg.registers } + tl current_label ) ) | Load (r1, r3) :: tl -> ( @@ -143,12 +159,16 @@ let reduce_instructions (prg: RISCArchitecture.t) (lo: string list) : int = (RegisterMap.find {index = r1.index} prg.registers) prg.memory in - helper {prg with registers = RegisterMap.add {index = r3.index} n prg.registers} tl current_label + helper { prg with + registers = RegisterMap.add {index = r3.index} n prg.registers } + tl current_label ) | LoadI (i, r3) :: tl -> ( let n = i in - helper {prg with registers = RegisterMap.add {index = r3.index} n prg.registers} tl current_label + helper { prg with + registers = RegisterMap.add {index = r3.index} n prg.registers } + tl current_label ) | Store (r1, r3) :: tl -> ( let n = RegisterMap.find {index = r1.index} prg.registers in @@ -166,12 +186,17 @@ let reduce_instructions (prg: RISCArchitecture.t) (lo: string list) : int = ) | Label _ :: tl -> helper prg tl current_label in - RegisterMap.find - prg.outputreg - (helper prg (CodeMap.find "main" prg.code) "main").registers + match + RegisterMap.find_opt + prg.outputreg + (helper prg (CodeMap.find "main" prg.code) "main").registers + with + Some x -> x + | None -> failwith "Output register not found" let reduce (prg: RISC.RISCAssembly.t) : int = + (* takes assembly and execute it *) reduce_instructions {code = convert prg; registers = ( diff --git a/lib/miniImp/definedVariables.ml b/lib/miniImp/definedVariables.ml index 7694bb0..4ace70e 100644 --- a/lib/miniImp/definedVariables.ml +++ b/lib/miniImp/definedVariables.ml @@ -191,6 +191,7 @@ let update (t: DVCfg.t) (node: Cfg.Node.t) : DVCfg.internalnode = let compute_defined_variables (cfg: RISCCfg.t) : DVCfg.t = + (* creates the DVCfg structure and finds the fixed point *) let all_variables = List.fold_left (fun acc (_, code) -> Utility.unique_union acc (variables_all code)) @@ -208,6 +209,7 @@ let compute_defined_variables (cfg: RISCCfg.t) : DVCfg.t = let check_undefined_variables (dvcfg: DVCfg.t) : Variable.t list option = + (* returns all undefined variables previously computed *) let helper (node: Cfg.Node.t) (dvcfg: DVCfg.t) : Variable.t list option = let code = match Cfg.NodeMap.find_opt node dvcfg.t.content with None -> [] @@ -257,4 +259,5 @@ let check_undefined_variables (dvcfg: DVCfg.t) : Variable.t list option = let compute_cfg (dvcfg: DVCfg.t) : RISCCfg.t = + (* no change to the cfg so returned as is *) DVCfg.to_cfg dvcfg diff --git a/lib/miniImp/liveVariables.ml b/lib/miniImp/liveVariables.ml index a721d85..1987fee 100644 --- a/lib/miniImp/liveVariables.ml +++ b/lib/miniImp/liveVariables.ml @@ -238,14 +238,14 @@ let optimize_cfg (t: DVCfg.t) : DVCfg.t = (newassignments, newt) in - (* --------- *) + (* ------------------- *) let assignments = VariableMap.empty in let a, newt = Cfg.NodeSet.fold (* for each node we replace all the variables with the optimized ones *) - (fun node (ass, t) -> aux ass t node) + (fun node (assign, t) -> aux assign t node) t.t.nodes (assignments, t) in diff --git a/lib/miniImp/reduceRegisters.ml b/lib/miniImp/reduceRegisters.ml index 81d0edc..5ebdf41 100644 --- a/lib/miniImp/reduceRegisters.ml +++ b/lib/miniImp/reduceRegisters.ml @@ -18,7 +18,8 @@ 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 + let add_one = (fun x -> match x with None -> Some 1 | Some x -> Some (x + 1)) + in let helper (acc: int VariableMap.t) (instr: RISCCfg.elt) : int VariableMap.t = match instr with @@ -40,7 +41,9 @@ let variables_frequency (instr : RISCCfg.elt) : (Variable.t * int) list = helper VariableMap.empty instr |> VariableMap.to_list -let variables_all_frequency (instructions : RISCCfg.elt list) : (Variable.t * int) list = +(* computes syntactic frequency of all variables in the code *) +let variables_all_frequency (instructions : RISCCfg.elt list) + : (Variable.t * int) list = List.fold_left ( fun (acc: int VariableMap.t) (instr: RISCCfg.elt) -> VariableMap.union @@ -50,16 +53,20 @@ let variables_all_frequency (instructions : RISCCfg.elt list) : (Variable.t * in let reduceregisters (n: int) (cfg: RISCCfg.t) : RISCCfg.t = - (if n < 4 then (failwith "ReduceRegisters: number of registers too small") else ()); + (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) -> - Utility.unique_union_assoc (fun _n x y -> x + y) acc (variables_all_frequency code)) + Utility.unique_union_assoc + (fun _n x y -> x + y) acc (variables_all_frequency code)) [] (Cfg.NodeMap.to_list cfg.content) in + (* add one to in and out variables *) let all_variables = match cfg.inputOutputVar with | None -> all_variables @@ -80,6 +87,8 @@ let reduceregisters (n: int) (cfg: RISCCfg.t) : RISCCfg.t = ) in + (* replace each operation with a list of operations that have the new + registers or load from memory *) let replaceregisters (remappedregisters: Variable.t VariableMap.t) (memorymap: int VariableMap.t) @@ -143,7 +152,10 @@ let reduceregisters (n: int) (cfg: RISCCfg.t) : RISCCfg.t = BRegOp (brop, tmpreg1, tmpreg2, tmpreg2); LoadI (m3, tmpreg1); Store (tmpreg2, tmpreg1)] - | _ -> [BRegOp (brop, {index = r1.index}, {index = r2.index}, {index = r3.index})] + | _ -> [BRegOp (brop, + {index = r1.index}, + {index = r2.index}, + {index = r3.index})] ) | BImmOp (biop, r1, i, r3) -> ( match ( VariableMap.find_opt r1.index remappedregisters, @@ -237,8 +249,8 @@ let reduceregisters (n: int) (cfg: RISCCfg.t) : RISCCfg.t = " registers have no binding.") ) | Store (r1, r3) -> ( - (* we want to maybe store an address in memory (very confusing, don't - think can happen) *) + (* 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, @@ -269,6 +281,8 @@ let reduceregisters (n: int) (cfg: RISCCfg.t) : RISCCfg.t = in + (* partition the variables into two sets, most frequent and least frequent + then apply the transformation *) 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 = @@ -298,8 +312,13 @@ let reduceregisters (n: int) (cfg: RISCCfg.t) : RISCCfg.t = let newcfg = { cfg with content = Cfg.NodeMap.map - (fun x -> replaceregisters most_frequent_mapping least_frequent_mapping ["1"; "2"] x) - cfg.content} + ( fun x -> + replaceregisters + most_frequent_mapping + least_frequent_mapping + ["1"; "2"] + x + ) cfg.content} in match newcfg.inputOutputVar with @@ -417,7 +436,6 @@ let reduceregisters (n: int) (cfg: RISCCfg.t) : RISCCfg.t = ) in - ( if List.length all_variables <= n then cfg else aux cfg all_variables ) diff --git a/lib/miniImp/replacePowerMod.ml b/lib/miniImp/replacePowerMod.ml index e2016ba..030fe3e 100644 --- a/lib/miniImp/replacePowerMod.ml +++ b/lib/miniImp/replacePowerMod.ml @@ -1,10 +1,8 @@ let rewrite_instructions (prg: Types.p_exp) : Types.p_exp = (* function takes a program and replaces all occurrences of powermod with simpler instructions *) - let i, o, prg = ( - match prg with - | Main (i, o, exp) -> i, o, exp - ) in + + let Main (i, o, prg) = prg in let rec contains_rewrite (prg: Types.c_exp) : Types.a_exp option = (* if the ast contains powermod anywhere returns the powermod, otherwise From d78e8bfbe08c558c8f3ef3478c3374a48fd73d1b Mon Sep 17 00:00:00 2001 From: elvis Date: Thu, 16 Jan 2025 23:48:23 +0100 Subject: [PATCH 18/29] Fixing live analysis --- lib/miniImp/CfgRISC.ml | 24 +++++-- lib/miniImp/liveVariables.ml | 118 +++++++++++++++++++++++++----- report/document.pdf | Bin 200986 -> 200098 bytes report/document.tex | 2 +- report/report.tex | 7 +- test/dune | 4 ++ test/testingAnalysis.expected | 8 +++ test/testingAnalysis.ml | 132 ++++++++++++++++++++++++++++++++++ test/testingImpParser.ml | 1 + 9 files changed, 269 insertions(+), 27 deletions(-) create mode 100644 test/testingAnalysis.expected create mode 100644 test/testingAnalysis.ml diff --git a/lib/miniImp/CfgRISC.ml b/lib/miniImp/CfgRISC.ml index 2f86c3d..4494deb 100644 --- a/lib/miniImp/CfgRISC.ml +++ b/lib/miniImp/CfgRISC.ml @@ -755,10 +755,15 @@ let convert (prg: CfgImp.SSCfg.t) : RISCCfg.t = } -> let initial_bindings = match inputOutputVar with - | Some (i, o) -> - RegisterMap.empty |> - RegisterMap.set_register i {index = "in"} |> - RegisterMap.set_register o {index = "out"} + | Some (i, o) -> ( + if i = o then + RegisterMap.empty |> + RegisterMap.set_register i {index = "in"} + else + RegisterMap.empty |> + RegisterMap.set_register i {index = "in"} |> + RegisterMap.set_register o {index = "out"} + ) | None -> RegisterMap.empty in @@ -767,7 +772,16 @@ let convert (prg: CfgImp.SSCfg.t) : RISCCfg.t = edges = edges; reverseEdges = reverseEdges; inputVal = inputVal; - inputOutputVar = Some ("in", "out"); + inputOutputVar = ( + match inputOutputVar with + | Some (i, o) -> ( + if i = o then + Some ("in", "in") + else + Some ("in", "out") + ) + | None -> Some ("in", "out") + ); initial = initial; terminal = terminal; content = helper content initial_bindings; diff --git a/lib/miniImp/liveVariables.ml b/lib/miniImp/liveVariables.ml index 1987fee..1593592 100644 --- a/lib/miniImp/liveVariables.ml +++ b/lib/miniImp/liveVariables.ml @@ -18,7 +18,9 @@ module DVCfg = Dataflow.Make (CfgRISC.RISCSimpleStatements) (Variable) module DVCeltSet = Set.Make(Variable) -let variables_used (instr : DVCfg.elt) : DVCfg.internal list = +let variables_used (instr : DVCfg.elt) + : DVCfg.internal list = + let helper (acc: DVCeltSet.t) (instr: DVCfg.elt) = match instr with | Nop @@ -51,6 +53,33 @@ let variables_defined (instructions : DVCfg.elt) : DVCfg.internal list = helper DVCeltSet.empty instructions |> DVCeltSet.to_list +let variables (instruction : DVCfg.elt) : DVCfg.internal list = + let helper (acc: DVCeltSet.t) (instr: DVCfg.elt) = + match instr with + | Nop -> acc + | Store (r1, r2) -> + DVCeltSet.add r1.index acc |> + DVCeltSet.add r2.index + | BRegOp (_, r1, r2, r3) -> + DVCeltSet.add r1.index acc |> + DVCeltSet.add r2.index |> + DVCeltSet.add r3.index + | BImmOp (_, r1, _, r3) + | URegOp (_, r1, r3) + | Load (r1, r3) -> + DVCeltSet.add r1.index acc |> + DVCeltSet.add r3.index + | LoadI (_, r3) -> + DVCeltSet.add r3.index acc + in + + helper DVCeltSet.empty instruction |> DVCeltSet.to_list + +let variables_all (instructions : DVCfg.elt list) : DVCfg.internal list = + List.fold_left (fun (acc: DVCeltSet.t) (instr: DVCfg.elt) -> + DVCeltSet.union acc (variables instr |> DVCeltSet.of_list) + ) DVCeltSet.empty instructions |> DVCeltSet.to_list + (* init function, assign the bottom to everything *) let init : (DVCfg.elt list -> DVCfg.internalnode) = (fun l -> {internalin = []; @@ -157,7 +186,7 @@ module VariableMap = struct let start = "1" in first_empty next start m l - let get_mapping m l r = + let get_or_set_mapping m l r = match find_opt r m with | None -> ( let newr = first_empty_Variable m l in @@ -179,33 +208,33 @@ let optimize_cfg (t: DVCfg.t) : DVCfg.t = (a, Nop) ) | BRegOp (brop, r1, r2, r3) -> ( - let (newa, newr1) = VariableMap.get_mapping a vin r1.index in - let (newa, newr2) = VariableMap.get_mapping newa vin r2.index in - let (newa, newr3) = VariableMap.get_mapping newa vout r3.index in + let (newa, newr1) = VariableMap.get_or_set_mapping a vin r1.index in + let (newa, newr2) = VariableMap.get_or_set_mapping newa vin r2.index in + let (newa, newr3) = VariableMap.get_or_set_mapping newa vout r3.index in (newa, BRegOp (brop, {index = newr1}, {index = newr2}, {index = newr3})) ) | BImmOp (biop, r1, i, r3) -> ( - let (newa, newr1) = VariableMap.get_mapping a vin r1.index in - let (newa, newr3) = VariableMap.get_mapping newa vout r3.index in + let (newa, newr1) = VariableMap.get_or_set_mapping a vin r1.index in + let (newa, newr3) = VariableMap.get_or_set_mapping newa vout r3.index in (newa, BImmOp (biop, {index = newr1}, i, {index = newr3})) ) | URegOp (urop, r1, r3) -> ( - let (newa, newr1) = VariableMap.get_mapping a vin r1.index in - let (newa, newr3) = VariableMap.get_mapping newa vout r3.index in + let (newa, newr1) = VariableMap.get_or_set_mapping a vin r1.index in + let (newa, newr3) = VariableMap.get_or_set_mapping newa vout r3.index in (newa, URegOp (urop, {index = newr1}, {index = newr3})) ) | Load (r1, r3) -> ( - let (newa, newr1) = VariableMap.get_mapping a vin r1.index in - let (newa, newr3) = VariableMap.get_mapping newa vout r3.index in + let (newa, newr1) = VariableMap.get_or_set_mapping a vin r1.index in + let (newa, newr3) = VariableMap.get_or_set_mapping newa vout r3.index in (newa, Load ({index = newr1}, {index = newr3})) ) | LoadI (i, r3) -> ( - let (newa, newr3) = VariableMap.get_mapping a vout r3.index in + let (newa, newr3) = VariableMap.get_or_set_mapping a vout r3.index in (newa, LoadI (i, {index = newr3})) ) | Store (r1, r3) -> ( - let (newa, newr1) = VariableMap.get_mapping a vin r1.index in - let (newa, newr3) = VariableMap.get_mapping newa vout r3.index in + let (newa, newr1) = VariableMap.get_or_set_mapping a vin r1.index in + let (newa, newr3) = VariableMap.get_or_set_mapping newa vout r3.index in (newa, Store ({index = newr1}, {index = newr3})) ) in @@ -228,6 +257,7 @@ let optimize_cfg (t: DVCfg.t) : DVCfg.t = livevars.internalbetween code) in + let newcontent = Cfg.NodeMap.add node newcode @@ -240,14 +270,65 @@ let optimize_cfg (t: DVCfg.t) : DVCfg.t = (* ------------------- *) - let assignments = VariableMap.empty in + (* at least the input variable should be in the mapping *) + let assignments = + match t.t.inputOutputVar with + None -> VariableMap.empty + | Some (i, _o) -> ( + VariableMap.get_or_set_mapping VariableMap.empty [] i |> fst + ) + in - let a, newt = + let all_variables = List.fold_left + (fun acc (_, code) -> + Utility.unique_union acc (variables_all code)) + [] + (Cfg.NodeMap.to_list t.t.content) + in + + let mapping = + (* for each variable we get the union of all in and out that contains it + then we find a register such that it's not in conflict *) + List.fold_left (fun assignments v -> ( + (* union of all in and out such that v is in the set *) + let union : 'a list = + List.fold_left + (fun (acc: 'a list) (node, (x: DVCfg.internalnode)) -> + (* not interested in internalin or internalout since information + is mirrored into internalbetween *) + List.fold_left2 + (fun acc (i, o) code -> + (* we also consider the out set if we "use" v as a guard *) + match List.mem v i, + List.mem v o, + List.mem v (variables_defined code) with + | false, false, false -> acc + | true, false, false -> Utility.unique_union i acc + | false, false, true + | false, true, _ -> Utility.unique_union o acc + | true, false, true + | true, true, _ -> Utility.unique_union + (Utility.unique_union i o) acc + ) + acc + x.internalbetween + (Cfg.NodeMap.find_opt node t.t.content |> + Option.value ~default:[]) + ) + [] + (Cfg.NodeMap.to_list t.internalvar) + in + let assignments, _ = VariableMap.get_or_set_mapping assignments union v in + assignments + )) assignments all_variables + in + + let mapping, newt = Cfg.NodeSet.fold (* for each node we replace all the variables with the optimized ones *) (fun node (assign, t) -> aux assign t node) t.t.nodes - (assignments, t) + (mapping, t) in { newt with @@ -256,7 +337,8 @@ let optimize_cfg (t: DVCfg.t) : DVCfg.t = match newt.t.inputOutputVar with None -> None | Some (i, o) -> ( - match VariableMap.find_opt i a, VariableMap.find_opt o a with + match VariableMap.find_opt i mapping, + VariableMap.find_opt o mapping with | None, None -> Some (i, o) | Some i, None -> Some (i, o) | None, Some o -> Some (i, o) diff --git a/report/document.pdf b/report/document.pdf index 1974e01e979adffb502935aedb2611312141745b..ca50d9c265ae6015f5666c1698089c0a212426a5 100644 GIT binary patch delta 33447 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 FMhb{OLt+2` diff --git a/report/document.tex b/report/document.tex index fea0d5a..0889831 100644 --- a/report/document.tex +++ b/report/document.tex @@ -68,7 +68,7 @@ \usepackage{pgfornament} %% ornaments %% load last -\usepackage[hidelinks]{hyperref} %% links for table of contents, load last +\usepackage{hyperref} %% links for table of contents, load last \usepackage{bookmark} %% for better table of contents diff --git a/report/report.tex b/report/report.tex index d9b9fd2..e929a5f 100644 --- a/report/report.tex +++ b/report/report.tex @@ -23,8 +23,8 @@ \alt{} `\%' | `^' | `powmod' `(' `,' `,' `)' | `rand' `(' `)' \end{grammar} - Where \texttt{\%} is the modulo operator and \texttt{a a \% a} is the powermod operator; - the variables are all integers, \texttt{n} is an integer and \texttt{v} is a boolean litteral. + Where \texttt{\%} is the modulo operator and the powmod operator is equivalent to \texttt{a \^{} a \% a}; + the variables are all integers, \texttt{n} is an integer and \texttt{v} is a boolean literal. The additional arithmetic expressions' semantics are implemented in a similar manner as with the other. @@ -47,7 +47,8 @@ A program \texttt{t} is defined as follows: \begin{grammar} \(\defeq\) | | | `(' `,' `)' - \alt{} `fun' `:' `=>' | | | % chktex 38 + \alt{} `fun' `:' `=>' | % chktex 38 + \alt{} | \alt{} `powmod' `(' `,' `,' `)' \alt{} `rand' `(' `)' | \alt{} `if' `then' `else' diff --git a/test/dune b/test/dune index c173233..1b83215 100644 --- a/test/dune +++ b/test/dune @@ -10,6 +10,10 @@ (name testingRISC) (libraries miniImp)) +(test + (name testingAnalysis) + (libraries miniImp)) + (test (name testingFun) (libraries miniFun)) diff --git a/test/testingAnalysis.expected b/test/testingAnalysis.expected new file mode 100644 index 0000000..bc5b107 --- /dev/null +++ b/test/testingAnalysis.expected @@ -0,0 +1,8 @@ +Identity program: 1 +Factorial program: 3628800 +Hailstone sequence's lenght program: 351 +Sum multiples of 3 and 5 program: 35565945 +Rand program: true +Fibonacci program: 4807526976 +Miller-Rabin primality test program 1: 0 +Miller-Rabin primality test program 2: 1 diff --git a/test/testingAnalysis.ml b/test/testingAnalysis.ml new file mode 100644 index 0000000..48d1c7a --- /dev/null +++ b/test/testingAnalysis.ml @@ -0,0 +1,132 @@ +open MiniImp + +let compute x i = + Lexing.from_string x |> + Parser.prg Lexer.lex |> + CfgImp.convert_io i |> + CfgRISC.convert |> + LiveVariables.compute_live_variables |> + LiveVariables.optimize_cfg |> + LiveVariables.compute_cfg |> + ReduceRegisters.reduceregisters 4 |> + RISC.convert |> + RISCSemantics.reduce + +(* -------------------------------------------------------------------------- *) +(* Identity program *) +let program = + "def main with input a output b as b := a" +;; + +Printf.printf "Identity program: "; +Printf.printf "%d\n" (compute program 1) +;; + +(* -------------------------------------------------------------------------- *) +(* Factorial program *) +let program = +"def main with input a output b as + b := 1; + for (i := 1, i <= a, i := i + 1) do + b := b * i; +" +;; + +Printf.printf "Factorial program: "; +Printf.printf "%d\n" (compute program 10) + +(* -------------------------------------------------------------------------- *) +(* Hailstone sequence's lenght program *) +let program = +"def main with input a output b as + b := 1; + while not a == 1 do ( + b := b + 1; + if ((a % 2) == 1) then a := 3 * a + 1 else a := a / 2 + ) +" +;; + +Printf.printf "Hailstone sequence's lenght program: "; +Printf.printf "%d\n" (compute program 77031) + +(* -------------------------------------------------------------------------- *) +(* Sum multiples of 3 and 5 program *) +let program = +"def main with input a output b as + b := 0; + for (i := 0, i <= a, i := i+1) do + if (i % 3 == 0 || i % 5 == 0) then b := b + i; + else skip; +" +;; + +Printf.printf "Sum multiples of 3 and 5 program: "; +Printf.printf "%d\n" (compute program 12345) + +(* -------------------------------------------------------------------------- *) +(* Rand program *) +let program = + "def main with input a output b as b := rand(a)" +;; + +Printf.printf "Rand program: "; +Printf.printf "%b\n" ((compute program 10) < 10) + +(* -------------------------------------------------------------------------- *) +(* Fibonacci program *) +let program = +"def main with input n output fnext as + fnow := 0; + fnext := 1; + while (n > 1) do ( + tmp := fnow + fnext; + fnow := fnext; + fnext := tmp; + n := n - 1; + ) +" +;; + +Printf.printf "Fibonacci program: "; +Printf.printf "%d\n" (compute program 48) + +(* -------------------------------------------------------------------------- *) +(* Miller-Rabin primality test program *) +let program = +"def main with input n output result as + if (n % 2) == 0 then result := 1 + else ( + + result := 0; + s := 0; + while (0 == ((n - 1) / (2 ^ s)) % 2) do ( + s := s + 1 + ); + d := ((n - 1) / 2 ^ s); + for (i := 20, i > 0, i := i - 1) do ( + a := rand(n - 4) + 2; + x := powmod(a, d, n); + y := 0; + for (j := 0, j < s, j := j+1) do ( + y := powmod(x, 2, n); + if (y == 1 && (not x == 1) && (not x == n - 1)) then + result := 1; + else + skip; + x := y; + ); + if not y == 1 then result := 1; + else skip; + ) + ) +" +;; + +(* should return 0 because prime *) +Printf.printf "Miller-Rabin primality test program 1: "; +Printf.printf "%d\n" (compute program 179424673); + +(* should return 1 because not prime *) +Printf.printf "Miller-Rabin primality test program 2: "; +Printf.printf "%d\n" (compute program 179424675); diff --git a/test/testingImpParser.ml b/test/testingImpParser.ml index dcacecd..7aad165 100644 --- a/test/testingImpParser.ml +++ b/test/testingImpParser.ml @@ -134,6 +134,7 @@ let program = for (i := 20, i > 0, i := i - 1) do ( a := rand(n - 4) + 2; x := powmod(a, d, n); + y := 0; for (j := 0, j < s, j := j+1) do ( y := powmod(x, 2, n); if (y == 1 && (not x == 1) && (not x == n - 1)) then From 4ef52dfd6a480a68c3e6690955dcc1f681db6d23 Mon Sep 17 00:00:00 2001 From: elvis Date: Fri, 17 Jan 2025 00:46:51 +0100 Subject: [PATCH 19/29] Minor modifications --- bin/miniFunInterpreter.ml | 2 ++ bin/miniImpInterpreter.ml | 2 ++ bin/miniImpInterpreterReg.ml | 2 ++ lib/miniImp/liveVariables.ml | 3 ++- report/document.pdf | Bin 200098 -> 223964 bytes report/document.tex | 14 +++++++++++- report/report.tex | 42 ++++++++++++++++++++--------------- 7 files changed, 45 insertions(+), 20 deletions(-) diff --git a/bin/miniFunInterpreter.ml b/bin/miniFunInterpreter.ml index ea20218..e932fd8 100644 --- a/bin/miniFunInterpreter.ml +++ b/bin/miniFunInterpreter.ml @@ -99,3 +99,5 @@ let () = | Some o -> o in interpret_file inx inputval outx; + + Out_channel.close outx diff --git a/bin/miniImpInterpreter.ml b/bin/miniImpInterpreter.ml index 0f938b7..fcef31b 100644 --- a/bin/miniImpInterpreter.ml +++ b/bin/miniImpInterpreter.ml @@ -90,3 +90,5 @@ let () = | Some o -> o in interpret_file inx inputval outx; + + Out_channel.close outx diff --git a/bin/miniImpInterpreterReg.ml b/bin/miniImpInterpreterReg.ml index 9bbb5c3..c270969 100644 --- a/bin/miniImpInterpreterReg.ml +++ b/bin/miniImpInterpreterReg.ml @@ -140,3 +140,5 @@ let () = in interpret_file inx registers outx; + + Out_channel.close outx diff --git a/lib/miniImp/liveVariables.ml b/lib/miniImp/liveVariables.ml index 1593592..a8b9ab6 100644 --- a/lib/miniImp/liveVariables.ml +++ b/lib/miniImp/liveVariables.ml @@ -318,7 +318,8 @@ let optimize_cfg (t: DVCfg.t) : DVCfg.t = [] (Cfg.NodeMap.to_list t.internalvar) in - let assignments, _ = VariableMap.get_or_set_mapping assignments union v in + let assignments, _ = + VariableMap.get_or_set_mapping assignments union v in assignments )) assignments all_variables in diff --git a/report/document.pdf b/report/document.pdf index ca50d9c265ae6015f5666c1698089c0a212426a5..af0dcfd4f1aee818844496ad0393d4a2bce2b490 100644 GIT binary patch delta 86490 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 X&zE@(0~`%9GBP(bI0_{tMNdWwbkIl& delta 62730 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 !ZH=Y&JHCom9#MO_^ zBKe`;iDB>=69bR7%=`ut=nGIJsV$$%;LY%CdF=A`crL4)SXJp5I=Ret4S4-o*X2vkc@vQx4h{>}C;$`ehgPr*I$E48 z_Xjx%GK0LtE0bMm)*?dTX@3i+9_sHre9SbF(wR~@S&OhRU_

5d%$0_k&V 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 X|BjPLP diff --git a/report/document.tex b/report/document.tex index 0889831..4bbd1b5 100644 --- a/report/document.tex +++ b/report/document.tex @@ -178,7 +178,17 @@ %% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - %% -\title{Document} +\title{ + \normalfont\normalsize + \textsc{University of Pisa}\\ + \vspace{25pt} + \rule{\linewidth}{0.5pt}\\ + \vspace{20pt} + {\huge Languages, Compilers and Interpreters Project}\\ + \vspace{12pt} + \rule{\linewidth}{2pt}\\ + \vspace{12pt} +} \author{ Elvis Rossi } @@ -188,6 +198,8 @@ \begin{document} +\maketitle + \input{report} \end{document} diff --git a/report/report.tex b/report/report.tex index e929a5f..29a147a 100644 --- a/report/report.tex +++ b/report/report.tex @@ -28,7 +28,7 @@ The additional arithmetic expressions' semantics are implemented in a similar manner as with the other. - The sematic of \texttt{for} is as follows: + The semantic of \texttt{for} is as follows: \begin{center} \inference[\texttt{for}] @@ -41,7 +41,7 @@ \begin{subsection}{MiniFun Semantics} The semantic of the MiniFun language is implemented in the \href{../lib/miniFun/Semantics.mli}{Semantics.mli} and \href{../lib/miniFun/Semantics.ml}{Semantics.ml} file. - A \texttt{reduce} function is provided that transforms the AST into the avluated value or an error. + A \texttt{reduce} function is provided that transforms the AST into the evaluated value or an error. The AST type is defined in \href{../lib/miniFun/Types.mli}{Types.mli} and in \href{../lib/miniFun/Types.ml}{Types.ml}. A program \texttt{t} is defined as follows: @@ -67,7 +67,7 @@ \begin{section}{Types for MiniFun} - A type \(\tau\) is defined as either {\it int}, {\it bool}, a touple or a function. + A type \(\tau\) is defined as either {\it int}, {\it bool}, a tuple or a function. \begin{equation*} \tau \defeq {\it int\/}\ \vert\ {\it bool\/}\ \vert\ (\tau,\tau)\ \vert\ \tau \to \tau @@ -98,13 +98,13 @@ \begin{center} \inference[\texttt{Fun}] {\Gamma[x \mapsto \tau] \vdash t \triangleright \tau'} % chktex 1 - {\Gamma \vdash \texttt{fun} x \texttt{:} \tau \to \tau' \texttt{=>} t \triangleright \tau \to \tau'} % chktex 1 + {\Gamma \vdash \texttt{fun } x \texttt{:} \tau \to \tau' \texttt{ => } t \triangleright \tau \to \tau'} % chktex 1 \end{center} \begin{center} \inference[\texttt{FunRec}] {\Gamma[f \mapsto \tau \to \tau'; x \mapsto \tau] \vdash t_1 \triangleright \tau' & \Gamma[f \mapsto \tau \to \tau'] \vdash t_2 \triangleright \tau''} % chktex 1 - {\Gamma \vdash \texttt{let rec} f x \texttt{:} \tau \to \tau' \texttt{ = } t_1 \texttt{ in } t_2 \triangleright \tau''} % chktex 1 + {\Gamma \vdash \texttt{let rec } f\ x \texttt{:} \tau \to \tau' \texttt{ = } t_1 \texttt{ in } t_2 \triangleright \tau''} % chktex 1 \end{center} In the files \href{../lib/miniFun/TypeChecker.mli}{TypeChecker.mli} and \href{../lib/miniFun/TypeChecker.ml}{TypeChecker.ml} there is the implementation of the deduction rules, but returns either the valid type of the expression or an error instead of simply the required option type of the valid type. @@ -112,7 +112,8 @@ \begin{section}{Parsing} \begin{subsection}{MiniImp} - Operators listed in order of precedence from highest to lowest: + As seen in class, lexing and parsing is done with ocamellex and menhir in the files \href{../lib/miniImp/Lexer.mli}{Lexer.mli} and \href{../lib/miniImp/Parser.ml}{Parser.ml}. + Operators listed in order of precedence from highest to lowest: \begin{center} \begin{tblr}{colspec={|c|c|}, rowspec={|Q|QQQQQQQ|}} @@ -127,14 +128,15 @@ \end{tblr} \end{center} - The expressions \(c_1 \texttt{;} c_2\) and \(c_1 \texttt{;}\) are both recognized and give respectively \(\texttt{SEQUENCE(} c_1 \texttt{,} c_2 \texttt{)}\) % chktex 9 - and \(c_1\), such that semicolons can be placed always at the end of a command. + The expressions \(c_1 \texttt{;} c_2\) and \(c_3 \texttt{;}\) are both recognized and give respectively \(\texttt{SEQUENCE(} c_1 \texttt{,} c_2 \texttt{)}\) % chktex 9 + and \(c_3\), such that semicolons can be placed always at the end of a command. Integers with a preceding minus sign can be interpreted as the opposite integer, with obviously lower precedence than the binary operator minus. \end{subsection} \begin{subsection}{MiniFun} + As seen in class, lexing and parsing is done with ocamellex and menhir in the files \href{../lib/miniFun/Lexer.mli}{Lexer.mli} and \href{../lib/miniFun/Parser.ml}{Parser.ml}. A decision was made to interpret \texttt{\textbackslash}, \texttt{lambda} and \texttt{fun} all as the start of the definition of a function just for ease of typing. They are associated to the same token \texttt{LAMBDA}. Operators listed in order of precedence from highest to lowest: @@ -157,7 +159,7 @@ \end{tblr} \end{center} - Tuples require parentesis in their definition, but the tuple type does not since there is no ambiguity. The symbol \texttt{->} that defines the function type is right associative and has lowest precedence. + Tuples require parenthesis in their definition, but the tuple type does not since there is no ambiguity. The symbol \texttt{->} that defines the function type is right associative and has lowest precedence. \end{subsection} \begin{subsection}{Interpreters} @@ -175,11 +177,11 @@ The control flow structure is composed of a flag to know if it is empty or contains nodes and the set of all contained nodes. Since each node can only have at maximum 2 nodes as next nodes, the data structure contains a map from each node to a tuple of the two nodes or to a node. - The structure also contains the back edges of each node implemented as a map from each node to a list of nodes, the input value, the variables that are the input and ouput, the initial node and the terminal node. + The structure also contains the back edges of each node implemented as a map from each node to a list of nodes, the input value, the variables that are the input and output, the initial node and the terminal node. Finally there is a map from each node to a list of generic elements that in our case are simple statements. \begin{subsection}{MiniImp Simple Statement} - MiniImp Simple Statements \(t\) is defined as follows: + MiniImp Simple Statements \(t\) is defined in the files \href{../lib/miniImp/CfgImp.ml}{CfgImp.ml} and \href{../lib/miniImp/CfgImp.mli}{CfgImp.mli} as follows: \begin{grammar} \(\defeq\) skip | `:=' | `{?}' @@ -191,9 +193,9 @@ \alt{} `mod' | `^' | `rand' \end{grammar} - The implemented cfg is neither minimal nor maximal, but can be either or both for some programs. In particular each node as associated a list of statements and sequence of statements in the AST is put, if possible, in the same node. + The implemented CFG is neither minimal nor maximal, but can be either or both for some programs. In particular each node as associated a list of statements and sequence of statements in the AST is put, if possible, in the same node. - \texttt{?} is only allowed as the last element of the list of statemets associated with a node and a node has associated a \texttt{?} if and only if they have two next nodes. + \texttt{?} is only allowed as the last element of the list of statements associated with a node and a node has associated a \texttt{?} if and only if they have two next nodes. The for loop is translated as: @@ -339,7 +341,7 @@ while exp > 0 do ( \end{subsection} \begin{subsection}{MiniRISC} - The MiniRISC CFG is finally tranlated into MiniRISC intermediate code by the function \texttt{convert} in the files \href{../lib/miniImp/RISC.ml}{RISC.ml} and \href{../lib/miniImp/RISC.mli}{RISC.mli}. + The MiniRISC CFG is finally translated into MiniRISC intermediate code by the function \texttt{convert} in the files \href{../lib/miniImp/RISC.ml}{RISC.ml} and \href{../lib/miniImp/RISC.mli}{RISC.mli}. The grammar of MiniRISC is analogous to the one for \hyperref[grammar:MiniRISC]{MiniRISC Simple Statements}: \begin{grammar} @@ -373,7 +375,7 @@ while exp > 0 do ( \end{section} \begin{section}{Dataflow Analysis} - A refined CFG structure used for analysis is defined in \href{../lib/analysis/Dataflow.ml}{Dataflow.ml} and \href{../lib/analysis/Dataflow.mli}{Dataflow.mli}. The CFG is supplemented with a map from each node to the support structure that stores the list of defined variables or live variables. Since the CFG is not minimal, there is also a list for each simple statement. A fixed point function then applies the input fuction until the map does not change. Simple structural equality is not appropriate since order in the lists should not matter; an internal function for equality is used. + A refined CFG structure used for analysis is defined in \href{../lib/analysis/Dataflow.ml}{Dataflow.ml} and \href{../lib/analysis/Dataflow.mli}{Dataflow.mli}. The CFG is supplemented with a map from each node to the support structure that stores the list of defined variables or live variables. Since the CFG is not minimal, there is also a list for each simple statement. A fixed point function then applies the input function until the map does not change. Simple structural equality is not appropriate since order in the lists should not matter; an internal function for equality is used. \begin{subsection}{Defined Variables} In the files \href{../lib/miniImp/definedVariables.ml}{definedVariables.ml} and \href{../lib/miniImp/definedVariables.mli}{definedVariables.mli} three functions are defined: \texttt{compute_defined_variables}, \texttt{compute_cfg} and \texttt{check_undefined_variables}. @@ -382,7 +384,7 @@ while exp > 0 do ( \texttt{compute_cfg} returns the CFG from the analysis data structure; in the case of defined variables analysis the CFG returned is the same as the one in input of \texttt{compute_defined_variables}. \texttt{check_undefined_variables} returns all variables that might be undefined at time of use. - Since the greatest fixed point is computed, first all variables are retrived from all code, then assigned to each input and ouput list of variables for each line of code. + Since the greatest fixed point is computed, first all variables are retrieved from all code, then assigned to each input and output list of variables for each line of code. Since it is an approximation some behaviour might not be intuitive. For example: @@ -406,7 +408,7 @@ output := y; \end{section} \begin{section}{Target Code Generation} - In the files \href{../lib/miniImp/reduceRegisters.ml}{reduceRegisters.ml} and \href{../lib/miniImp/reduceRegisters.mli}{reduceRegisters.mli} the function \texttt{reduceregisters} reduces the number of used registers by counting the syntactic occurrence of each variable and partitioning the set keeping the most used as registers. All registers are either renamed or put into memory. It is allowed for the input or output registers to be put in memory, in the latter case some code is added at the end of the program to retrive the value and put into a register (register \texttt{2}). + In the files \href{../lib/miniImp/reduceRegisters.ml}{reduceRegisters.ml} and \href{../lib/miniImp/reduceRegisters.mli}{reduceRegisters.mli} the function \texttt{reduceregisters} reduces the number of used registers by counting the syntactic occurrence of each variable and partitioning the set keeping the most used as registers. All registers are either renamed or put into memory. It is allowed for the input or output registers to be put in memory, in the latter case some code is added at the end of the program to retrieve the value and put into a register (in particular register \texttt{2}). \begin{subsection}{MiniImp to MiniRISC compiler} The file \href{../bin/miniImpInterpreterReg.ml}{miniImpInterpreterReg.ml} compiles from MiniImp to MiniRISC or execute the MiniRISC code. It uses the package \href{https://opam.ocaml.org/packages/clap/}{Clap} to parse command line arguments and generate help pages. @@ -418,7 +420,11 @@ output := y; \begin{section}{Running the code} The project uses the following packages: \href{https://dune.build/}{Dune}, \href{https://gallium.inria.fr/~fpottier/menhir/}{Menhir} and \href{https://github.com/rbardou/clap}{Clap}. They can be installed via \href{https://opam.ocaml.org/}{Opam} with the command \texttt{opam install dune menhir clap}. To compile the project simply run \texttt{dune build}. To run the test run \texttt{dune runtest}. - In order to execute one of the interpreters run \texttt{dune exec {-}{-} }. To see a list of all options run \texttt{dune exec {-}{-} -h}. A binary version of the executables can also be found in \href{./_build/default/bin/}{./_build/default/bin/}. + In order to execute one of the interpreters run \texttt{dune exec {-}{-} }. + + For example: \texttt{dune exec miniImpInterpreterReg {-}{-} -i bin/sum.miniimp -r 4 -v 100 -e}. + + To see a list of all options run \texttt{dune exec {-}{-} -h}. A binary version of the executables can also be found in the build directory: \href{./_build/default/bin/}{./\_build/default/bin/}. \end{section} %%% Local Variables: %%% TeX-command-extra-options: "-shell-escape" From 404a720aaed324b931c7ce69419145c2110d03a1 Mon Sep 17 00:00:00 2001 From: elvis Date: Fri, 17 Jan 2025 00:48:43 +0100 Subject: [PATCH 20/29] Compiling pdf --- report/document.pdf | Bin 223964 -> 223964 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/report/document.pdf b/report/document.pdf index af0dcfd4f1aee818844496ad0393d4a2bce2b490..7833181459cffef3ddff06313aa0f815f59b4719 100644 GIT binary patch delta 108 zcmca}mG{n7-i8*&7N#xCWueR#h6d9sLzxvojO}wmnePiYyI2@HI~ka`IT|^dxS3j5 gy11Dd8(BIzS(;h6Ihz Date: Fri, 17 Jan 2025 00:53:18 +0100 Subject: [PATCH 21/29] Updating README --- README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/README.md b/README.md index 7a28fb0..3eed96f 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,21 @@ # LCI +[Report](report/document.pdf) is located in the [report/](report/) folder + +## To install prerequisites + +``` +opam install dune menhir clap +``` + +## To run the tests + +``` +dune runtest +``` + +## To run an executable + +``` +dune exec miniImpInterpreterReg {-}{-} -i bin/sum.miniimp -r 4 -v 100 -e +``` From 4a9f62b42dad4f5d166aadc83e87d6ae70471ca0 Mon Sep 17 00:00:00 2001 From: elvis Date: Fri, 17 Jan 2025 00:54:00 +0100 Subject: [PATCH 22/29] Updating README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3eed96f..77175f4 100644 --- a/README.md +++ b/README.md @@ -17,5 +17,5 @@ dune runtest ## To run an executable ``` -dune exec miniImpInterpreterReg {-}{-} -i bin/sum.miniimp -r 4 -v 100 -e +dune exec miniImpInterpreterReg -- -i bin/sum.miniimp -r 4 -v 100 -e ``` From ca39024ee34528dc746ba343d368050a49d61606 Mon Sep 17 00:00:00 2001 From: elvis Date: Sun, 26 Jan 2025 21:47:05 +0100 Subject: [PATCH 23/29] Better linting --- bin/miniImpInterpreterReg.ml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/bin/miniImpInterpreterReg.ml b/bin/miniImpInterpreterReg.ml index c270969..a8b1a19 100644 --- a/bin/miniImpInterpreterReg.ml +++ b/bin/miniImpInterpreterReg.ml @@ -37,7 +37,8 @@ let () = in let checkundefined = Clap.flag - ~description: "Optional flag for disabling the check for undefined variables." + ~description: "Optional flag for disabling the check for undefined \ + variables." ~section: values ~unset_long: "undefined" ~unset_short: 'u' @@ -45,7 +46,8 @@ let () = in let optimizereg = Clap.flag - ~description: "Optional flag for disabling optimizing registers with liveness analysis." + ~description: "Optional flag for disabling optimizing registers with \ + liveness analysis." ~section: values ~unset_long: "liveness" ~unset_short: 'l' @@ -128,9 +130,9 @@ let () = RISC.convert in - if not evalb - then Printf.fprintf outch "%a\n" RISC.RISCAssembly.pp return_value - else Printf.fprintf outch "%d\n" (RISCSemantics.reduce return_value) + if evalb + then Printf.fprintf outch "%d\n" (RISCSemantics.reduce return_value) + else Printf.fprintf outch "%a\n" RISC.RISCAssembly.pp return_value in let inx = In_channel.open_text input in From f175a6089d06f04f4f1051cbb1abf52bb9706e80 Mon Sep 17 00:00:00 2001 From: elvis Date: Sun, 26 Jan 2025 22:07:53 +0100 Subject: [PATCH 24/29] Removing lines with more than 80 characters in tests --- test/testingFun.ml | 72 ++++++++--- test/testingImp.ml | 33 +++-- test/testingTypeFun.ml | 227 +++++++++++++++++++++++++---------- test/testingTypeFunParser.ml | 5 +- 4 files changed, 243 insertions(+), 94 deletions(-) diff --git a/test/testingFun.ml b/test/testingFun.ml index 9977d44..a02ec60 100644 --- a/test/testingFun.ml +++ b/test/testingFun.ml @@ -35,7 +35,8 @@ let program = LetIn ("f", (Function ("x", - FunctionType (IntegerType, FunctionType (IntegerType, IntegerType)), + FunctionType + (IntegerType, FunctionType (IntegerType, IntegerType)), (Function ("y", FunctionType (IntegerType, IntegerType), Plus (Variable "x", Variable "y")) ) @@ -73,10 +74,13 @@ let program = ("f", (Function ( "z", - FunctionType (FunctionType (IntegerType, IntegerType), IntegerType), + FunctionType (FunctionType (IntegerType, IntegerType), + FunctionType (FunctionType (IntegerType, IntegerType), + FunctionType (IntegerType, IntegerType))), (Function ( "y", - FunctionType (FunctionType (IntegerType, IntegerType), IntegerType), + FunctionType (FunctionType (IntegerType, IntegerType), + FunctionType (IntegerType, IntegerType)), Function ( "x", FunctionType (IntegerType, IntegerType), @@ -91,10 +95,12 @@ let program = ( (Application (Variable "f", - Function ("x", FunctionType (IntegerType, IntegerType), Plus (Variable "x", Integer 1)) + Function ("x", FunctionType (IntegerType, IntegerType), + Plus (Variable "x", Integer 1)) ) ), - Function ("x", FunctionType (IntegerType, IntegerType), Minus (Variable "x", Integer 1)) + Function ("x", FunctionType (IntegerType, IntegerType), + Minus (Variable "x", Integer 1)) ) ) ) @@ -115,7 +121,13 @@ let program = ("f", "x", FunctionType (IntegerType, IntegerType), - (IfThenElse (CmpLess (Variable "x", Integer 2),Integer 1, Plus (Variable "x", Application (Variable "f", Minus (Variable "x", Integer 1))))), + (IfThenElse (CmpLess (Variable "x", Integer 2), + Integer 1, + Plus ( + Variable "x", + Application ( + Variable "f", + Minus (Variable "x", Integer 1))))), (Variable "f") ) ;; @@ -129,7 +141,9 @@ match reduce program 10 with let program = LetIn ("f", - (LetIn ("a", Integer 1, (Function ("y", FunctionType (IntegerType, IntegerType), Plus (Variable "y", Variable "a"))))), + (LetIn ("a", Integer 1, (Function ("y", + FunctionType (IntegerType, IntegerType), + Plus (Variable "y", Variable "a"))))), (LetIn ("a", Integer 2, Variable "f")) ) ;; @@ -145,7 +159,11 @@ let program = "f", "x", FunctionType (IntegerType, IntegerType), - (IfThenElse (CmpLessEq (Variable "x", Integer 0), Integer 1, Times (Variable "x", Application (Variable "f", Minus (Variable "x", Integer 1))))), + (IfThenElse (CmpLessEq (Variable "x", Integer 0), + Integer 1, + Times (Variable "x", + Application (Variable "f", + Minus (Variable "x", Integer 1))))), (Variable "f") ) ;; @@ -158,27 +176,38 @@ match reduce program 10 with (* Hailstone sequence's lenght program *) let program = - LetFun ( "collatz", "input", FunctionType (TupleType (IntegerType, IntegerType), IntegerType), ( IfThenElse (BNot (Cmp (First (Variable "input"), Integer 1)), - (IfThenElse (Cmp (Modulo (First (Variable "input"), Integer 2), Integer 0), + (IfThenElse (Cmp (Modulo (First (Variable "input"), + Integer 2), + Integer 0), Application (Variable "collatz", Tuple ( - Division (First (Variable "input"), Integer 2), - Plus (Integer 1, Second (Variable "input")))), + Division (First + (Variable "input"), + Integer 2), + Plus (Integer 1, Second + (Variable "input")))), Application (Variable "collatz", Tuple ( - Plus (Integer 1, Times (Integer 3, First (Variable "input"))), - Plus (Integer 1, Second (Variable "input")))))), + Plus + (Integer 1, + Times + (Integer 3, + First (Variable "input"))), + Plus ( + Integer 1, + Second (Variable "input")))))), (Second (Variable "input"))) ), (Function ("x", FunctionType (IntegerType, IntegerType), - Application (Variable "collatz", Tuple (Variable "x", Integer 1)))) + Application (Variable "collatz", + Tuple (Variable "x", Integer 1)))) ) ;; @@ -194,11 +223,15 @@ let program = "sum", "n", FunctionType (IntegerType, IntegerType), - (IfThenElse ((BOr (Cmp (Modulo (Variable "n", Integer 3), Integer 0), Cmp (Modulo (Variable "n", Integer 5), Integer 0))), - Plus (Variable "n", Application (Variable "sum", Minus (Variable "n", Integer 1))), + (IfThenElse ((BOr (Cmp (Modulo (Variable "n", Integer 3), Integer 0), + Cmp (Modulo (Variable "n", Integer 5), Integer 0))), + Plus (Variable "n", + Application (Variable "sum", + Minus (Variable "n", Integer 1))), (IfThenElse ((CmpLessEq (Variable "n", Integer 1)), (Integer 0), - (Application (Variable "sum", Minus (Variable "n", Integer 1)))) + (Application (Variable "sum", + Minus (Variable "n", Integer 1)))) )) ), (Variable "sum") @@ -231,7 +264,8 @@ let program = LetFun ( "fib", "i", - FunctionType (IntegerType, FunctionType (IntegerType, FunctionType (IntegerType, IntegerType))), + FunctionType (IntegerType, FunctionType + (IntegerType, FunctionType (IntegerType, IntegerType))), Function ( "a", FunctionType (IntegerType, FunctionType (IntegerType, IntegerType)), diff --git a/test/testingImp.ml b/test/testingImp.ml index d4fcdd5..820bc45 100644 --- a/test/testingImp.ml +++ b/test/testingImp.ml @@ -27,7 +27,8 @@ let program = (Sequence ( (Assignment ("x", (Integer 1))), (Assignment ("b", - (Plus ((Plus (Variable "a", Variable "x")), (Variable "y"))))) + (Plus ((Plus (Variable "a", Variable "x")), + (Variable "y"))))) ) ) ) @@ -83,7 +84,8 @@ let program = (Assignment ("b", (Plus (Variable "b", Integer 1)))), (If ( (BCmp (Modulo (Variable "a", Integer 2), Integer 1)), - (Assignment ("a", Plus (Times (Integer 3, Variable "a"), Integer 1))), + (Assignment ("a", Plus (Times (Integer 3, Variable "a"), + Integer 1))), (Assignment ("a", Division (Variable "a", Integer 2))) )) )) @@ -161,7 +163,8 @@ let program = (BCmpGreater (Variable "n", Integer 1)), (Sequence ( (Sequence ( - (Assignment ("tmp", Plus (Variable "fnow", Variable "fnext"))), + (Assignment ("tmp", Plus (Variable "fnow", + Variable "fnext"))), (Assignment ("fnow", Variable "fnext")) )), (Sequence ( @@ -205,15 +208,21 @@ let program = )) )), (Sequence ( - (Assignment ("d", Division (Minus (Variable "n", Integer 1), Power (Integer 2, Variable "s")))), + (Assignment ("d", Division (Minus (Variable "n", Integer 1), + Power (Integer 2, Variable "s")))), (For ( (Assignment ("i", Integer 20)), (BCmpGreater (Variable "i", Integer 0)), (Assignment ("i", Minus (Variable "i", Integer 1))), (Sequence ( Sequence ( - (Assignment ("a", Plus (Rand (Minus (Variable "n", Integer 4)), Integer 2))), - (Assignment ("x", PowerMod (Variable "a", Variable "d", Variable "n")))), + (Assignment + ("a", + Plus (Rand (Minus (Variable "n", Integer 4)), + Integer 2))), + (Assignment ("x", PowerMod (Variable "a", + Variable "d", + Variable "n")))), Sequence ( (For ( (Assignment ("j", Integer 0)), @@ -221,9 +230,17 @@ let program = (Assignment ("j", Plus (Variable "j", Integer 1))), (Sequence ( Sequence ( - (Assignment ("y", PowerMod (Variable "x", Integer 2, Variable "n"))), + (Assignment ("y", PowerMod (Variable "x", + Integer 2, + Variable "n"))), (If ( - (BAnd (BAnd (BCmp (Variable "y", Integer 1), BNot (BCmp (Variable "x", Integer 1))), BNot (BCmp (Variable "x", Minus (Variable "n", Integer 1))))), + (BAnd + (BAnd (BCmp (Variable "y", Integer 1), + BNot (BCmp (Variable "x", + Integer 1))), + BNot (BCmp (Variable "x", + Minus (Variable "n", + Integer 1))))), (Assignment ("result", Integer 1)), (Skip) ))), diff --git a/test/testingTypeFun.ml b/test/testingTypeFun.ml index 581d0e9..a44e3c9 100644 --- a/test/testingTypeFun.ml +++ b/test/testingTypeFun.ml @@ -13,8 +13,10 @@ let program = ;; match typecheck program with - Error (`AbsentAssignment _) -> Printf.printf "Error absent assignment program: error (success)\n" -| _ -> Printf.printf "Error absent assignment program: failed\n" +| Error (`AbsentAssignment _) -> + Printf.printf "Error absent assignment program: error (success)\n" +| _ -> + Printf.printf "Error absent assignment program: failed\n" (* -------------------------------------------------------------------------- *) (* Error wrong return type program *) @@ -28,8 +30,10 @@ let program = ;; match typecheck program with - Error (`WrongTypeSpecification _) -> Printf.printf "Error wrong return type program: error (success)\n" -| _ -> Printf.printf "Error wrong return type program: failed\n" +| Error (`WrongTypeSpecification _) -> + Printf.printf "Error wrong return type program: error (success)\n" +| _ -> + Printf.printf "Error wrong return type program: failed\n" (* -------------------------------------------------------------------------- *) (* Error wrong specification program *) @@ -43,8 +47,10 @@ let program = ;; match typecheck program with - Error (`WrongTypeSpecification _) -> Printf.printf "Error wrong specification program: error (success)\n" -| _ -> Printf.printf "Error wrong specification program: failed\n" +| Error (`WrongTypeSpecification _) -> + Printf.printf "Error wrong specification program: error (success)\n" +| _ -> + Printf.printf "Error wrong specification program: failed\n" (* -------------------------------------------------------------------------- *) (* Error wrong input type program *) @@ -61,8 +67,10 @@ let program = ;; match typecheck program with - Error (`WrongType _) -> Printf.printf "Error wrong input type program: error (success)\n" -| _ -> Printf.printf "Error wrong input type program: failed\n" +| Error (`WrongType _) -> + Printf.printf "Error wrong input type program: error (success)\n" +| _ -> + Printf.printf "Error wrong input type program: failed\n" (* -------------------------------------------------------------------------- *) (* Error not a function program *) @@ -75,8 +83,10 @@ let program = ;; match typecheck program with - Error (`WrongType _) -> Printf.printf "Error not a function program: error (success)\n" -| _ -> Printf.printf "Error not a function program: failed\n" +| Error (`WrongType _) -> + Printf.printf "Error not a function program: error (success)\n" +| _ -> + Printf.printf "Error not a function program: failed\n" (* -------------------------------------------------------------------------- *) (* Error if branches with different types program *) @@ -90,8 +100,11 @@ let program = ;; match typecheck program with - Error (`WrongType _) -> Printf.printf "Error if branches with different types program: error (success)\n" -| _ -> Printf.printf "Error if branches with different types program: failed\n" +| Error (`WrongType _) -> + Printf.printf + "Error if branches with different types program: error (success)\n" +| _ -> + Printf.printf "Error if branches with different types program: failed\n" (* -------------------------------------------------------------------------- *) (* Error if guard is not a boolean program *) @@ -105,8 +118,10 @@ let program = ;; match typecheck program with - Error (`WrongType _) -> Printf.printf "Error if guard is not a boolean program: error (success)\n" -| _ -> Printf.printf "Error if guard is not a boolean program: failed\n" +| Error (`WrongType _) -> + Printf.printf "Error if guard is not a boolean program: error (success)\n" +| _ -> + Printf.printf "Error if guard is not a boolean program: failed\n" (* -------------------------------------------------------------------------- *) @@ -120,8 +135,10 @@ let program = ;; match typecheck program with - Ok _ -> Printf.printf "Identity program: success\n" -| _ -> Printf.printf "Identity program: failed\n" +| Ok _ -> + Printf.printf "Identity program: success\n" +| _ -> + Printf.printf "Identity program: failed\n" (* -------------------------------------------------------------------------- *) (* Constant program *) @@ -134,8 +151,10 @@ let program = ;; match typecheck program with - Ok _ -> Printf.printf "Constant program: success\n" -| _ -> Printf.printf "Constant program: failed\n" +| Ok _ -> + Printf.printf "Constant program: success\n" +| _ -> + Printf.printf "Constant program: failed\n" (* -------------------------------------------------------------------------- *) (* Partial application of function program *) @@ -155,8 +174,10 @@ let program = ;; match typecheck program with - Ok _ -> Printf.printf "Partial application of function program 1: success\n" -| _ -> Printf.printf "Partial application of function program 1: failed\n" +| Ok _ -> + Printf.printf "Partial application of function program 1: success\n" +| _ -> + Printf.printf "Partial application of function program 1: failed\n" (* -------------------------------------------------------------------------- *) (* Partial application of function program *) @@ -173,8 +194,10 @@ let program = ;; match typecheck program with - Ok _ -> Printf.printf "Partial application of function program 2: success\n" -| _ -> Printf.printf "Partial application of function program 2: failed\n" +| Ok _ -> + Printf.printf "Partial application of function program 2: success\n" +| _ -> + Printf.printf "Partial application of function program 2: failed\n" (* -------------------------------------------------------------------------- *) (* Passing functions to functions program *) @@ -183,10 +206,13 @@ let program = ("f", (Function ( "z", - FunctionType (FunctionType (IntegerType, IntegerType), FunctionType (FunctionType (IntegerType, IntegerType), FunctionType (IntegerType, IntegerType))), + FunctionType (FunctionType (IntegerType, IntegerType), + FunctionType (FunctionType (IntegerType, IntegerType), + FunctionType (IntegerType, IntegerType))), (Function ( "y", - FunctionType (FunctionType (IntegerType, IntegerType), FunctionType (IntegerType, IntegerType)), + FunctionType (FunctionType (IntegerType, IntegerType), + FunctionType (IntegerType, IntegerType)), Function ( "x", FunctionType (IntegerType, IntegerType), @@ -201,18 +227,23 @@ let program = ( (Application (Variable "f", - Function ("x", FunctionType (IntegerType, IntegerType), Plus (Variable "x", Integer 1)) + Function ("x", FunctionType (IntegerType, IntegerType), + Plus (Variable "x", Integer 1)) ) ), - Function ("x", FunctionType (IntegerType, IntegerType), Minus (Variable "x", Integer 1)) + Function ("x", FunctionType (IntegerType, IntegerType), + Minus (Variable "x", Integer 1)) ) ) ) ;; + match typecheck program with - Ok _ -> Printf.printf "Passing functions to functions program: success\n" -| _ -> Printf.printf "Passing functions to functions program: failed\n" +| Ok _ -> + Printf.printf "Passing functions to functions program: success\n" +| _ -> + Printf.printf "Passing functions to functions program: failed\n" (* -------------------------------------------------------------------------- *) (* Recursive function program *) @@ -221,28 +252,40 @@ let program = ("f", "x", FunctionType (IntegerType, IntegerType), - (IfThenElse (CmpLess (Variable "x", Integer 2),Integer 1, Plus (Variable "x", Application (Variable "f", Minus (Variable "x", Integer 1))))), + (IfThenElse (CmpLess (Variable "x", Integer 2), + Integer 1, + Plus ( + Variable "x", + Application ( + Variable "f", + Minus (Variable "x", Integer 1))))), (Variable "f") ) ;; match typecheck program with - Ok _ -> Printf.printf "Recursive function program: success\n" -| _ -> Printf.printf "Recursive function program: failed\n" +| Ok _ -> + Printf.printf "Recursive function program: success\n" +| _ -> + Printf.printf "Recursive function program: failed\n" (* -------------------------------------------------------------------------- *) (* Scope program *) let program = LetIn ("f", - (LetIn ("a", Integer 1, (Function ("y", FunctionType (IntegerType, IntegerType), Plus (Variable "y", Variable "a"))))), + (LetIn ("a", Integer 1, (Function ("y", + FunctionType (IntegerType, IntegerType), + Plus (Variable "y", Variable "a"))))), (LetIn ("a", Integer 2, Variable "f")) ) ;; match typecheck program with - Ok _ -> Printf.printf "Scope program: success\n" -| _ -> Printf.printf "Scope program: failed\n" +| Ok _ -> + Printf.printf "Scope program: success\n" +| _ -> + Printf.printf "Scope program: failed\n" (* -------------------------------------------------------------------------- *) (* Factorial program *) @@ -251,14 +294,20 @@ let program = "f", "x", FunctionType (IntegerType, IntegerType), - (IfThenElse (CmpLessEq (Variable "x", Integer 0), Integer 1, Times (Variable "x", Application (Variable "f", Minus (Variable "x", Integer 1))))), + (IfThenElse (CmpLessEq (Variable "x", Integer 0), + Integer 1, + Times (Variable "x", + Application (Variable "f", + Minus (Variable "x", Integer 1))))), (Variable "f") ) ;; match typecheck program with - Ok _ -> Printf.printf "Factorial program: success\n" -| _ -> Printf.printf "Factorial program: failed\n" +| Ok _ -> + Printf.printf "Factorial program: success\n" +| _ -> + Printf.printf "Factorial program: failed\n" (* -------------------------------------------------------------------------- *) (* Hailstone sequence's lenght program *) @@ -270,27 +319,41 @@ let program = FunctionType (TupleType (IntegerType, IntegerType), IntegerType), ( IfThenElse (BNot (Cmp (First (Variable "input"), Integer 1)), - (IfThenElse (Cmp (Modulo (First (Variable "input"), Integer 2), Integer 0), + (IfThenElse (Cmp (Modulo (First (Variable "input"), + Integer 2), + Integer 0), Application (Variable "collatz", Tuple ( - Division (First (Variable "input"), Integer 2), - Plus (Integer 1, Second (Variable "input")))), + Division (First + (Variable "input"), + Integer 2), + Plus (Integer 1, Second + (Variable "input")))), Application (Variable "collatz", Tuple ( - Plus (Integer 1, Times (Integer 3, First (Variable "input"))), - Plus (Integer 1, Second (Variable "input")))))), + Plus + (Integer 1, + Times + (Integer 3, + First (Variable "input"))), + Plus ( + Integer 1, + Second (Variable "input")))))), (Second (Variable "input"))) ), (Function ("x", FunctionType (IntegerType, IntegerType), - Application (Variable "collatz", Tuple (Variable "x", Integer 1))) - ) + Application (Variable "collatz", + Tuple (Variable "x", Integer 1)))) ) ;; + match typecheck program with - Ok _ -> Printf.printf "Hailstone sequence's lenght program: success\n" -| _ -> Printf.printf "Hailstone sequence's lenght program: failed\n" +| Ok _ -> + Printf.printf "Hailstone sequence's lenght program: success\n" +| _ -> + Printf.printf "Hailstone sequence's lenght program: failed\n" (* -------------------------------------------------------------------------- *) (* Sum multiples of 3 and 5 program *) @@ -300,11 +363,15 @@ let program = "sum", "n", FunctionType (IntegerType, IntegerType), - (IfThenElse ((BOr (Cmp (Modulo (Variable "n", Integer 3), Integer 0), Cmp (Modulo (Variable "n", Integer 5), Integer 0))), - Plus (Variable "n", Application (Variable "sum", Minus (Variable "n", Integer 1))), + (IfThenElse ((BOr (Cmp (Modulo (Variable "n", Integer 3), Integer 0), + Cmp (Modulo (Variable "n", Integer 5), Integer 0))), + Plus (Variable "n", + Application (Variable "sum", + Minus (Variable "n", Integer 1))), (IfThenElse ((CmpLessEq (Variable "n", Integer 1)), (Integer 0), - (Application (Variable "sum", Minus (Variable "n", Integer 1)))) + (Application (Variable "sum", + Minus (Variable "n", Integer 1)))) )) ), (Variable "sum") @@ -312,8 +379,10 @@ let program = ;; match typecheck program with - Ok _ -> Printf.printf "Sum multiples of 3 and 5 program: success\n" -| _ -> Printf.printf "Sum multiples of 3 and 5 program: failed\n" +| Ok _ -> + Printf.printf "Sum multiples of 3 and 5 program: success\n" +| _ -> + Printf.printf "Sum multiples of 3 and 5 program: failed\n" (* -------------------------------------------------------------------------- *) (* Rand program *) @@ -327,30 +396,56 @@ let program = ;; match typecheck program with - Ok _ -> Printf.printf "Rand program: success\n" -| _ -> Printf.printf "Rand program: failed\n" +| Ok _ -> + Printf.printf "Rand program: success\n" +| _ -> + Printf.printf "Rand program: failed\n" (* -------------------------------------------------------------------------- *) (* Fibonacci program *) let program = LetFun ( "fib", - "input", - FunctionType (TupleType (TupleType (IntegerType, IntegerType), IntegerType), IntegerType), - (IfThenElse (Cmp (First (First (Variable "input")), Integer 0), - Second (First (Variable "input")), - Application (Variable "fib", - Tuple ( Tuple ( - Minus (First (First (Variable "input")), Integer 1), - Second (Variable "input")), - Plus (Second (First (Variable "input")), Second (Variable "input")))) - )), + "i", + FunctionType (IntegerType, FunctionType + (IntegerType, FunctionType (IntegerType, IntegerType))), + Function ( + "a", + FunctionType (IntegerType, FunctionType (IntegerType, IntegerType)), + Function ( + "b", + FunctionType (IntegerType, IntegerType), + (IfThenElse (Cmp (Variable "i", Integer 0), + Variable "a", + Application ( + Application ( + Application ( + Variable "fib", + Minus (Variable "i", Integer 1)), + Variable "b"), + Plus (Variable "a", Variable "b") + ) + )) + ) + ), Function ("x", FunctionType (IntegerType, IntegerType), - (Application (Variable "fib", Tuple (Tuple (Variable "x", Integer 0), Integer 1)))) + Application ( + Application ( + Application ( + Variable "fib", + Variable "x" + ), + Integer 0 + ), + Integer 1 + ) + ) ) ;; match typecheck program with - Ok _ -> Printf.printf "Fibonacci program: success\n" -| _ -> Printf.printf "Fibonacci program: failed\n" +| Ok _ -> + Printf.printf "Fibonacci program: success\n" +| _ -> + Printf.printf "Fibonacci program: failed\n" diff --git a/test/testingTypeFunParser.ml b/test/testingTypeFunParser.ml index dad6fc8..73aaaad 100644 --- a/test/testingTypeFunParser.ml +++ b/test/testingTypeFunParser.ml @@ -215,7 +215,10 @@ match get_result program with (* Sum multiples of 3 and 5 program *) let program = - "let rec sum n: int -> int = if n % 3 == 0 || n % 5 == 0 then n + sum (n - 1) else if n < 1 then 0 else sum (n - 1) in sum" + "let rec sum n: int -> int = + if n % 3 == 0 || n % 5 == 0 + then n + sum (n - 1) + else if n < 1 then 0 else sum (n - 1) in sum" ;; Printf.printf "Sum multiples of 3 and 5 program: "; From b16aef4ecbc7aa256aa88f2a778590becad1e5b8 Mon Sep 17 00:00:00 2001 From: elvis Date: Sun, 26 Jan 2025 23:03:20 +0100 Subject: [PATCH 25/29] Better styling --- lib/analysis/Cfg.ml | 6 +++--- lib/analysis/Dataflow.ml | 8 +++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/analysis/Cfg.ml b/lib/analysis/Cfg.ml index c79e20a..74f678f 100644 --- a/lib/analysis/Cfg.ml +++ b/lib/analysis/Cfg.ml @@ -132,8 +132,8 @@ module Make (M: PrintableType) = struct } let addToLastNode (newcontent: elt) (cfg: t) : t = - match cfg.empty with - | true -> let newnode = Node.create () in + if cfg.empty then + let newnode = Node.create () in { empty = false; nodes = NodeSet.singleton newnode; edges = NodeMap.empty; @@ -144,7 +144,7 @@ module Make (M: PrintableType) = struct terminal = Some newnode; content = NodeMap.singleton newnode [newcontent] } - | false -> + else let prevcfgterminal = Option.get cfg.terminal in { cfg with content = (NodeMap.add_to_list_last diff --git a/lib/analysis/Dataflow.ml b/lib/analysis/Dataflow.ml index da583f8..04dcb86 100644 --- a/lib/analysis/Dataflow.ml +++ b/lib/analysis/Dataflow.ml @@ -75,13 +75,15 @@ module Make (M: Cfg.PrintableType) (I: Cfg.PrintableType) = struct let pp (ppf: out_channel) (c: t) : unit = ( Printf.fprintf ppf "Cfg:\n"; Printf.fprintf ppf "Nodes' ids: "; - List.iter (fun (x : Node.t) -> Printf.fprintf ppf "%d " x.id) (NodeSet.to_list c.t.nodes); + List.iter (fun (x : Node.t) -> + Printf.fprintf ppf "%d " x.id) (NodeSet.to_list c.t.nodes); Printf.fprintf ppf "\n"; Printf.fprintf ppf "Nodes' edges:\n"; List.iter (fun ((n, (a, b)) : (Node.t * (Node.t * Node.t option))) : unit -> - match b with None -> Printf.fprintf ppf "\t%d -> %d\n" n.id a.id - | Some b -> Printf.fprintf ppf "\t%d -> %d, %d\n" n.id a.id b.id + match b with + None -> Printf.fprintf ppf "\t%d -> %d\n" n.id a.id + | Some b -> Printf.fprintf ppf "\t%d -> %d, %d\n" n.id a.id b.id ) (NodeMap.to_list c.t.edges); Printf.fprintf ppf "\n"; From 6d5587692c2f35e86e75451357ae1d918449e074 Mon Sep 17 00:00:00 2001 From: elvis Date: Sun, 26 Jan 2025 23:03:36 +0100 Subject: [PATCH 26/29] Better styling for utility --- lib/utility/utility.ml | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/lib/utility/utility.ml b/lib/utility/utility.ml index df9e491..3b42a65 100644 --- a/lib/utility/utility.ml +++ b/lib/utility/utility.ml @@ -48,8 +48,8 @@ let int_not a = (* else if x < base then *) (* String.get alphabet x |> String.make 1 *) (* else *) -(* (fromIntToString (alphabet) (x/base - 1)) ^ (String.get alphabet (x mod base) *) -(* |> String.make 1) *) +(* (fromIntToString (alphabet) (x/base - 1)) ^ *) +(* (String.get alphabet (x mod base) |> String.make 1) *) (* true if every element of la is in lb *) @@ -58,9 +58,7 @@ let inclusion la lb = function [] -> true | b::lb -> - if List.mem b la - then aux la lb - else false + List.mem b la && aux la lb in aux lb la @@ -127,9 +125,9 @@ let unique_union_assoc f l1 l2 = (* returns a list with at most n items and the rest in the second *) let rec take (n: int) (l: 'a list) : ('a list * 'a list) = - match n with - | 0 -> ([], l) - | n -> + if n = 0 + then ([], l) + else match l with | [] -> ([], []) | i::ls -> From 5e8b3394400ed35f512350187770ccd9dfb1cecf Mon Sep 17 00:00:00 2001 From: elvis Date: Mon, 27 Jan 2025 00:18:23 +0100 Subject: [PATCH 27/29] Better styling for miniFun --- lib/miniFun/Semantics.ml | 9 ++++--- lib/miniFun/TypeChecker.ml | 6 +++-- lib/miniFun/Types.ml | 55 +++++++++++++++++++------------------- 3 files changed, 38 insertions(+), 32 deletions(-) diff --git a/lib/miniFun/Semantics.ml b/lib/miniFun/Semantics.ml index 49640d6..5661a93 100644 --- a/lib/miniFun/Semantics.ml +++ b/lib/miniFun/Semantics.ml @@ -5,14 +5,17 @@ Random.self_init () let (let*) = Result.bind -let rec evaluate (mem: memory) (command: t_exp) : (permittedValues, [> error]) result = +let rec evaluate (mem: memory) (command: t_exp) : + (permittedValues, [> error]) result = match command with Integer n -> Ok (IntegerPermitted n) | Boolean b -> Ok (BooleanPermitted b) | Variable v -> ( match VariableMap.find_opt v mem.assignments with - None -> Error (`AbsentAssignment ("The variable " ^ v ^ " is not defined.")) - | Some a -> Ok a + | None -> + Error (`AbsentAssignment ("The variable " ^ v ^ " is not defined.")) + | Some a -> + Ok a ) | Tuple (x, y) -> ( let* xval = evaluate mem x in diff --git a/lib/miniFun/TypeChecker.ml b/lib/miniFun/TypeChecker.ml index a19378e..d3d8df0 100644 --- a/lib/miniFun/TypeChecker.ml +++ b/lib/miniFun/TypeChecker.ml @@ -5,7 +5,8 @@ Random.self_init () let (let*) = Result.bind -let rec evaluate_type (program: t_exp) (context: ftype VariableMap.t) : (ftype, [> typechecking_error]) result = +let rec evaluate_type (program: t_exp) (context: ftype VariableMap.t) : + (ftype, [> typechecking_error]) result = match program with Integer _ -> Ok IntegerType | Boolean _ -> Ok BooleanType @@ -25,7 +26,8 @@ let rec evaluate_type (program: t_exp) (context: ftype VariableMap.t) : (ftype, the type of the body using the bindings for the input *) match typef with FunctionType (tin, tout) -> ( - let* typefbody = evaluate_type fbody (VariableMap.add x tin context) in + let* typefbody = evaluate_type fbody (VariableMap.add x tin context) + in if (typefbody = tout) then Ok typef else diff --git a/lib/miniFun/Types.ml b/lib/miniFun/Types.ml index 9bfae31..a32281e 100644 --- a/lib/miniFun/Types.ml +++ b/lib/miniFun/Types.ml @@ -9,33 +9,34 @@ type ftype = | FunctionType of ftype * ftype type t_exp = - Integer of int (* x := a *) - | Boolean of bool (* v *) - | Variable of variable (* x *) - | Tuple of t_exp * t_exp (* (a, b) *) - | Function of variable * ftype * t_exp (* lambda x: t. x *) - | Application of t_exp * t_exp (* x x *) - | Plus of t_exp * t_exp (* x + x *) - | Minus of t_exp * t_exp (* x - x *) - | Times of t_exp * t_exp (* x * x *) - | Division of t_exp * t_exp (* x / x *) - | Modulo of t_exp * t_exp (* x % x *) - | Power of t_exp * t_exp (* x ^ x *) - | PowerMod of t_exp * t_exp * t_exp (* (x ^ x) % x *) - | Rand of t_exp (* rand(0, x) *) - | BAnd of t_exp * t_exp (* x && x *) - | BOr of t_exp * t_exp (* x || x *) - | BNot of t_exp (* not x *) - | First of t_exp (* fst x *) - | Second of t_exp (* scn x *) - | Cmp of t_exp * t_exp (* x == x *) - | CmpLess of t_exp * t_exp (* x < x *) - | CmpLessEq of t_exp * t_exp (* x <= x *) - | CmpGreater of t_exp * t_exp (* x > x *) - | CmpGreaterEq of t_exp * t_exp (* x >= x *) - | IfThenElse of t_exp * t_exp * t_exp (* if b then c else c *) - | LetIn of variable * t_exp * t_exp (* let x = x in x *) - | LetFun of variable * variable * ftype * t_exp * t_exp (* let rec x. y: t. x in x*) + Integer of int (* x := a *) + | Boolean of bool (* v *) + | Variable of variable (* x *) + | Tuple of t_exp * t_exp (* (a, b) *) + | Function of variable * ftype * t_exp (* lambda x: t. x *) + | Application of t_exp * t_exp (* x x *) + | Plus of t_exp * t_exp (* x + x *) + | Minus of t_exp * t_exp (* x - x *) + | Times of t_exp * t_exp (* x * x *) + | Division of t_exp * t_exp (* x / x *) + | Modulo of t_exp * t_exp (* x % x *) + | Power of t_exp * t_exp (* x ^ x *) + | PowerMod of t_exp * t_exp * t_exp (* (x ^ x) % x *) + | Rand of t_exp (* rand(0, x) *) + | BAnd of t_exp * t_exp (* x && x *) + | BOr of t_exp * t_exp (* x || x *) + | BNot of t_exp (* not x *) + | First of t_exp (* fst x *) + | Second of t_exp (* scn x *) + | Cmp of t_exp * t_exp (* x == x *) + | CmpLess of t_exp * t_exp (* x < x *) + | CmpLessEq of t_exp * t_exp (* x <= x *) + | CmpGreater of t_exp * t_exp (* x > x *) + | CmpGreaterEq of t_exp * t_exp (* x >= x *) + | IfThenElse of t_exp * t_exp * t_exp (* if b then c else c *) + | LetIn of variable * t_exp * t_exp (* let x = x in x *) + | LetFun of variable * variable * ftype * t_exp * t_exp + (* let rec x. y: t. x in x*) type permittedValues = IntegerPermitted of int From 4ab0b40ccade6f1d54015ba4867b7e098226c6c3 Mon Sep 17 00:00:00 2001 From: elvis Date: Mon, 27 Jan 2025 01:17:53 +0100 Subject: [PATCH 28/29] Better styling for miniImp --- lib/miniImp/CfgImp.ml | 63 +++-- lib/miniImp/CfgRISC.ml | 417 +++++++++++++++++++++++--------- lib/miniImp/RISC.ml | 87 ++++--- lib/miniImp/RISCSemantics.ml | 20 +- lib/miniImp/Semantics.ml | 16 +- lib/miniImp/Types.ml | 136 +++++++---- lib/miniImp/definedVariables.ml | 11 +- lib/miniImp/liveVariables.ml | 8 +- lib/miniImp/reduceRegisters.ml | 251 +++++++++---------- lib/miniImp/replacePowerMod.ml | 28 ++- 10 files changed, 677 insertions(+), 360 deletions(-) diff --git a/lib/miniImp/CfgImp.ml b/lib/miniImp/CfgImp.ml index 0428ea3..c447109 100644 --- a/lib/miniImp/CfgImp.ml +++ b/lib/miniImp/CfgImp.ml @@ -30,31 +30,52 @@ module SimpleStatements = struct let pp (ppf: out_channel) (c: t) : unit = let rec helper_c (ppf) (c: t) : unit = match c with - | SimpleSkip -> Printf.fprintf ppf "Skip" - | SimpleAssignment (v, a) -> Printf.fprintf ppf "Assignment {%s, %a}" v helper_a a - | SimpleGuard (b) -> Printf.fprintf ppf "Guard {%a}" helper_b b + | SimpleSkip -> + Printf.fprintf ppf "Skip" + | SimpleAssignment (v, a) -> + Printf.fprintf ppf "Assignment {%s, %a}" v helper_a a + | SimpleGuard (b) -> + Printf.fprintf ppf "Guard {%a}" helper_b b and helper_b (ppf) (c: simpleBoolean) : unit = match c with - | SimpleBoolean b -> Printf.fprintf ppf "%b" b - | SimpleBAnd (b1, b2) -> Printf.fprintf ppf "{%a && %a}" helper_b b1 helper_b b2 - | SimpleBOr (b1, b2) -> Printf.fprintf ppf "{%a || %a}" helper_b b1 helper_b b2 - | SimpleBNot b -> Printf.fprintf ppf "{not %a}" helper_b b - | SimpleBCmp (a1, a2) -> Printf.fprintf ppf "{%a == %a}" helper_a a1 helper_a a2 - | SimpleBCmpLess (a1, a2) -> Printf.fprintf ppf "{%a < %a}" helper_a a1 helper_a a2 - | SimpleBCmpLessEq (a1, a2) -> Printf.fprintf ppf "{%a <= %a}" helper_a a1 helper_a a2 - | SimpleBCmpGreater (a1, a2) -> Printf.fprintf ppf "{%a > %a}" helper_a a1 helper_a a2 - | SimpleBCmpGreaterEq (a1, a2) -> Printf.fprintf ppf "{%a >= %a}" helper_a a1 helper_a a2 + | SimpleBoolean b -> + Printf.fprintf ppf "%b" b + | SimpleBAnd (b1, b2) -> + Printf.fprintf ppf "{%a && %a}" helper_b b1 helper_b b2 + | SimpleBOr (b1, b2) -> + Printf.fprintf ppf "{%a || %a}" helper_b b1 helper_b b2 + | SimpleBNot b -> + Printf.fprintf ppf "{not %a}" helper_b b + | SimpleBCmp (a1, a2) -> + Printf.fprintf ppf "{%a == %a}" helper_a a1 helper_a a2 + | SimpleBCmpLess (a1, a2) -> + Printf.fprintf ppf "{%a < %a}" helper_a a1 helper_a a2 + | SimpleBCmpLessEq (a1, a2) -> + Printf.fprintf ppf "{%a <= %a}" helper_a a1 helper_a a2 + | SimpleBCmpGreater (a1, a2) -> + Printf.fprintf ppf "{%a > %a}" helper_a a1 helper_a a2 + | SimpleBCmpGreaterEq (a1, a2) -> + Printf.fprintf ppf "{%a >= %a}" helper_a a1 helper_a a2 and helper_a (ppf) (c: simpleArithmetic) : unit = match c with - | SimpleVariable (v) -> Printf.fprintf ppf "%s" v - | SimpleInteger (i) -> Printf.fprintf ppf "%d" i - | SimplePlus (a1, a2) -> Printf.fprintf ppf "{%a + %a}" helper_a a1 helper_a a2 - | SimpleMinus (a1, a2) -> Printf.fprintf ppf "{%a - %a}" helper_a a1 helper_a a2 - | SimpleTimes (a1, a2) -> Printf.fprintf ppf "{%a * %a}" helper_a a1 helper_a a2 - | SimpleDivision (a1, a2) -> Printf.fprintf ppf "{%a / %a}" helper_a a1 helper_a a2 - | SimpleModulo (a1, a2) -> Printf.fprintf ppf "{%a %% %a}" helper_a a1 helper_a a2 - | SimplePower (a1, a2) -> Printf.fprintf ppf "{%a ^ %a}" helper_a a1 helper_a a2 - | SimpleRand (a) -> Printf.fprintf ppf "{rand %a}" helper_a a + | SimpleVariable (v) -> + Printf.fprintf ppf "%s" v + | SimpleInteger (i) -> + Printf.fprintf ppf "%d" i + | SimplePlus (a1, a2) -> + Printf.fprintf ppf "{%a + %a}" helper_a a1 helper_a a2 + | SimpleMinus (a1, a2) -> + Printf.fprintf ppf "{%a - %a}" helper_a a1 helper_a a2 + | SimpleTimes (a1, a2) -> + Printf.fprintf ppf "{%a * %a}" helper_a a1 helper_a a2 + | SimpleDivision (a1, a2) -> + Printf.fprintf ppf "{%a / %a}" helper_a a1 helper_a a2 + | SimpleModulo (a1, a2) -> + Printf.fprintf ppf "{%a %% %a}" helper_a a1 helper_a a2 + | SimplePower (a1, a2) -> + Printf.fprintf ppf "{%a ^ %a}" helper_a a1 helper_a a2 + | SimpleRand (a) -> + Printf.fprintf ppf "{rand %a}" helper_a a in helper_c ppf c diff --git a/lib/miniImp/CfgRISC.ml b/lib/miniImp/CfgRISC.ml index 4494deb..a18651b 100644 --- a/lib/miniImp/CfgRISC.ml +++ b/lib/miniImp/CfgRISC.ml @@ -49,13 +49,21 @@ module RISCSimpleStatements = struct let pp (ppf: out_channel) (v: t) : unit = let rec pp_t (ppf: out_channel) (v: t) : unit = match v with - Nop -> Printf.fprintf ppf "Nop" - | BRegOp (b, r1, r2, r3) -> Printf.fprintf ppf "%a r%s r%s => r%s" pp_brop b r1.index r2.index r3.index - | BImmOp (b, r1, i, r3) -> Printf.fprintf ppf "%a r%s %d => r%s" pp_biop b r1.index i r3.index - | URegOp (u, r1, r2) -> Printf.fprintf ppf "%a r%s => r%s" pp_urop u r1.index r2.index - | Load (r1, r2) -> Printf.fprintf ppf "Load r%s => r%s" r1.index r2.index - | LoadI (i, r2) -> Printf.fprintf ppf "LoadI %d => r%s" i r2.index - | Store (r1, r2) -> Printf.fprintf ppf "Store r%s => r%s" r1.index r2.index + Nop -> + Printf.fprintf ppf "Nop" + | BRegOp (b, r1, r2, r3) -> + Printf.fprintf ppf "%a r%s r%s => r%s" + pp_brop b r1.index r2.index r3.index + | BImmOp (b, r1, i, r3) -> + Printf.fprintf ppf "%a r%s %d => r%s" pp_biop b r1.index i r3.index + | URegOp (u, r1, r2) -> + Printf.fprintf ppf "%a r%s => r%s" pp_urop u r1.index r2.index + | Load (r1, r2) -> + Printf.fprintf ppf "Load r%s => r%s" r1.index r2.index + | LoadI (i, r2) -> + Printf.fprintf ppf "LoadI %d => r%s" i r2.index + | Store (r1, r2) -> + Printf.fprintf ppf "Store r%s => r%s" r1.index r2.index and pp_brop (ppf: out_channel) (v: brop) : unit = match v with Add -> Printf.fprintf ppf "Add" @@ -118,7 +126,8 @@ module RegisterMap = struct ({index = string_of_int !globalcounter}, {assignments = Types.VariableMap.add x - ({index = (string_of_int !globalcounter)}: RISCSimpleStatements.register) + ({index = (string_of_int !globalcounter)} + : RISCSimpleStatements.register) m.assignments})) | Some i -> (i, m) @@ -129,7 +138,8 @@ module RegisterMap = struct ({index = string_of_int !globalcounter}, {assignments = Types.VariableMap.add freshvariable - ({index = string_of_int !globalcounter}: RISCSimpleStatements.register) + ({index = string_of_int !globalcounter} + : RISCSimpleStatements.register) m.assignments}, freshvariable) @@ -182,11 +192,16 @@ and c_ss_sb (convertedcode @ [LoadI (0, register)], m) ) | (_, _) -> ( - let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in - let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in + let partialresreg1, m, _partialresvar1 = + RegisterMap.get_fresh_register m + in + let partialresreg2, m, _partialresvar2 = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sb b1 m convertedcode partialresreg1 in let convertedcode, m = c_ss_sb b2 m convertedcode partialresreg2 in - (convertedcode @ [BRegOp (And, partialresreg1, partialresreg2, register)], m) + (convertedcode @ + [BRegOp (And, partialresreg1, partialresreg2, register)], m) ) ) | SimpleBOr (b1, b2) -> ( @@ -200,11 +215,16 @@ and c_ss_sb (LoadI (1, register) :: convertedcode, m) ) | (_, _) -> ( - let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in - let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in + let partialresreg1, m, _partialresvar1 = + RegisterMap.get_fresh_register m + in + let partialresreg2, m, _partialresvar2 = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sb b1 m convertedcode partialresreg1 in let convertedcode, m = c_ss_sb b2 m convertedcode partialresreg2 in - (convertedcode @ [BRegOp (Or, partialresreg1, partialresreg2, register)], m) + (convertedcode @ + [BRegOp (Or, partialresreg1, partialresreg2, register)], m) ) ) | SimpleBNot (b) -> ( @@ -216,7 +236,9 @@ and c_ss_sb (LoadI (1, register) :: convertedcode, m) ) | _ -> ( - let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let partialresreg, m, _partialresvar = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sb b m convertedcode partialresreg in (convertedcode @ [URegOp (Not, partialresreg, register)], m) ) @@ -230,7 +252,9 @@ and c_ss_sb ) | (SimpleInteger (i), a) | (a, SimpleInteger (i)) -> ( - let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let partialresreg, m, _partialresvar = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in (convertedcode @ [BImmOp (EqI, partialresreg, i, register)], m) ) @@ -241,17 +265,26 @@ and c_ss_sb ) | (SimpleVariable (x), a) | (a, SimpleVariable (x)) -> ( - let xreg, m = RegisterMap.get_or_set_register x m in - let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let xreg, m = + RegisterMap.get_or_set_register x m + in + let partialresreg, m, _partialresvar = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in (convertedcode @ [BRegOp (Eq, partialresreg, xreg, register)], m) ) | (_, _) -> ( - let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in - let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in + let partialresreg1, m, _partialresvar1 = + RegisterMap.get_fresh_register m + in + let partialresreg2, m, _partialresvar2 = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a1 m convertedcode partialresreg1 in let convertedcode, m = c_ss_sa a2 m convertedcode partialresreg2 in - (convertedcode @ [BRegOp (Eq, partialresreg1, partialresreg2, register)], m) + (convertedcode @ + [BRegOp (Eq, partialresreg1, partialresreg2, register)], m) ) ) | SimpleBCmpLess (a1, a2) -> ( @@ -265,12 +298,16 @@ and c_ss_sb (convertedcode @ [BImmOp (LessI, xreg, i, register)], m) ) | (SimpleInteger (i), a) -> ( - let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let partialresreg, m, _partialresvar = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in (convertedcode @ [BImmOp (MoreI, partialresreg, i, register)], m) ) | (a, SimpleInteger (i)) -> ( - let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let partialresreg, m, _partialresvar = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in (convertedcode @ [BImmOp (LessI, partialresreg, i, register)], m) ) @@ -281,22 +318,31 @@ and c_ss_sb ) | (SimpleVariable (x), a) -> ( let xreg, m = RegisterMap.get_or_set_register x m in - let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let partialresreg, m, _partialresvar = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in (convertedcode @ [BRegOp (Less, xreg, partialresreg, register)], m) ) | (a, SimpleVariable (x)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in - let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let partialresreg, m, _partialresvar = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in (convertedcode @ [BRegOp (Less, partialresreg, xreg, register)], m) ) | (_, _) -> ( - let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in - let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in + let partialresreg1, m, _partialresvar1 = + RegisterMap.get_fresh_register m + in + let partialresreg2, m, _partialresvar2 = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a1 m convertedcode partialresreg1 in let convertedcode, m = c_ss_sa a2 m convertedcode partialresreg2 in - (convertedcode @ [BRegOp (Less, partialresreg1, partialresreg2, register)], m) + (convertedcode @ + [BRegOp (Less, partialresreg1, partialresreg2, register)], m) ) ) | SimpleBCmpLessEq (a1, a2) -> ( @@ -310,12 +356,16 @@ and c_ss_sb (convertedcode @ [BImmOp (LessEqI, xreg, i, register)], m) ) | (SimpleInteger (i), a) -> ( - let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let partialresreg, m, _partialresvar = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in (convertedcode @ [BImmOp (MoreEqI, partialresreg, i, register)], m) ) | (a, SimpleInteger (i)) -> ( - let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let partialresreg, m, _partialresvar = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in (convertedcode @ [BImmOp (LessEqI, partialresreg, i, register)], m) ) @@ -326,22 +376,31 @@ and c_ss_sb ) | (SimpleVariable (x), a) -> ( let xreg, m = RegisterMap.get_or_set_register x m in - let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let partialresreg, m, _partialresvar = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in (convertedcode @ [BRegOp (LessEq, xreg, partialresreg, register)], m) ) | (a, SimpleVariable (x)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in - let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let partialresreg, m, _partialresvar = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in (convertedcode @ [BRegOp (LessEq, partialresreg, xreg, register)], m) ) | (_, _) -> ( - let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in - let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in + let partialresreg1, m, _partialresvar1 = + RegisterMap.get_fresh_register m + in + let partialresreg2, m, _partialresvar2 = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a1 m convertedcode partialresreg1 in let convertedcode, m = c_ss_sa a2 m convertedcode partialresreg2 in - (convertedcode @ [BRegOp (LessEq, partialresreg1, partialresreg2, register)], m) + (convertedcode @ + [BRegOp (LessEq, partialresreg1, partialresreg2, register)], m) ) ) | SimpleBCmpGreater (a1, a2) -> ( @@ -355,12 +414,16 @@ and c_ss_sb (convertedcode @ [BImmOp (MoreI, xreg, i, register)], m) ) | (SimpleInteger (i), a) -> ( - let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let partialresreg, m, _partialresvar = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in (convertedcode @ [BImmOp (LessI, partialresreg, i, register)], m) ) | (a, SimpleInteger (i)) -> ( - let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let partialresreg, m, _partialresvar = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in (convertedcode @ [BImmOp (MoreI, partialresreg, i, register)], m) ) @@ -371,22 +434,31 @@ and c_ss_sb ) | (SimpleVariable (x), a) -> ( let xreg, m = RegisterMap.get_or_set_register x m in - let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let partialresreg, m, _partialresvar = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in (convertedcode @ [BRegOp (More, xreg, partialresreg, register)], m) ) | (a, SimpleVariable (x)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in - let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let partialresreg, m, _partialresvar = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in (convertedcode @ [BRegOp (More, partialresreg, xreg, register)], m) ) | (_, _) -> ( - let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in - let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in + let partialresreg1, m, _partialresvar1 = + RegisterMap.get_fresh_register m + in + let partialresreg2, m, _partialresvar2 = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a1 m convertedcode partialresreg1 in let convertedcode, m = c_ss_sa a2 m convertedcode partialresreg2 in - (convertedcode @ [BRegOp (More, partialresreg1, partialresreg2, register)], m) + (convertedcode @ + [BRegOp (More, partialresreg1, partialresreg2, register)], m) ) ) | SimpleBCmpGreaterEq (a1, a2) -> ( @@ -400,12 +472,16 @@ and c_ss_sb (convertedcode @ [BImmOp (MoreEqI, xreg, i, register)], m) ) | (SimpleInteger (i), a) -> ( - let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let partialresreg, m, _partialresvar = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in (convertedcode @ [BImmOp (LessEqI, partialresreg, i, register)], m) ) | (a, SimpleInteger (i)) -> ( - let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let partialresreg, m, _partialresvar = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in (convertedcode @ [BImmOp (MoreEqI, partialresreg, i, register)], m) ) @@ -416,22 +492,31 @@ and c_ss_sb ) | (SimpleVariable (x), a) -> ( let xreg, m = RegisterMap.get_or_set_register x m in - let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let partialresreg, m, _partialresvar = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in (convertedcode @ [BRegOp (MoreEq, xreg, partialresreg, register)], m) ) | (a, SimpleVariable (x)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in - let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let partialresreg, m, _partialresvar = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in (convertedcode @ [BRegOp (MoreEq, partialresreg, xreg, register)], m) ) | (_, _) -> ( - let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in - let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in + let partialresreg1, m, _partialresvar1 = + RegisterMap.get_fresh_register m + in + let partialresreg2, m, _partialresvar2 = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a1 m convertedcode partialresreg1 in let convertedcode, m = c_ss_sa a2 m convertedcode partialresreg2 in - (convertedcode @ [BRegOp (MoreEq, partialresreg1, partialresreg2, register)], m) + (convertedcode @ + [BRegOp (MoreEq, partialresreg1, partialresreg2, register)], m) ) ) @@ -461,7 +546,9 @@ and c_ss_sa ) | (SimpleInteger (i), a) | (a, SimpleInteger (i)) -> ( - let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let partialresreg, m, _partialresvar = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in (convertedcode @ [BImmOp (AddI, partialresreg, i, register)], m) ) @@ -473,37 +560,56 @@ and c_ss_sa | (SimpleVariable (x), a) | (a, SimpleVariable (x)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in - let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let partialresreg, m, _partialresvar = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in (convertedcode @ [BRegOp (Add, partialresreg, xreg, register)], m) ) | (_, _) -> ( - let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in - let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in + let partialresreg1, m, _partialresvar1 = + RegisterMap.get_fresh_register m + in + let partialresreg2, m, _partialresvar2 = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a1 m convertedcode partialresreg1 in let convertedcode, m = c_ss_sa a2 m convertedcode partialresreg2 in - (convertedcode @ [BRegOp (Add, partialresreg1, partialresreg2, register)], m) + (convertedcode @ + [BRegOp (Add, partialresreg1, partialresreg2, register)], m) ) ) | SimpleMinus (a1, a2) -> ( match (a1, a2) with | (SimpleInteger (i), SimpleVariable (x)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in - let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in - (convertedcode @ [LoadI (i, partialresreg); BRegOp (Sub, partialresreg, xreg, register)], m) + let partialresreg, m, _partialresvar = + RegisterMap.get_fresh_register m + in + (convertedcode @ + [LoadI (i, partialresreg); + BRegOp (Sub, partialresreg, xreg, register)], m) ) | (SimpleVariable (x), SimpleInteger (i)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in (convertedcode @ [BImmOp (SubI, xreg, i, register)], m) ) | (SimpleInteger (i), a) -> ( - let partialresregi, m, _partialresvari = RegisterMap.get_fresh_register m in - let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let partialresregi, m, _partialresvari = + RegisterMap.get_fresh_register m + in + let partialresreg, m, _partialresvar = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in - (convertedcode @ [LoadI (i, partialresregi); BRegOp (Sub, partialresregi, partialresreg, register)], m) + (convertedcode @ + [LoadI (i, partialresregi); + BRegOp (Sub, partialresregi, partialresreg, register)], m) ) | (a, SimpleInteger (i)) -> ( - let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let partialresreg, m, _partialresvar = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in (convertedcode @ [BImmOp (SubI, partialresreg, i, register)], m) ) @@ -514,22 +620,31 @@ and c_ss_sa ) | (SimpleVariable (x), a) -> ( let xreg, m = RegisterMap.get_or_set_register x m in - let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let partialresreg, m, _partialresvar = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in (convertedcode @ [BRegOp (Sub, xreg, partialresreg, register)], m) ) | (a, SimpleVariable (x)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in - let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let partialresreg, m, _partialresvar = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in (convertedcode @ [BRegOp (Sub, partialresreg, xreg, register)], m) ) | (_, _) -> ( - let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in - let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in + let partialresreg1, m, _partialresvar1 = + RegisterMap.get_fresh_register m + in + let partialresreg2, m, _partialresvar2 = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a1 m convertedcode partialresreg1 in let convertedcode, m = c_ss_sa a2 m convertedcode partialresreg2 in - (convertedcode @ [BRegOp (Sub, partialresreg1, partialresreg2, register)], m) + (convertedcode @ + [BRegOp (Sub, partialresreg1, partialresreg2, register)], m) ) ) | SimpleTimes (a1, a2) -> ( @@ -541,7 +656,9 @@ and c_ss_sa ) | (SimpleInteger (i), a) | (a, SimpleInteger (i)) -> ( - let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let partialresreg, m, _partialresvar = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in (convertedcode @ [BImmOp (MultI, partialresreg, i, register)], m) ) @@ -553,37 +670,56 @@ and c_ss_sa | (SimpleVariable (x), a) | (a, SimpleVariable (x)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in - let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let partialresreg, m, _partialresvar = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in (convertedcode @ [BRegOp (Mult, partialresreg, xreg, register)], m) ) | (_, _) -> ( - let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in - let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in + let partialresreg1, m, _partialresvar1 = + RegisterMap.get_fresh_register m + in + let partialresreg2, m, _partialresvar2 = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a1 m convertedcode partialresreg1 in let convertedcode, m = c_ss_sa a2 m convertedcode partialresreg2 in - (convertedcode @ [BRegOp (Mult, partialresreg1, partialresreg2, register)], m) + (convertedcode @ + [BRegOp (Mult, partialresreg1, partialresreg2, register)], m) ) ) | SimpleDivision (a1, a2) -> ( match (a1, a2) with | (SimpleInteger (i), SimpleVariable (x)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in - let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in - (convertedcode @ [LoadI (i, partialresreg); BRegOp (Div, partialresreg, xreg, register)], m) + let partialresreg, m, _partialresvar = + RegisterMap.get_fresh_register m + in + (convertedcode @ + [LoadI (i, partialresreg); + BRegOp (Div, partialresreg, xreg, register)], m) ) | (SimpleVariable (x), SimpleInteger (i)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in (convertedcode @ [BImmOp (DivI, xreg, i, register)], m) ) | (SimpleInteger (i), a) -> ( - let partialresregi, m, _partialresvari = RegisterMap.get_fresh_register m in - let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let partialresregi, m, _partialresvari = + RegisterMap.get_fresh_register m + in + let partialresreg, m, _partialresvar = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in - (convertedcode @ [LoadI (i, partialresregi); BRegOp (Div, partialresregi, partialresreg, register)], m) + (convertedcode @ + [LoadI (i, partialresregi); + BRegOp (Div, partialresregi, partialresreg, register)], m) ) | (a, SimpleInteger (i)) -> ( - let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let partialresreg, m, _partialresvar = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in (convertedcode @ [BImmOp (DivI, partialresreg, i, register)], m) ) @@ -594,43 +730,64 @@ and c_ss_sa ) | (SimpleVariable (x), a) -> ( let xreg, m = RegisterMap.get_or_set_register x m in - let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let partialresreg, m, _partialresvar = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in (convertedcode @ [BRegOp (Div, xreg, partialresreg, register)], m) ) | (a, SimpleVariable (x)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in - let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let partialresreg, m, _partialresvar = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in (convertedcode @ [BRegOp (Div, partialresreg, xreg, register)], m) ) | (_, _) -> ( - let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in - let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in + let partialresreg1, m, _partialresvar1 = + RegisterMap.get_fresh_register m + in + let partialresreg2, m, _partialresvar2 = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a1 m convertedcode partialresreg1 in let convertedcode, m = c_ss_sa a2 m convertedcode partialresreg2 in - (convertedcode @ [BRegOp (Div, partialresreg1, partialresreg2, register)], m) + (convertedcode @ + [BRegOp (Div, partialresreg1, partialresreg2, register)], m) ) ) | SimpleModulo (a1, a2) -> ( match (a1, a2) with | (SimpleInteger (i), SimpleVariable (x)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in - let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in - (convertedcode @ [LoadI (i, partialresreg); BRegOp (Mod, partialresreg, xreg, register)], m) + let partialresreg, m, _partialresvar = + RegisterMap.get_fresh_register m + in + (convertedcode @ + [LoadI (i, partialresreg); + BRegOp (Mod, partialresreg, xreg, register)], m) ) | (SimpleVariable (x), SimpleInteger (i)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in (convertedcode @ [BImmOp (ModI, xreg, i, register)], m) ) | (SimpleInteger (i), a) -> ( - let partialresregi, m, _partialresvari = RegisterMap.get_fresh_register m in - let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let partialresregi, m, _partialresvari = + RegisterMap.get_fresh_register m + in + let partialresreg, m, _partialresvar = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in - (convertedcode @ [LoadI (i, partialresregi); BRegOp (Mod, partialresregi, partialresreg, register)], m) + (convertedcode @ + [LoadI (i, partialresregi); + BRegOp (Mod, partialresregi, partialresreg, register)], m) ) | (a, SimpleInteger (i)) -> ( - let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let partialresreg, m, _partialresvar = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in (convertedcode @ [BImmOp (ModI, partialresreg, i, register)], m) ) @@ -641,43 +798,64 @@ and c_ss_sa ) | (SimpleVariable (x), a) -> ( let xreg, m = RegisterMap.get_or_set_register x m in - let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let partialresreg, m, _partialresvar = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in (convertedcode @ [BRegOp (Mod, xreg, partialresreg, register)], m) ) | (a, SimpleVariable (x)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in - let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let partialresreg, m, _partialresvar = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in (convertedcode @ [BRegOp (Mod, partialresreg, xreg, register)], m) ) | (_, _) -> ( - let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in - let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in + let partialresreg1, m, _partialresvar1 = + RegisterMap.get_fresh_register m + in + let partialresreg2, m, _partialresvar2 = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a1 m convertedcode partialresreg1 in let convertedcode, m = c_ss_sa a2 m convertedcode partialresreg2 in - (convertedcode @ [BRegOp (Mod, partialresreg1, partialresreg2, register)], m) + (convertedcode @ + [BRegOp (Mod, partialresreg1, partialresreg2, register)], m) ) ) | SimplePower (a1, a2) -> ( match (a1, a2) with | (SimpleInteger (i), SimpleVariable (x)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in - let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in - (convertedcode @ [LoadI (i, partialresreg); BRegOp (Pow, partialresreg, xreg, register)], m) + let partialresreg, m, _partialresvar = + RegisterMap.get_fresh_register m + in + (convertedcode @ + [LoadI (i, partialresreg); + BRegOp (Pow, partialresreg, xreg, register)], m) ) | (SimpleVariable (x), SimpleInteger (i)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in (convertedcode @ [BImmOp (PowI, xreg, i, register)], m) ) | (SimpleInteger (i), a) -> ( - let partialresregi, m, _partialresvari = RegisterMap.get_fresh_register m in - let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let partialresregi, m, _partialresvari = + RegisterMap.get_fresh_register m + in + let partialresreg, m, _partialresvar = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in - (convertedcode @ [LoadI (i, partialresregi); BRegOp (Pow, partialresregi, partialresreg, register)], m) + (convertedcode @ + [LoadI (i, partialresregi); + BRegOp (Pow, partialresregi, partialresreg, register)], m) ) | (a, SimpleInteger (i)) -> ( - let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let partialresreg, m, _partialresvar = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in (convertedcode @ [BImmOp (PowI, partialresreg, i, register)], m) ) @@ -688,22 +866,31 @@ and c_ss_sa ) | (SimpleVariable (x), a) -> ( let xreg, m = RegisterMap.get_or_set_register x m in - let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let partialresreg, m, _partialresvar = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in (convertedcode @ [BRegOp (Pow, xreg, partialresreg, register)], m) ) | (a, SimpleVariable (x)) -> ( let xreg, m = RegisterMap.get_or_set_register x m in - let partialresreg, m, _partialresvar = RegisterMap.get_fresh_register m in + let partialresreg, m, _partialresvar = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a m convertedcode partialresreg in (convertedcode @ [BRegOp (Pow, partialresreg, xreg, register)], m) ) | (_, _) -> ( - let partialresreg1, m, _partialresvar1 = RegisterMap.get_fresh_register m in - let partialresreg2, m, _partialresvar2 = RegisterMap.get_fresh_register m in + let partialresreg1, m, _partialresvar1 = + RegisterMap.get_fresh_register m + in + let partialresreg2, m, _partialresvar2 = + RegisterMap.get_fresh_register m + in let convertedcode, m = c_ss_sa a1 m convertedcode partialresreg1 in let convertedcode, m = c_ss_sa a2 m convertedcode partialresreg2 in - (convertedcode @ [BRegOp (Pow, partialresreg1, partialresreg2, register)], m) + (convertedcode @ + [BRegOp (Pow, partialresreg1, partialresreg2, register)], m) ) ) | SimpleRand (a) -> ( @@ -742,17 +929,17 @@ let helper risccode let convert (prg: CfgImp.SSCfg.t) : RISCCfg.t = - match prg with - { empty: bool; - nodes: Cfg.NodeSet.t; - edges: (Cfg.Node.t * (Cfg.Node.t option)) Cfg.NodeMap.t; - reverseEdges: (Cfg.Node.t list) Cfg.NodeMap.t; - inputVal: int option; - inputOutputVar: (string * string) option; - initial: Cfg.Node.t option; - terminal: Cfg.Node.t option; - content: CfgImp.SimpleStatements.t list Cfg.NodeMap.t - } -> + let ({ empty: bool; + nodes: Cfg.NodeSet.t; + edges: (Cfg.Node.t * (Cfg.Node.t option)) Cfg.NodeMap.t; + reverseEdges: (Cfg.Node.t list) Cfg.NodeMap.t; + inputVal: int option; + inputOutputVar: (string * string) option; + initial: Cfg.Node.t option; + terminal: Cfg.Node.t option; + content: CfgImp.SimpleStatements.t list Cfg.NodeMap.t + }: CfgImp.SSCfg.t) = prg + in let initial_bindings = match inputOutputVar with | Some (i, o) -> ( diff --git a/lib/miniImp/RISC.ml b/lib/miniImp/RISC.ml index 2858078..e694bc0 100644 --- a/lib/miniImp/RISC.ml +++ b/lib/miniImp/RISC.ml @@ -66,16 +66,27 @@ module RISCAssembly = struct let pp_risci (ppf: out_channel) (v: risci) : unit = let rec pp_risci (ppf: out_channel) (v: risci) : unit = match v with - Nop -> Printf.fprintf ppf "\tNop\n" - | BRegOp (b, r1, r2, r3) -> Printf.fprintf ppf "\t%a r%s r%s => r%s\n" pp_brop b r1.index r2.index r3.index - | BImmOp (b, r1, i, r3) -> Printf.fprintf ppf "\t%a r%s %d => r%s\n" pp_biop b r1.index i r3.index - | URegOp (u, r1, r2) -> Printf.fprintf ppf "\t%a r%s => r%s\n" pp_urop u r1.index r2.index - | Load (r1, r2) -> Printf.fprintf ppf "\tLoad r%s => r%s\n" r1.index r2.index - | LoadI (i, r2) -> Printf.fprintf ppf "\tLoadI %d => r%s\n" i r2.index - | Store (r1, r2) -> Printf.fprintf ppf "\tStore r%s => r%s\n" r1.index r2.index - | Jump (label) -> Printf.fprintf ppf "\tJump %s\n" label - | CJump (r, l1, l2) -> Printf.fprintf ppf "\tCJump r%s => %s, %s\n" r.index l1 l2 - | Label (label) -> Printf.fprintf ppf "%s:" label + | Nop -> + Printf.fprintf ppf "\tNop\n" + | BRegOp (b, r1, r2, r3) -> + Printf.fprintf ppf "\t%a r%s r%s => r%s\n" + pp_brop b r1.index r2.index r3.index + | BImmOp (b, r1, i, r3) -> + Printf.fprintf ppf "\t%a r%s %d => r%s\n" pp_biop b r1.index i r3.index + | URegOp (u, r1, r2) -> + Printf.fprintf ppf "\t%a r%s => r%s\n" pp_urop u r1.index r2.index + | Load (r1, r2) -> + Printf.fprintf ppf "\tLoad r%s => r%s\n" r1.index r2.index + | LoadI (i, r2) -> + Printf.fprintf ppf "\tLoadI %d => r%s\n" i r2.index + | Store (r1, r2) -> + Printf.fprintf ppf "\tStore r%s => r%s\n" r1.index r2.index + | Jump (label) -> + Printf.fprintf ppf "\tJump %s\n" label + | CJump (r, l1, l2) -> + Printf.fprintf ppf "\tCJump r%s => %s, %s\n" r.index l1 l2 + | Label (label) -> + Printf.fprintf ppf "%s:" label and pp_brop (ppf: out_channel) (v: brop) : unit = match v with Add -> Printf.fprintf ppf "Add" @@ -121,14 +132,18 @@ module RISCAssembly = struct | Some i -> Printf.fprintf ppf "Some %d\n" i ); Printf.fprintf ppf "Input/Output Registers: "; ( match t.inputoutputreg with - None -> Printf.fprintf ppf "None\n" - | Some (i, o) -> Printf.fprintf ppf "[i: Some r%s, o: Some r%s]\n" i.index o.index); + | None -> + Printf.fprintf ppf "None\n" + | Some (i, o) -> + Printf.fprintf ppf "[i: Some r%s, o: Some r%s]\n" i.index o.index); Printf.fprintf ppf "Code:\n"; List.iter (pp_risci ppf) t.code end -let convert_cfgrisc_risci (i: CfgRISC.RISCSimpleStatements.t list) : (RISCAssembly.risci list) = - let rec helper (i: CfgRISC.RISCSimpleStatements.t) : RISCAssembly.risci = +let convert_cfgrisc_risci (i: CfgRISC.RISCSimpleStatements.t list) : + (RISCAssembly.risci list) = + let rec helper (i: CfgRISC.RISCSimpleStatements.t) + : RISCAssembly.risci = match i with | Nop -> Nop | BRegOp (brop, r1, r2, r3) -> BRegOp (helper_brop brop, @@ -148,7 +163,8 @@ let convert_cfgrisc_risci (i: CfgRISC.RISCSimpleStatements.t list) : (RISCAssemb {index = r3.index}) | Store (r1, r3) -> Store ({index = r1.index}, {index = r3.index}) - and helper_brop (brop: CfgRISC.RISCSimpleStatements.brop) : RISCAssembly.brop = + and helper_brop (brop: CfgRISC.RISCSimpleStatements.brop) + : RISCAssembly.brop = match brop with | Add -> Add | Sub -> Sub @@ -163,7 +179,8 @@ let convert_cfgrisc_risci (i: CfgRISC.RISCSimpleStatements.t list) : (RISCAssemb | LessEq -> LessEq | More -> More | MoreEq -> MoreEq - and helper_biop (biop: CfgRISC.RISCSimpleStatements.biop) : RISCAssembly.biop = + and helper_biop (biop: CfgRISC.RISCSimpleStatements.biop) + : RISCAssembly.biop = match biop with | AddI -> AddI | SubI -> SubI @@ -178,7 +195,8 @@ let convert_cfgrisc_risci (i: CfgRISC.RISCSimpleStatements.t list) : (RISCAssemb | LessEqI -> LessEqI | MoreI -> MoreI | MoreEqI -> MoreEqI - and helper_urop (urop: CfgRISC.RISCSimpleStatements.urop) : RISCAssembly.urop = + and helper_urop (urop: CfgRISC.RISCSimpleStatements.urop) + : RISCAssembly.urop = match urop with | Not -> Not | Copy -> Copy @@ -186,7 +204,11 @@ let convert_cfgrisc_risci (i: CfgRISC.RISCSimpleStatements.t list) : (RISCAssemb in List.map helper i -let nextCommonSuccessor (prg: CfgRISC.RISCCfg.t) (node1: Cfg.Node.t) (node2: Cfg.Node.t) : Cfg.Node.t option = +let nextCommonSuccessor + (prg: CfgRISC.RISCCfg.t) + (node1: Cfg.Node.t) + (node2: Cfg.Node.t) + : Cfg.Node.t option = (* Assume the two input nodes are the two branches of an if then else statement, then create the two lists that represent the runs until the terminal node by choosing always the false statement in guard statements @@ -244,8 +266,12 @@ let rec helper let label2 = nextLabel () in let label3 = nextLabel () in - let res1, _ = (helper prg nextnode1 (currentnode :: nextnode2 :: alreadyVisited)) in - let res2, vis2 = (helper prg nextnode2 (currentnode :: nextnode1 :: alreadyVisited)) in + let res1, _ = + (helper prg nextnode1 + (currentnode :: nextnode2 :: alreadyVisited)) in + let res2, vis2 = + (helper prg nextnode2 + (currentnode :: nextnode1 :: alreadyVisited)) in match List.nth currentcode ((List.length currentcode) - 1) with | BRegOp (_, _, _, r) @@ -253,11 +279,14 @@ let rec helper | URegOp (_, _, r) | Load (_, r) | Store (r, _) - | LoadI (_, r) -> (([Label label1] : RISCAssembly.risci list) @ + | LoadI (_, r) -> (([Label label1] + : RISCAssembly.risci list) @ currentcode @ - ([CJump (r, label2, label3); Label label2] : RISCAssembly.risci list) @ + ([CJump (r, label2, label3); Label label2] + : RISCAssembly.risci list) @ res1 @ - ([Jump label1; Label label3] : RISCAssembly.risci list) @ + ([Jump label1; Label label3] + : RISCAssembly.risci list) @ res2 , vis2) | _ -> failwith "Missing instruction at branch" @@ -266,7 +295,8 @@ let rec helper let label2 = nextLabel () in let label3 = nextLabel () in - let res1, vis1 = (helper prg nextnode1 (currentnode :: ncs :: alreadyVisited)) in + let res1, vis1 = + (helper prg nextnode1 (currentnode :: ncs :: alreadyVisited)) in let res2, _ = (helper prg nextnode2 vis1) in let res3, vis3 = (helper prg ncs (currentnode :: alreadyVisited)) in match List.nth currentcode ((List.length currentcode) - 1) with @@ -276,11 +306,14 @@ let rec helper | Load (_, r) | Store (r, _) | LoadI (_, r) -> (currentcode @ - ([CJump (r, label1, label2); Label label1] : RISCAssembly.risci list) @ + ([CJump (r, label1, label2); Label label1] + : RISCAssembly.risci list) @ res1 @ - ([Jump label3; Label label2] : RISCAssembly.risci list) @ + ([Jump label3; Label label2] + : RISCAssembly.risci list) @ res2 @ - ([Label label3] : RISCAssembly.risci list) @ + ([Label label3] + : RISCAssembly.risci list) @ res3 , vis3) | _ -> failwith "Missing instruction at branch" diff --git a/lib/miniImp/RISCSemantics.ml b/lib/miniImp/RISCSemantics.ml index cec0683..d01414d 100644 --- a/lib/miniImp/RISCSemantics.ml +++ b/lib/miniImp/RISCSemantics.ml @@ -18,7 +18,8 @@ module RISCArchitecture = struct } end -let convert (prg: RISC.RISCAssembly.t) : RISC.RISCAssembly.risci list CodeMap.t = +let convert (prg: RISC.RISCAssembly.t) + : RISC.RISCAssembly.risci list CodeMap.t = (* takes as input a sequence of RISC commands and computes a map to the right labels for easier execution *) let rec helper @@ -101,7 +102,9 @@ let reduce_instructions (prg: RISCArchitecture.t) (lo: string list) : int = | Some i -> if i + 1 < (List.length lo) then helper - prg (CodeMap.find (List.nth lo (i+1)) prg.code) (List.nth lo (i+1)) + prg + (CodeMap.find (List.nth lo (i+1)) prg.code) + (List.nth lo (i+1)) else prg ) @@ -113,7 +116,7 @@ let reduce_instructions (prg: RISCArchitecture.t) (lo: string list) : int = (RegisterMap.find {index = r2.index} prg.registers) in helper { prg with - registers = RegisterMap.add {index = r3.index} n prg.registers } + registers = RegisterMap.add {index = r3.index} n prg.registers} tl current_label ) | BImmOp (biop, r1, i, r3) :: tl -> ( @@ -122,7 +125,7 @@ let reduce_instructions (prg: RISCArchitecture.t) (lo: string list) : int = i in helper { prg with - registers = RegisterMap.add {index = r3.index} n prg.registers } + registers = RegisterMap.add {index = r3.index} n prg.registers} tl current_label ) | URegOp (urop, r1, r3) :: tl -> ( @@ -160,20 +163,23 @@ let reduce_instructions (prg: RISCArchitecture.t) (lo: string list) : int = prg.memory in helper { prg with - registers = RegisterMap.add {index = r3.index} n prg.registers } + registers = RegisterMap.add {index = r3.index} n prg.registers} tl current_label ) | LoadI (i, r3) :: tl -> ( let n = i in helper { prg with - registers = RegisterMap.add {index = r3.index} n prg.registers } + registers = RegisterMap.add {index = r3.index} n prg.registers} tl current_label ) | Store (r1, r3) :: tl -> ( let n = RegisterMap.find {index = r1.index} prg.registers in let n1 = RegisterMap.find {index = r3.index} prg.registers in - helper {prg with memory = MemoryMap.add n1 n prg.memory} tl current_label + helper + { prg with memory = MemoryMap.add n1 n prg.memory } + tl + current_label ) | Jump l :: _ -> helper prg (CodeMap.find l prg.code) l | CJump (r, l1, l2) :: _ -> ( diff --git a/lib/miniImp/Semantics.ml b/lib/miniImp/Semantics.ml index b783715..7884884 100644 --- a/lib/miniImp/Semantics.ml +++ b/lib/miniImp/Semantics.ml @@ -53,8 +53,10 @@ and evaluate_a (mem: memory) (exp_a: a_exp) : (int, [> error]) result = match exp_a with Variable v -> ( match VariableMap.find_opt v mem.assignments with - None -> Error (`AbsentAssignment ("The variable " ^ v ^ " is not defined.")) - | Some a -> Ok a + | None -> + Error (`AbsentAssignment ("The variable " ^ v ^ " is not defined.")) + | Some a -> + Ok a ) | Integer n -> Ok n | Plus (exp_a1, exp_a2) -> ( @@ -148,9 +150,13 @@ and evaluate_b (mem: memory) (exp_b: b_exp) : (bool, [> error]) result = let reduce (program: p_exp) (iin : int) : (int, [> error]) result = match program with Main (vin, vout, expression) -> ( - let mem : memory = {assignments = (VariableMap.empty |> VariableMap.add vin iin)} in + let mem : memory = + { assignments = (VariableMap.empty |> VariableMap.add vin iin) } in let* resultmem : memory = evaluate mem expression in match VariableMap.find_opt vout resultmem.assignments with - None -> Error (`AbsentAssignment ("The output variable is not defined (" ^ vout ^ ")")) - | Some a -> Ok a + | None -> + Error (`AbsentAssignment + ("The output variable is not defined (" ^ vout ^ ")")) + | Some a -> + Ok a ) diff --git a/lib/miniImp/Types.ml b/lib/miniImp/Types.ml index 950f5ab..7762cfb 100644 --- a/lib/miniImp/Types.ml +++ b/lib/miniImp/Types.ml @@ -1,72 +1,106 @@ type variable = string type p_exp = - Main of variable * variable * c_exp (* def main with input x output y as c *) + Main of variable * variable * c_exp + (* def main with input x output y as c *) and c_exp = Skip - | Assignment of variable * a_exp (* x := a *) - | Sequence of c_exp * c_exp (* c; c *) - | If of b_exp * c_exp * c_exp (* if b then c else c *) - | While of b_exp * c_exp (* while b do c *) - | For of c_exp * b_exp * c_exp * c_exp (* for (c; b; c) do c *) + | Assignment of variable * a_exp (* x := a *) + | Sequence of c_exp * c_exp (* c; c *) + | If of b_exp * c_exp * c_exp (* if b then c else c *) + | While of b_exp * c_exp (* while b do c *) + | For of c_exp * b_exp * c_exp * c_exp + (* for (c; b; c) do c *) and b_exp = - Boolean of bool (* v *) - | BAnd of b_exp * b_exp (* b && b *) - | BOr of b_exp * b_exp (* b || b *) - | BNot of b_exp (* not b *) - | BCmp of a_exp * a_exp (* a == a *) - | BCmpLess of a_exp * a_exp (* a < a *) - | BCmpLessEq of a_exp * a_exp (* a <= a *) - | BCmpGreater of a_exp * a_exp (* a > a *) - | BCmpGreaterEq of a_exp * a_exp (* a >= a *) + Boolean of bool (* v *) + | BAnd of b_exp * b_exp (* b && b *) + | BOr of b_exp * b_exp (* b || b *) + | BNot of b_exp (* not b *) + | BCmp of a_exp * a_exp (* a == a *) + | BCmpLess of a_exp * a_exp (* a < a *) + | BCmpLessEq of a_exp * a_exp (* a <= a *) + | BCmpGreater of a_exp * a_exp (* a > a *) + | BCmpGreaterEq of a_exp * a_exp (* a >= a *) and a_exp = - Variable of variable (* x *) - | Integer of int (* n *) - | Plus of a_exp * a_exp (* a + a *) - | Minus of a_exp * a_exp (* a - a *) - | Times of a_exp * a_exp (* a * a *) - | Division of a_exp * a_exp (* a / a *) - | Modulo of a_exp * a_exp (* a % a *) - | Power of a_exp * a_exp (* a ^ a *) - | PowerMod of a_exp * a_exp * a_exp (* a ^ a % a *) - | Rand of a_exp (* rand(0, a) *) + Variable of variable (* x *) + | Integer of int (* n *) + | Plus of a_exp * a_exp (* a + a *) + | Minus of a_exp * a_exp (* a - a *) + | Times of a_exp * a_exp (* a * a *) + | Division of a_exp * a_exp (* a / a *) + | Modulo of a_exp * a_exp (* a % a *) + | Power of a_exp * a_exp (* a ^ a *) + | PowerMod of a_exp * a_exp * a_exp (* a ^ a % a *) + | Rand of a_exp (* rand(0, a) *) let pp_p_exp (ppf: Format.formatter) (p: p_exp) : unit = + let open Format in let rec helper_c (ppf) (c: c_exp) : unit = match c with - Skip -> Format.fprintf ppf "Skip" - | Assignment (x, a) -> Format.fprintf ppf "%S := @[%a@]" x helper_a a - | Sequence (c1, c2) -> Format.fprintf ppf "@[Sequence (@;<1 2>%a,@;<1 0>%a@;<0 0>)@]" helper_c c1 helper_c c2 - | If (b, c1, c2) -> Format.fprintf ppf "@[If @[%a@]@;<1 2>then (@[%a@])@;<1 2>else (@[%a@])@]" helper_b b helper_c c1 helper_c c2 - | While (b, c) -> Format.fprintf ppf "@[While @[%a@] do@;<1 2>%a@]@;<0 0>" helper_b b helper_c c - | For (c1, b, c2, c3) -> Format.fprintf ppf "@[For (@;<0 2>%a,@;<1 2>@[%a@],@;<1 2>%a) do@]@;<1 4>%a@;<0 0>" helper_c c1 helper_b b helper_c c2 helper_c c3 + | Skip -> + fprintf ppf "Skip" + | Assignment (x, a) -> + fprintf ppf "%S := @[%a@]" x helper_a a + | Sequence (c1, c2) -> + fprintf ppf "@[Sequence (@;<1 2>%a,@;<1 0>%a@;<0 0>)@]" + helper_c c1 helper_c c2 + | If (b, c1, c2) -> + fprintf ppf + "@[If @[%a@]@;<1 2>then (@[%a@])@;<1 2>else (@[%a@])@]" + helper_b b helper_c c1 helper_c c2 + | While (b, c) -> + fprintf ppf "@[While @[%a@] do@;<1 2>%a@]@;<0 0>" + helper_b b helper_c c + | For (c1, b, c2, c3) -> + fprintf ppf + "@[For (@;<0 2>%a,@;<1 2>@[%a@],@;<1 2>%a) do@]@;<1 4>%a@;<0 0>" + helper_c c1 helper_b b helper_c c2 helper_c c3 and helper_b (ppf) (b: b_exp) = match b with - Boolean (b) -> Format.fprintf ppf "%b" b - | BAnd (b1, b2) -> Format.fprintf ppf "(%a &&@;<1 2>%a)" helper_b b1 helper_b b2 - | BOr (b1, b2) -> Format.fprintf ppf "(%a ||@;<1 2>%a)" helper_b b1 helper_b b2 - | BNot (b) -> Format.fprintf ppf "(not %a)" helper_b b - | BCmp (a1, a2) -> Format.fprintf ppf "(%a ==@;<1 2>%a)" helper_a a1 helper_a a2 - | BCmpLess (a1, a2) -> Format.fprintf ppf "(%a <@;<1 2>%a)" helper_a a1 helper_a a2 - | BCmpLessEq (a1, a2) -> Format.fprintf ppf "(%a <=@;<1 2>%a)" helper_a a1 helper_a a2 - | BCmpGreater (a1, a2) -> Format.fprintf ppf "(%a >@;<1 2>%a)" helper_a a1 helper_a a2 - | BCmpGreaterEq (a1, a2) -> Format.fprintf ppf "(%a >=@;<1 2>%a)" helper_a a1 helper_a a2 + | Boolean (b) -> + fprintf ppf "%b" b + | BAnd (b1, b2) -> + fprintf ppf "(%a &&@;<1 2>%a)" helper_b b1 helper_b b2 + | BOr (b1, b2) -> + fprintf ppf "(%a ||@;<1 2>%a)" helper_b b1 helper_b b2 + | BNot (b) -> + fprintf ppf "(not %a)" helper_b b + | BCmp (a1, a2) -> + fprintf ppf "(%a ==@;<1 2>%a)" helper_a a1 helper_a a2 + | BCmpLess (a1, a2) -> + fprintf ppf "(%a <@;<1 2>%a)" helper_a a1 helper_a a2 + | BCmpLessEq (a1, a2) -> + fprintf ppf "(%a <=@;<1 2>%a)" helper_a a1 helper_a a2 + | BCmpGreater (a1, a2) -> + fprintf ppf "(%a >@;<1 2>%a)" helper_a a1 helper_a a2 + | BCmpGreaterEq (a1, a2) -> + fprintf ppf "(%a >=@;<1 2>%a)" helper_a a1 helper_a a2 and helper_a (ppf) (a: a_exp) = match a with - Variable v -> Format.fprintf ppf "%S" v - | Integer n -> Format.fprintf ppf "%i" n - | Plus (a1, a2) -> Format.fprintf ppf "%a +@;<1 2>%a" helper_a a1 helper_a a2 - | Minus (a1, a2) -> Format.fprintf ppf "%a -@;<1 2>%a" helper_a a1 helper_a a2 - | Times (a1, a2) -> Format.fprintf ppf "%a *@;<1 2>%a" helper_a a1 helper_a a2 - | Division (a1, a2) -> Format.fprintf ppf "%a /@;<1 2>%a" helper_a a1 helper_a a2 - | Modulo (a1, a2) -> Format.fprintf ppf "%a %%@;<1 2>%a" helper_a a1 helper_a a2 - | Power (a1, a2) -> Format.fprintf ppf "(%a ^@;<1 2>%a)" helper_a a1 helper_a a2 - | PowerMod (a1, a2, a3) -> Format.fprintf ppf "(%a ^ %a %% %a)" helper_a a1 helper_a a2 helper_a a3 - | Rand (a) -> Format.fprintf ppf "Rand (%a)" helper_a a + | Variable v -> + fprintf ppf "%S" v + | Integer n -> + fprintf ppf "%i" n + | Plus (a1, a2) -> + fprintf ppf "%a +@;<1 2>%a" helper_a a1 helper_a a2 + | Minus (a1, a2) -> + fprintf ppf "%a -@;<1 2>%a" helper_a a1 helper_a a2 + | Times (a1, a2) -> + fprintf ppf "%a *@;<1 2>%a" helper_a a1 helper_a a2 + | Division (a1, a2) -> + fprintf ppf "%a /@;<1 2>%a" helper_a a1 helper_a a2 + | Modulo (a1, a2) -> + fprintf ppf "%a %%@;<1 2>%a" helper_a a1 helper_a a2 + | Power (a1, a2) -> + fprintf ppf "(%a ^@;<1 2>%a)" helper_a a1 helper_a a2 + | PowerMod (a1, a2, a3) -> + fprintf ppf "(%a ^ %a %% %a)" helper_a a1 helper_a a2 helper_a a3 + | Rand (a) -> + fprintf ppf "Rand (%a)" helper_a a in match p with | Main (i, o, exp) -> - Format.fprintf ppf "def main with (input %S) (output %S) as @.%a" i o helper_c exp + fprintf ppf "def main with (input %S) (output %S) as @.%a" i o helper_c exp module VariableMap = Map.Make(String) diff --git a/lib/miniImp/definedVariables.ml b/lib/miniImp/definedVariables.ml index 4ace70e..6e4b74f 100644 --- a/lib/miniImp/definedVariables.ml +++ b/lib/miniImp/definedVariables.ml @@ -114,8 +114,11 @@ let lub (t: DVCfg.t) (node: Cfg.Node.t) : DVCfg.internalnode = let newinternalout = match newinternalbetween with - | [] -> previnternalvar.internalin - | _ -> (snd (Utility.last_list newinternalbetween)) + | [] -> + previnternalvar.internalin + | _ -> + let _, newinternalout = (Utility.last_list newinternalbetween) in + newinternalout in { previnternalvar with @@ -186,7 +189,9 @@ let lucf (t: DVCfg.t) (node: Cfg.Node.t) : DVCfg.internalnode = let update (t: DVCfg.t) (node: Cfg.Node.t) : DVCfg.internalnode = - let newt = {t with internalvar = (Cfg.NodeMap.add node (lucf t node) t.internalvar)} in + let newt = + {t with internalvar = (Cfg.NodeMap.add node (lucf t node) t.internalvar)} + in lub newt node diff --git a/lib/miniImp/liveVariables.ml b/lib/miniImp/liveVariables.ml index a8b9ab6..35107df 100644 --- a/lib/miniImp/liveVariables.ml +++ b/lib/miniImp/liveVariables.ml @@ -239,7 +239,10 @@ let optimize_cfg (t: DVCfg.t) : DVCfg.t = ) in - let aux (assignments: Variable.t VariableMap.t) (t: DVCfg.t) (node: Cfg.Node.t) + let aux + (assignments: Variable.t VariableMap.t) + (t: DVCfg.t) + (node: Cfg.Node.t) : (Variable.t VariableMap.t * DVCfg.t) = let livevars = Cfg.NodeMap.find node t.internalvar in let code = @@ -298,7 +301,8 @@ let optimize_cfg (t: DVCfg.t) : DVCfg.t = is mirrored into internalbetween *) List.fold_left2 (fun acc (i, o) code -> - (* we also consider the out set if we "use" v as a guard *) + (* we also consider the out set if we "use" v as a + guard *) match List.mem v i, List.mem v o, List.mem v (variables_defined code) with diff --git a/lib/miniImp/reduceRegisters.ml b/lib/miniImp/reduceRegisters.ml index 5ebdf41..420778e 100644 --- a/lib/miniImp/reduceRegisters.ml +++ b/lib/miniImp/reduceRegisters.ml @@ -71,20 +71,20 @@ let reduceregisters (n: int) (cfg: RISCCfg.t) : RISCCfg.t = 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) - ) + 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 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) - ) + 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 (* replace each operation with a list of operations that have the new @@ -181,7 +181,7 @@ let reduceregisters (n: int) (cfg: RISCCfg.t) : RISCCfg.t = 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, @@ -274,7 +274,7 @@ let reduceregisters (n: int) (cfg: RISCCfg.t) : RISCCfg.t = Store (tmpreg2, tmpreg1)] | _ -> failwith ("ReduceRegisters: fail to partition a set, some" ^ " registers have no binding.") - ) + ) in List.map aux code |> List.concat @@ -289,14 +289,16 @@ let reduceregisters (n: int) (cfg: RISCCfg.t) : RISCCfg.t = 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 + 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*) - List.mapi (fun n v -> (v, (string_of_int (n+3): Variable.t))) most_frequent + 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 *) @@ -321,119 +323,120 @@ let reduceregisters (n: int) (cfg: RISCCfg.t) : RISCCfg.t = ) 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 - } - ) + if newcfg.inputOutputVar = None + then newcfg (* if no input or output variables we ignore *) + else + let i, o = Option.get newcfg.inputOutputVar in + 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 inputOutputVar = Some (i, o) } + | 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 inputOutputVar = Some (i, o)} + | 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 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 _, _, _, None -> + { newcfg with inputOutputVar = Some (i, o) } + | 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 + inputOutputVar = Some ("1", o); + content = content + } + ) + | None, None, Some _, Some _, None, None -> + { newcfg with inputOutputVar = Some (i, o) } + | 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 + inputOutputVar = Some (i, "2"); + 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 ) - | 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 - } - ) + 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, 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 ) - | 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 + 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 ( if List.length all_variables <= n diff --git a/lib/miniImp/replacePowerMod.ml b/lib/miniImp/replacePowerMod.ml index 030fe3e..3b37773 100644 --- a/lib/miniImp/replacePowerMod.ml +++ b/lib/miniImp/replacePowerMod.ml @@ -17,7 +17,10 @@ let rewrite_instructions (prg: Types.p_exp) : Types.p_exp = | _, Some a -> Some a ) | If (b, c1, c2) -> ( - match contains_rewrite_b b, contains_rewrite c1, contains_rewrite c2 with + match contains_rewrite_b b, + contains_rewrite c1, + contains_rewrite c2 + with | None, None, None -> None | Some a, _, _ | _, Some a, _ @@ -30,7 +33,11 @@ let rewrite_instructions (prg: Types.p_exp) : Types.p_exp = | _, Some a -> Some a ) | For (c1, b, c2, c3) -> ( - match contains_rewrite c1, contains_rewrite_b b, contains_rewrite c2, contains_rewrite c3 with + match contains_rewrite c1, + contains_rewrite_b b, + contains_rewrite c2, + contains_rewrite c3 + with | None, None, None, None -> None | Some a, _, _, _ | _, Some a, _, _ @@ -105,7 +112,11 @@ let rewrite_instructions (prg: Types.p_exp) : Types.p_exp = in (* functions that replace a pattern in a subexpression *) - let rec replace_occurrence_a (pattern: Types.a_exp) (replace: Types.a_exp) (a: Types.a_exp) : Types.a_exp = + let rec replace_occurrence_a + (pattern: Types.a_exp) + (replace: Types.a_exp) + (a: Types.a_exp) + : Types.a_exp = if a = pattern then replace else ( @@ -122,7 +133,11 @@ let rewrite_instructions (prg: Types.p_exp) : Types.p_exp = | PowerMod (a1, a2, a3) -> PowerMod (r_o_a a1, r_o_a a2, r_o_a a3) | Rand (a) -> Rand (r_o_a a) ) - and replace_occurrence_b (pattern: Types.a_exp) (replace: Types.a_exp) (b: Types.b_exp) : Types.b_exp = + and replace_occurrence_b + (pattern: Types.a_exp) + (replace: Types.a_exp) + (b: Types.b_exp) + : Types.b_exp = let r_o_b = replace_occurrence_b pattern replace in let r_o_a = replace_occurrence_a pattern replace in match b with @@ -197,7 +212,10 @@ let rewrite_instructions (prg: Types.p_exp) : Types.p_exp = let freshres = new_fresh_var "res" in Sequence ( partial freshres a1 a2 a3, - For (fora1, replace_occurrence_b pwm (Variable freshres) b, fora2, fora3) + For ( fora1, + replace_occurrence_b pwm (Variable freshres) b, + fora2, + fora3 ) ) | _ -> failwith "PowerMod is not present" in From 2fbbf4e4d1db0cad4faf58fb0358550a03bfe74e Mon Sep 17 00:00:00 2001 From: elvis Date: Mon, 27 Jan 2025 16:28:23 +0100 Subject: [PATCH 29/29] Style more consistent, replace capitalization with camel case --- bin/miniImpInterpreterReg.ml | 4 +- lib/analysis/Cfg.ml | 112 +++++++++++++------------- lib/analysis/Cfg.mli | 10 +-- lib/analysis/Dataflow.ml | 89 ++++++++++----------- lib/analysis/Dataflow.mli | 14 ++-- lib/miniFun/Semantics.ml | 14 ++-- lib/miniFun/Semantics.mli | 2 +- lib/miniFun/Types.ml | 8 +- lib/miniFun/Types.mli | 8 +- lib/miniImp/CfgImp.ml | 30 +++---- lib/miniImp/CfgImp.mli | 2 +- lib/miniImp/CfgRISC.ml | 22 +++--- lib/miniImp/CfgRISC.mli | 2 +- lib/miniImp/RISC.ml | 69 +++++++++------- lib/miniImp/RISCSemantics.ml | 48 +++++------ lib/miniImp/Semantics.ml | 4 +- lib/miniImp/Types.mli | 52 ++++++------ lib/miniImp/definedVariables.ml | 132 ++++++++++++++++--------------- lib/miniImp/definedVariables.mli | 2 +- lib/miniImp/liveVariables.ml | 95 +++++++++++----------- lib/miniImp/reduceRegisters.ml | 40 +++++----- lib/miniImp/reduceRegisters.mli | 2 +- test/testingAnalysis.ml | 2 +- 23 files changed, 390 insertions(+), 373 deletions(-) diff --git a/bin/miniImpInterpreterReg.ml b/bin/miniImpInterpreterReg.ml index a8b1a19..413decc 100644 --- a/bin/miniImpInterpreterReg.ml +++ b/bin/miniImpInterpreterReg.ml @@ -110,7 +110,7 @@ let () = | None -> () | Some l -> Printf.printf "Error: undefined variables: %a\n" - DefinedVariables.Variable.pplist l; + DefinedVariables.Variable.pp_list l; exit (-1) ) else (); @@ -126,7 +126,7 @@ let () = let return_value = return_value |> - ReduceRegisters.reduceregisters registers |> + ReduceRegisters.reduce_registers registers |> RISC.convert in diff --git a/lib/analysis/Cfg.ml b/lib/analysis/Cfg.ml index 74f678f..19d6fbc 100644 --- a/lib/analysis/Cfg.ml +++ b/lib/analysis/Cfg.ml @@ -1,7 +1,7 @@ module type PrintableType = sig type t val pp : out_channel -> t -> unit - val pplist : out_channel -> t list -> unit + val pp_list : out_channel -> t list -> unit end let globalIdNode = ref 0; @@ -33,9 +33,9 @@ type 'a cfginternal = { empty: bool; nodes: NodeSet.t; edges: (Node.t * (Node.t option)) NodeMap.t; - reverseEdges: (Node.t list) NodeMap.t; - inputVal: int option; - inputOutputVar: (string * string) option; + reverse_edges: (Node.t list) NodeMap.t; + input_val: int option; + input_output_var: (string * string) option; initial: Node.t option; terminal: Node.t option; content: 'a list NodeMap.t; @@ -48,7 +48,7 @@ module type C = sig val empty : t val merge : t -> t -> Node.t -> Node.t -> t val concat : t -> t -> t - val addToLastNode : elt -> t -> t + val add_to_last_node : elt -> t -> t val pp : out_channel -> t -> unit end @@ -61,43 +61,43 @@ module Make (M: PrintableType) = struct { empty = true; nodes = NodeSet.empty; edges = NodeMap.empty; - reverseEdges = NodeMap.empty; - inputVal = None; - inputOutputVar = None; + reverse_edges = NodeMap.empty; + input_val = None; + input_output_var = None; initial = None; terminal = None; content = NodeMap.empty } - let merge (cfg1: t) (cfg2: t) (entryNode: Node.t) (exitNode: Node.t) : t = + let merge (cfg1: t) (cfg2: t) (entry_node: Node.t) (exit_node: Node.t) : t = match (cfg1.empty, cfg2.empty) with true, _ -> cfg2 | _, true -> cfg1 | false, false -> - let cfg1initial = Option.get cfg1.initial in - let cfg2initial = Option.get cfg2.initial in - let cfg1terminal = Option.get cfg1.terminal in - let cfg2terminal = Option.get cfg2.terminal in + let cfg1_initial = Option.get cfg1.initial in + let cfg2_initial = Option.get cfg2.initial in + let cfg1_terminal = Option.get cfg1.terminal in + let cfg2_terminal = Option.get cfg2.terminal in { empty = false; nodes = NodeSet.union cfg1.nodes cfg2.nodes |> - NodeSet.add entryNode |> - NodeSet.add exitNode; + NodeSet.add entry_node |> + NodeSet.add exit_node; edges = NodeMap.union (fun _ -> failwith "Failed merging edges of cfg.") cfg1.edges cfg2.edges |> - NodeMap.add entryNode (cfg1initial, Some cfg2initial) |> - NodeMap.add cfg1terminal (exitNode, None) |> - NodeMap.add cfg2terminal (exitNode, None); - reverseEdges = NodeMap.union + NodeMap.add entry_node (cfg1_initial, Some cfg2_initial) |> + NodeMap.add cfg1_terminal (exit_node, None) |> + NodeMap.add cfg2_terminal (exit_node, None); + reverse_edges = NodeMap.union (fun _ -> failwith "Failed merging edges of cfg.") - cfg1.reverseEdges cfg2.reverseEdges |> - NodeMap.add_to_list cfg1initial entryNode |> - NodeMap.add_to_list cfg2initial entryNode |> - NodeMap.add_to_list exitNode cfg1terminal |> - NodeMap.add_to_list exitNode cfg2terminal; - inputVal = cfg1.inputVal; - inputOutputVar = cfg1.inputOutputVar; - initial = Some entryNode; - terminal = Some exitNode; + cfg1.reverse_edges cfg2.reverse_edges |> + NodeMap.add_to_list cfg1_initial entry_node |> + NodeMap.add_to_list cfg2_initial entry_node |> + NodeMap.add_to_list exit_node cfg1_terminal |> + NodeMap.add_to_list exit_node cfg2_terminal; + input_val = cfg1.input_val; + input_output_var = cfg1.input_output_var; + initial = Some entry_node; + terminal = Some exit_node; content = NodeMap.union (fun _ -> failwith "Failed merging code of cfg.") cfg1.content cfg2.content @@ -108,48 +108,48 @@ module Make (M: PrintableType) = struct true, _ -> cfg2 | _, true -> cfg1 | false, false -> - let cfg1initial = Option.get cfg1.initial in - let cfg2initial = Option.get cfg2.initial in - let cfg1terminal = Option.get cfg1.terminal in - let cfg2terminal = Option.get cfg2.terminal in + let cfg1_initial = Option.get cfg1.initial in + let cfg2_initial = Option.get cfg2.initial in + let cfg1_terminal = Option.get cfg1.terminal in + let cfg2_terminal = Option.get cfg2.terminal in { empty = false; nodes = NodeSet.union cfg1.nodes cfg2.nodes; edges = NodeMap.union (fun _ -> failwith "Failed merging edges of cfg.") cfg1.edges cfg2.edges |> - NodeMap.add cfg1terminal (cfg2initial, None); - reverseEdges = NodeMap.union + NodeMap.add cfg1_terminal (cfg2_initial, None); + reverse_edges = NodeMap.union (fun _ -> failwith "Failed merging edges of cfg.") - cfg1.reverseEdges cfg2.reverseEdges |> - NodeMap.add_to_list cfg2initial cfg1terminal; - inputVal = cfg1.inputVal; - inputOutputVar = cfg1.inputOutputVar; - initial = Some cfg1initial; - terminal = Some cfg2terminal; + cfg1.reverse_edges cfg2.reverse_edges |> + NodeMap.add_to_list cfg2_initial cfg1_terminal; + input_val = cfg1.input_val; + input_output_var = cfg1.input_output_var; + initial = Some cfg1_initial; + terminal = Some cfg2_terminal; content = NodeMap.union (fun _ -> failwith "Failed merging code of cfg.") cfg1.content cfg2.content } - let addToLastNode (newcontent: elt) (cfg: t) : t = + let add_to_last_node (new_content: elt) (cfg: t) : t = if cfg.empty then - let newnode = Node.create () in + let new_node = Node.create () in { empty = false; - nodes = NodeSet.singleton newnode; + nodes = NodeSet.singleton new_node; edges = NodeMap.empty; - reverseEdges = NodeMap.empty; - inputVal = None; - inputOutputVar = None; - initial = Some newnode; - terminal = Some newnode; - content = NodeMap.singleton newnode [newcontent] + reverse_edges = NodeMap.empty; + input_val = None; + input_output_var = None; + initial = Some new_node; + terminal = Some new_node; + content = NodeMap.singleton new_node [new_content] } else - let prevcfgterminal = Option.get cfg.terminal in + let prevcfg_terminal = Option.get cfg.terminal in { cfg with content = (NodeMap.add_to_list_last - prevcfgterminal - newcontent + prevcfg_terminal + new_content cfg.content) } let pp (ppf) (c: t) : unit = @@ -173,17 +173,17 @@ module Make (M: PrintableType) = struct Printf.fprintf ppf "\t%d -> " n.id; List.iter (fun (x: Node.t) -> Printf.fprintf ppf "%d, " x.id) xs; Printf.fprintf ppf "\n" - ) (NodeMap.to_list c.reverseEdges); + ) (NodeMap.to_list c.reverse_edges); Printf.fprintf ppf "\n"; Printf.fprintf ppf "Input Value: "; - (match c.inputVal with + (match c.input_val with Some i -> Printf.fprintf ppf "%d" i; | None -> Printf.fprintf ppf "None";); Printf.fprintf ppf "\n"; Printf.fprintf ppf "Input and Output Vars: "; - (match c.inputOutputVar with + (match c.input_output_var with Some (i, o) -> Printf.fprintf ppf "(in: %s, out: %s)" i o; | None -> Printf.fprintf ppf "None";); Printf.fprintf ppf "\n"; @@ -202,7 +202,7 @@ module Make (M: PrintableType) = struct Printf.fprintf ppf "Code:\n"; List.iter (fun ((n, stms) : Node.t * elt list) : unit -> - Printf.fprintf ppf "\tid %d --> %a\n%!" n.id M.pplist stms + Printf.fprintf ppf "\tid %d --> %a\n%!" n.id M.pp_list stms ) (NodeMap.to_list c.content); Printf.fprintf ppf "\n"; end diff --git a/lib/analysis/Cfg.mli b/lib/analysis/Cfg.mli index ae1d790..837ffae 100644 --- a/lib/analysis/Cfg.mli +++ b/lib/analysis/Cfg.mli @@ -1,7 +1,7 @@ module type PrintableType = sig type t val pp : out_channel -> t -> unit - val pplist : out_channel -> t list -> unit + val pp_list : out_channel -> t list -> unit end module Node : sig @@ -25,9 +25,9 @@ type 'a cfginternal = { empty: bool; nodes: NodeSet.t; edges: (Node.t * (Node.t option)) NodeMap.t; - reverseEdges: (Node.t list) NodeMap.t; - inputVal: int option; - inputOutputVar: (string * string) option; + reverse_edges: (Node.t list) NodeMap.t; + input_val: int option; + input_output_var: (string * string) option; initial: Node.t option; terminal: Node.t option; content: 'a list NodeMap.t; @@ -40,7 +40,7 @@ module type C = sig val empty : t val merge : t -> t -> Node.t -> Node.t -> t val concat : t -> t -> t - val addToLastNode : elt -> t -> t + val add_to_last_node : elt -> t -> t val pp : out_channel -> t -> unit end diff --git a/lib/analysis/Dataflow.ml b/lib/analysis/Dataflow.ml index 04dcb86..904b733 100644 --- a/lib/analysis/Dataflow.ml +++ b/lib/analysis/Dataflow.ml @@ -2,25 +2,25 @@ module type C = sig type elt type internal - type internalnode = { - internalin: internal list; - internalout: internal list; - internalbetween: (internal list * internal list) list; + type internal_node = { + internal_in: internal list; + internal_out: internal list; + internal_between: (internal list * internal list) list; } type cfgt = elt Cfg.cfginternal type t = { t: cfgt; - internalvar: internalnode Cfg.NodeMap.t; + internal_var: internal_node Cfg.NodeMap.t; } val from_cfg : cfgt -> t val to_cfg : t -> cfgt val fixed_point : - ?init:(elt list -> internalnode) -> - ?update:(t -> Cfg.Node.t -> internalnode) -> + ?init : (elt list -> internal_node) -> + ?update : (t -> Cfg.Node.t -> internal_node) -> t -> t @@ -31,20 +31,20 @@ module Make (M: Cfg.PrintableType) (I: Cfg.PrintableType) = struct type elt = M.t type internal = I.t - type internalnode = { - internalin: internal list; - internalout: internal list; - internalbetween: (internal list * internal list) list; + type internal_node = { + internal_in: internal list; + internal_out: internal list; + internal_between: (internal list * internal list) list; } - let compareinternalnode (a:internalnode) (b:internalnode) : bool = - match Utility.equality a.internalin b.internalin, - Utility.equality a.internalout b.internalout, + let compare_internal_node (a:internal_node) (b:internal_node) : bool = + match Utility.equality a.internal_in b.internal_in, + Utility.equality a.internal_out b.internal_out, (List.fold_left2 (fun acc (ain, aout) (bin, bout) -> acc && (Utility.equality ain bin) && (Utility.equality aout bout) - ) true a.internalbetween b.internalbetween) + ) true a.internal_between b.internal_between) with | true, true, true -> true | _, _, _ -> false @@ -53,19 +53,19 @@ module Make (M: Cfg.PrintableType) (I: Cfg.PrintableType) = struct type t = { t: cfgt; - internalvar: internalnode Cfg.NodeMap.t; + internal_var: internal_node Cfg.NodeMap.t; } - let compareinternal a b = + let compare_internal a b = Cfg.NodeMap.fold (fun node bi acc -> match Cfg.NodeMap.find_opt node a with None -> false - | Some ai -> acc && compareinternalnode ai bi + | Some ai -> acc && compare_internal_node ai bi ) b true let from_cfg (cfg: cfgt) : t = - {t = cfg; internalvar = Cfg.NodeMap.empty} + {t = cfg; internal_var = Cfg.NodeMap.empty} let to_cfg ({t; _}: t) : cfgt = t @@ -92,17 +92,17 @@ module Make (M: Cfg.PrintableType) (I: Cfg.PrintableType) = struct Printf.fprintf ppf "\t%d -> " n.id; List.iter (fun (x: Node.t) -> Printf.fprintf ppf "%d, " x.id) xs; Printf.fprintf ppf "\n" - ) (NodeMap.to_list c.t.reverseEdges); + ) (NodeMap.to_list c.t.reverse_edges); Printf.fprintf ppf "\n"; Printf.fprintf ppf "Input Value: "; - (match c.t.inputVal with + (match c.t.input_val with Some i -> Printf.fprintf ppf "%d" i; | None -> Printf.fprintf ppf "None";); Printf.fprintf ppf "\n"; Printf.fprintf ppf "Input and Output Vars: "; - (match c.t.inputOutputVar with + (match c.t.input_output_var with Some (i, o) -> Printf.fprintf ppf "(in: %s, out: %s)" i o; | None -> Printf.fprintf ppf "None";); Printf.fprintf ppf "\n"; @@ -121,35 +121,36 @@ module Make (M: Cfg.PrintableType) (I: Cfg.PrintableType) = struct Printf.fprintf ppf "Code:\n"; List.iter (fun ((n, stms) : Node.t * elt list) : unit -> - Printf.fprintf ppf "\tid %d --> %a\n%!" n.id M.pplist stms + Printf.fprintf ppf "\tid %d --> %a\n%!" n.id M.pp_list stms ) (NodeMap.to_list c.t.content); Printf.fprintf ppf "\n"; Printf.fprintf ppf "Analysis structure:\n"; - List.iter (fun ((n, {internalin; internalout; internalbetween}) - : (Node.t * internalnode)) : unit -> + List.iter (fun ((n, {internal_in; internal_out; internal_between}) + : (Node.t * internal_node)) : unit -> Printf.fprintf ppf "Node: %d\n" n.id; Printf.fprintf ppf "Internal Input: "; - Printf.fprintf ppf "%a\n" I.pplist internalin; + Printf.fprintf ppf "%a\n" I.pp_list internal_in; Printf.fprintf ppf "Internal Output: "; - Printf.fprintf ppf "%a\n" I.pplist internalout; + Printf.fprintf ppf "%a\n" I.pp_list internal_out; Printf.fprintf ppf "Internal Between: "; List.iter (fun (i, o) -> - Printf.fprintf ppf "IN: %a;" I.pplist i; - Printf.fprintf ppf "OUT: %a;" I.pplist o;) internalbetween; + Printf.fprintf ppf "IN: %a;" I.pp_list i; + Printf.fprintf ppf "OUT: %a;" I.pp_list o;) + internal_between; Printf.fprintf ppf "\n"; - ) (NodeMap.to_list c.internalvar); + ) (NodeMap.to_list c.internal_var); Printf.fprintf ppf "\n"; ) let fixed_point - ?(init : (elt list -> internalnode) = - (fun _ -> {internalin = []; - internalout = []; - internalbetween = []})) - ?(update : (t -> Cfg.Node.t -> internalnode) = - (fun t n -> Cfg.NodeMap.find n t.internalvar)) + ?(init : (elt list -> internal_node) = + (fun _ -> {internal_in = []; + internal_out = []; + internal_between = []})) + ?(update : (t -> Cfg.Node.t -> internal_node) = + (fun t n -> Cfg.NodeMap.find n t.internal_var)) (t: t) : t = (* init function is applied only once to each node content, @@ -158,21 +159,21 @@ module Make (M: Cfg.PrintableType) (I: Cfg.PrintableType) = struct update function is applied to the resulting structure until no change is observed with compareinternal function *) - let rec helper t = + let rec aux t = let newt = {t with - internalvar = Cfg.NodeMap.mapi (fun n _ -> update t n) t.internalvar} + internal_var = Cfg.NodeMap.mapi (fun n _ -> update t n) t.internal_var} in - if compareinternal newt.internalvar t.internalvar + if compare_internal newt.internal_var t.internal_var then newt - else helper newt + else aux newt in let content = List.fold_left - (fun cfg node -> Cfg.NodeMap.add node {internalin = []; - internalout = []; - internalbetween = []} cfg) + (fun cfg node -> Cfg.NodeMap.add node {internal_in = []; + internal_out = []; + internal_between = []} cfg) Cfg.NodeMap.empty (Cfg.NodeSet.to_list t.t.nodes) in @@ -190,6 +191,6 @@ module Make (M: Cfg.PrintableType) (I: Cfg.PrintableType) = struct content (Cfg.NodeMap.map init code) in - helper { t with internalvar = content } + aux { t with internal_var = content } end diff --git a/lib/analysis/Dataflow.mli b/lib/analysis/Dataflow.mli index 76e9cfa..7b33a00 100644 --- a/lib/analysis/Dataflow.mli +++ b/lib/analysis/Dataflow.mli @@ -2,25 +2,25 @@ module type C = sig type elt type internal - type internalnode = { - internalin: internal list; - internalout: internal list; - internalbetween: (internal list * internal list) list; + type internal_node = { + internal_in: internal list; + internal_out: internal list; + internal_between: (internal list * internal list) list; } type cfgt = elt Cfg.cfginternal type t = { t: cfgt; - internalvar: internalnode Cfg.NodeMap.t; + internal_var: internal_node Cfg.NodeMap.t; } val from_cfg : cfgt -> t val to_cfg : t -> cfgt val fixed_point : - ?init:(elt list -> internalnode) -> - ?update:(t -> Cfg.Node.t -> internalnode) -> t -> t + ?init : (elt list -> internal_node) -> + ?update : (t -> Cfg.Node.t -> internal_node) -> t -> t val pp : out_channel -> t -> unit end diff --git a/lib/miniFun/Semantics.ml b/lib/miniFun/Semantics.ml index 5661a93..7ab5de5 100644 --- a/lib/miniFun/Semantics.ml +++ b/lib/miniFun/Semantics.ml @@ -6,7 +6,7 @@ Random.self_init () let (let*) = Result.bind let rec evaluate (mem: memory) (command: t_exp) : - (permittedValues, [> error]) result = + (permitted_values, [> error]) result = match command with Integer n -> Ok (IntegerPermitted n) | Boolean b -> Ok (BooleanPermitted b) @@ -31,7 +31,7 @@ let rec evaluate (mem: memory) (command: t_exp) : ) | Application (f, x) -> ( let* evalf = evaluate mem f in - let* funcClosure = ( + let* func_closure = ( match evalf with FunctionPermitted ff -> Ok ff | IntegerPermitted _ -> Error (`WrongType ("Function is not a function," @@ -43,15 +43,15 @@ let rec evaluate (mem: memory) (command: t_exp) : ) in let* param = evaluate mem x in let mem2 = - match funcClosure.recursiveness with + match func_closure.recursiveness with None -> {assignments = ( - VariableMap.add funcClosure.input param funcClosure.assignments)} + VariableMap.add func_closure.input param func_closure.assignments)} | Some nameF -> {assignments = ( - VariableMap.add funcClosure.input param funcClosure.assignments |> - VariableMap.add nameF (FunctionPermitted funcClosure) + VariableMap.add func_closure.input param func_closure.assignments |> + VariableMap.add nameF (FunctionPermitted func_closure) )} in - evaluate mem2 funcClosure.body + evaluate mem2 func_closure.body ) | Plus (a, b) -> let* aval = ( diff --git a/lib/miniFun/Semantics.mli b/lib/miniFun/Semantics.mli index 176883e..92f88e3 100644 --- a/lib/miniFun/Semantics.mli +++ b/lib/miniFun/Semantics.mli @@ -1,3 +1,3 @@ -val evaluate : Types.memory -> Types.t_exp -> (Types.permittedValues, [> Types.error]) result +val evaluate : Types.memory -> Types.t_exp -> (Types.permitted_values, [> Types.error]) result val reduce : Types.t_exp -> int -> (int, [> Types.error]) result diff --git a/lib/miniFun/Types.ml b/lib/miniFun/Types.ml index a32281e..ace2bad 100644 --- a/lib/miniFun/Types.ml +++ b/lib/miniFun/Types.ml @@ -38,20 +38,20 @@ type t_exp = | LetFun of variable * variable * ftype * t_exp * t_exp (* let rec x. y: t. x in x*) -type permittedValues = +type permitted_values = IntegerPermitted of int | BooleanPermitted of bool - | TuplePermitted of permittedValues * permittedValues + | TuplePermitted of permitted_values * permitted_values | FunctionPermitted of closure and closure = { input: variable; body: t_exp; - assignments: permittedValues VariableMap.t; + assignments: permitted_values VariableMap.t; recursiveness: variable option } type memory = { - assignments: permittedValues VariableMap.t + assignments: permitted_values VariableMap.t } diff --git a/lib/miniFun/Types.mli b/lib/miniFun/Types.mli index ca38091..749244f 100644 --- a/lib/miniFun/Types.mli +++ b/lib/miniFun/Types.mli @@ -37,20 +37,20 @@ type t_exp = | LetIn of variable * t_exp * t_exp (* let x = x in x *) | LetFun of variable * variable * ftype * t_exp * t_exp (* let rec x. y: t. x in x*) -type permittedValues = +type permitted_values = IntegerPermitted of int | BooleanPermitted of bool - | TuplePermitted of permittedValues * permittedValues + | TuplePermitted of permitted_values * permitted_values | FunctionPermitted of closure and closure = { input: variable; body: t_exp; - assignments: permittedValues VariableMap.t; + assignments: permitted_values VariableMap.t; recursiveness: variable option } type memory = { - assignments: permittedValues VariableMap.t + assignments: permitted_values VariableMap.t } diff --git a/lib/miniImp/CfgImp.ml b/lib/miniImp/CfgImp.ml index c447109..9ba41f2 100644 --- a/lib/miniImp/CfgImp.ml +++ b/lib/miniImp/CfgImp.ml @@ -79,7 +79,7 @@ module SimpleStatements = struct in helper_c ppf c - let pplist (ppf: out_channel) (c: t list) : unit = + let pp_list (ppf: out_channel) (c: t list) : unit = List.iter (fun x -> pp ppf x; Printf.printf "; ") c end @@ -89,10 +89,10 @@ let rec convert_c (prevcfg: SSCfg.t) (prg: Types.c_exp) : SSCfg.t = let open SimpleStatements in match prg with | Skip -> (* we preserve the skips *) - prevcfg |> SSCfg.addToLastNode SimpleSkip + prevcfg |> SSCfg.add_to_last_node SimpleSkip | Assignment (x, a) -> (* we simply add the assignment to the terminal node *) - prevcfg |> SSCfg.addToLastNode (SimpleAssignment (x, convert_a a)) + prevcfg |> SSCfg.add_to_last_node (SimpleAssignment (x, convert_a a)) | Sequence (c1, c2) -> (* we first convert the first sequence, then the second using the previous as prevcfg *) @@ -101,7 +101,7 @@ let rec convert_c (prevcfg: SSCfg.t) (prg: Types.c_exp) : SSCfg.t = cfg2 | If (b, c1, c2) -> (* constructs two branches with a two new nodes *) - let convertedb = convert_b b in + let converted_b = convert_b b in let cfg1 = convert_c SSCfg.empty c1 in let cfg2 = convert_c SSCfg.empty c2 in let entrynode = Node.create () in @@ -110,11 +110,11 @@ let rec convert_c (prevcfg: SSCfg.t) (prg: Types.c_exp) : SSCfg.t = let mergedcfg = SSCfg.concat prevcfg newcfg in { mergedcfg with content = mergedcfg.content |> - NodeMap.add_to_list entrynode (SimpleGuard convertedb) |> + NodeMap.add_to_list entrynode (SimpleGuard converted_b) |> NodeMap.add_to_list exitnode (SimpleSkip) } | While (b, c) -> (* constructs a loop, needs three new nodes *) - let convertedb = convert_b b in + let converted_b = convert_b b in let cfg = convert_c SSCfg.empty c in let cfginitial = Option.get cfg.initial in let cfgterminal = Option.get cfg.terminal in @@ -130,17 +130,17 @@ let rec convert_c (prevcfg: SSCfg.t) (prg: Types.c_exp) : SSCfg.t = NodeMap.add entrynode (guardnode, None) |> NodeMap.add guardnode (cfginitial, Some exitnode) |> NodeMap.add cfgterminal (guardnode, None); - reverseEdges = cfg.reverseEdges |> + reverse_edges = cfg.reverse_edges |> NodeMap.add_to_list guardnode entrynode |> NodeMap.add_to_list cfginitial guardnode |> NodeMap.add_to_list exitnode guardnode |> NodeMap.add_to_list guardnode cfgterminal; - inputVal = prevcfg.inputVal; - inputOutputVar = prevcfg.inputOutputVar; + input_val = prevcfg.input_val; + input_output_var = prevcfg.input_output_var; initial = Some entrynode; terminal = Some exitnode; content = cfg.content |> - NodeMap.add_to_list guardnode (SimpleGuard (convertedb)) |> + NodeMap.add_to_list guardnode (SimpleGuard (converted_b)) |> NodeMap.add_to_list exitnode (SimpleSkip) } |> SSCfg.concat prevcfg @@ -166,12 +166,12 @@ let rec convert_c (prevcfg: SSCfg.t) (prg: Types.c_exp) : SSCfg.t = edges = bodyincrement.edges |> NodeMap.add guardnode (cfginitial, Some exitnode) |> NodeMap.add cfgterminal (guardnode, None); - reverseEdges = bodyincrement.reverseEdges |> + reverse_edges = bodyincrement.reverse_edges |> NodeMap.add_to_list cfginitial guardnode |> NodeMap.add_to_list exitnode guardnode |> NodeMap.add_to_list guardnode cfgterminal; - inputVal = prevcfg.inputVal; - inputOutputVar = prevcfg.inputOutputVar; + input_val = prevcfg.input_val; + input_output_var = prevcfg.input_output_var; initial = Some guardnode; terminal = Some exitnode; content = bodyincrement.content |> @@ -208,8 +208,8 @@ let convert (prg: Types.p_exp) : SSCfg.t = let prg = ReplacePowerMod.rewrite_instructions prg in match prg with | Main (i, o, exp) -> - {(convert_c SSCfg.empty exp) with inputOutputVar = Some (i, o)} + {(convert_c SSCfg.empty exp) with input_output_var = Some (i, o)} let convert_io (i: int) (prg: Types.p_exp) : SSCfg.t = let prg = ReplacePowerMod.rewrite_instructions prg in - {(convert prg) with inputVal = Some i} + {(convert prg) with input_val = Some i} diff --git a/lib/miniImp/CfgImp.mli b/lib/miniImp/CfgImp.mli index 5e933a1..f6a29ae 100644 --- a/lib/miniImp/CfgImp.mli +++ b/lib/miniImp/CfgImp.mli @@ -27,7 +27,7 @@ module SimpleStatements : sig | SimpleRand of simpleArithmetic val pp : out_channel -> t -> unit - val pplist : out_channel -> t list -> unit + val pp_list : out_channel -> t list -> unit end module SSCfg : Cfg.C with type elt = SimpleStatements.t diff --git a/lib/miniImp/CfgRISC.ml b/lib/miniImp/CfgRISC.ml index a18651b..3990acf 100644 --- a/lib/miniImp/CfgRISC.ml +++ b/lib/miniImp/CfgRISC.ml @@ -102,7 +102,7 @@ module RISCSimpleStatements = struct in pp_t ppf v - let pplist (ppf: out_channel) (l: t list) : unit = + let pp_list (ppf: out_channel) (l: t list) : unit = List.iter (fun x -> pp ppf x; Printf.printf "; ") l end @@ -915,7 +915,7 @@ let convert_ss in (Cfg.NodeMap.add node instructions risccode, m) -let helper +let helper_convert (c: CfgImp.SimpleStatements.t list Cfg.NodeMap.t) (m: RegisterMap.m) : RISCSimpleStatements.t list Cfg.NodeMap.t = @@ -932,16 +932,16 @@ let convert (prg: CfgImp.SSCfg.t) : RISCCfg.t = let ({ empty: bool; nodes: Cfg.NodeSet.t; edges: (Cfg.Node.t * (Cfg.Node.t option)) Cfg.NodeMap.t; - reverseEdges: (Cfg.Node.t list) Cfg.NodeMap.t; - inputVal: int option; - inputOutputVar: (string * string) option; + reverse_edges: (Cfg.Node.t list) Cfg.NodeMap.t; + input_val: int option; + input_output_var: (string * string) option; initial: Cfg.Node.t option; terminal: Cfg.Node.t option; content: CfgImp.SimpleStatements.t list Cfg.NodeMap.t }: CfgImp.SSCfg.t) = prg in let initial_bindings = - match inputOutputVar with + match input_output_var with | Some (i, o) -> ( if i = o then RegisterMap.empty |> @@ -957,10 +957,10 @@ let convert (prg: CfgImp.SSCfg.t) : RISCCfg.t = { empty = empty; nodes = nodes; edges = edges; - reverseEdges = reverseEdges; - inputVal = inputVal; - inputOutputVar = ( - match inputOutputVar with + reverse_edges = reverse_edges; + input_val = input_val; + input_output_var = ( + match input_output_var with | Some (i, o) -> ( if i = o then Some ("in", "in") @@ -971,5 +971,5 @@ let convert (prg: CfgImp.SSCfg.t) : RISCCfg.t = ); initial = initial; terminal = terminal; - content = helper content initial_bindings; + content = helper_convert content initial_bindings; } diff --git a/lib/miniImp/CfgRISC.mli b/lib/miniImp/CfgRISC.mli index 53b56d7..36c1dcf 100644 --- a/lib/miniImp/CfgRISC.mli +++ b/lib/miniImp/CfgRISC.mli @@ -47,7 +47,7 @@ module RISCSimpleStatements : sig | Rand val pp : out_channel -> t -> unit - val pplist : out_channel -> t list -> unit + val pp_list : out_channel -> t list -> unit end module RISCCfg : Cfg.C with type elt = RISCSimpleStatements.t diff --git a/lib/miniImp/RISC.ml b/lib/miniImp/RISC.ml index e694bc0..a6cde7d 100644 --- a/lib/miniImp/RISC.ml +++ b/lib/miniImp/RISC.ml @@ -142,19 +142,19 @@ end let convert_cfgrisc_risci (i: CfgRISC.RISCSimpleStatements.t list) : (RISCAssembly.risci list) = - let rec helper (i: CfgRISC.RISCSimpleStatements.t) + let rec aux (i: CfgRISC.RISCSimpleStatements.t) : RISCAssembly.risci = match i with | Nop -> Nop - | BRegOp (brop, r1, r2, r3) -> BRegOp (helper_brop brop, + | BRegOp (brop, r1, r2, r3) -> BRegOp (aux_brop brop, {index = r1.index}, {index = r2.index}, {index = r3.index}) - | BImmOp (biop, r1, imm, r3) -> BImmOp (helper_biop biop, + | BImmOp (biop, r1, imm, r3) -> BImmOp (aux_biop biop, {index = r1.index}, imm, {index = r3.index}) - | URegOp (urop, r1, r3) -> URegOp (helper_urop urop, + | URegOp (urop, r1, r3) -> URegOp (aux_urop urop, {index = r1.index}, {index = r3.index}) | Load (r1, r3) -> Load ({index = r1.index}, @@ -163,7 +163,7 @@ let convert_cfgrisc_risci (i: CfgRISC.RISCSimpleStatements.t list) : {index = r3.index}) | Store (r1, r3) -> Store ({index = r1.index}, {index = r3.index}) - and helper_brop (brop: CfgRISC.RISCSimpleStatements.brop) + and aux_brop (brop: CfgRISC.RISCSimpleStatements.brop) : RISCAssembly.brop = match brop with | Add -> Add @@ -179,7 +179,7 @@ let convert_cfgrisc_risci (i: CfgRISC.RISCSimpleStatements.t list) : | LessEq -> LessEq | More -> More | MoreEq -> MoreEq - and helper_biop (biop: CfgRISC.RISCSimpleStatements.biop) + and aux_biop (biop: CfgRISC.RISCSimpleStatements.biop) : RISCAssembly.biop = match biop with | AddI -> AddI @@ -195,16 +195,16 @@ let convert_cfgrisc_risci (i: CfgRISC.RISCSimpleStatements.t list) : | LessEqI -> LessEqI | MoreI -> MoreI | MoreEqI -> MoreEqI - and helper_urop (urop: CfgRISC.RISCSimpleStatements.urop) + and aux_urop (urop: CfgRISC.RISCSimpleStatements.urop) : RISCAssembly.urop = match urop with | Not -> Not | Copy -> Copy | Rand -> Rand in - List.map helper i + List.map aux i -let nextCommonSuccessor +let next_common_successor (prg: CfgRISC.RISCCfg.t) (node1: Cfg.Node.t) (node2: Cfg.Node.t) @@ -231,30 +231,35 @@ let nextCommonSuccessor | a::_ -> Some a -let rec helper +let rec helper_convert (prg: CfgRISC.RISCCfg.t) - (currentnode: Cfg.Node.t) - (alreadyVisited: Cfg.Node.t list) + (current_node: Cfg.Node.t) + (already_visited: Cfg.Node.t list) : (RISCAssembly.risci list) * (Cfg.Node.t list) = (* takes the program, the current node and a list of already visited nodes to compute the linearized three address instructions and the list of previoulsy visited nodes plus the newly visited nodes. Stops as soon if node has already been visited or if no nodes are next *) - if List.mem currentnode alreadyVisited then - ([], alreadyVisited) + if List.mem current_node already_visited then + ([], already_visited) else ( - let nextnodes = (Cfg.NodeMap.find_opt currentnode prg.edges) in + let nextnodes = (Cfg.NodeMap.find_opt current_node prg.edges) in let currentcode = - (match (Cfg.NodeMap.find_opt currentnode prg.content) with + (match (Cfg.NodeMap.find_opt current_node prg.content) with | None -> [] | Some x -> convert_cfgrisc_risci x) in match nextnodes with | Some (nextnode1, None) -> - let res, vis = (helper prg nextnode1 (currentnode :: alreadyVisited)) in + let res, vis = + helper_convert + prg + nextnode1 + (current_node :: already_visited) + in (currentcode @ res, vis) | Some (nextnode1, Some nextnode2) -> ( - let ncs = nextCommonSuccessor prg nextnode1 nextnode2 in + let ncs = next_common_successor prg nextnode1 nextnode2 in match ncs with | None -> (* should never happen since the terminal node should always be rechable *) @@ -267,11 +272,11 @@ let rec helper let label3 = nextLabel () in let res1, _ = - (helper prg nextnode1 - (currentnode :: nextnode2 :: alreadyVisited)) in + (helper_convert prg nextnode1 + (current_node :: nextnode2 :: already_visited)) in let res2, vis2 = - (helper prg nextnode2 - (currentnode :: nextnode1 :: alreadyVisited)) in + (helper_convert prg nextnode2 + (current_node :: nextnode1 :: already_visited)) in match List.nth currentcode ((List.length currentcode) - 1) with | BRegOp (_, _, _, r) @@ -296,9 +301,15 @@ let rec helper let label3 = nextLabel () in let res1, vis1 = - (helper prg nextnode1 (currentnode :: ncs :: alreadyVisited)) in - let res2, _ = (helper prg nextnode2 vis1) in - let res3, vis3 = (helper prg ncs (currentnode :: alreadyVisited)) in + helper_convert + prg + nextnode1 + (current_node :: ncs :: already_visited) + in + let res2, _ = helper_convert prg nextnode2 vis1 in + let res3, vis3 = + helper_convert prg ncs (current_node :: already_visited) + in match List.nth currentcode ((List.length currentcode) - 1) with | BRegOp (_, _, _, r) | BImmOp (_, _, _, r) @@ -319,15 +330,15 @@ let rec helper | _ -> failwith "Missing instruction at branch" ) ) - | None -> (currentcode, currentnode :: alreadyVisited) + | None -> (currentcode, current_node :: already_visited) ) let convert (prg: CfgRISC.RISCCfg.t) : RISCAssembly.t = - {code = (helper prg (Option.get prg.initial) [] |> fst |> + {code = (helper_convert prg (Option.get prg.initial) [] |> fst |> List.append ([Label "main"] : RISCAssembly.risci list)); - inputval = prg.inputVal; + inputval = prg.input_val; inputoutputreg = - match prg.inputOutputVar with + match prg.input_output_var with None -> None | Some (i, o) -> Some ({index = i}, {index = o}) } diff --git a/lib/miniImp/RISCSemantics.ml b/lib/miniImp/RISCSemantics.ml index d01414d..f346765 100644 --- a/lib/miniImp/RISCSemantics.ml +++ b/lib/miniImp/RISCSemantics.ml @@ -22,7 +22,7 @@ let convert (prg: RISC.RISCAssembly.t) : RISC.RISCAssembly.risci list CodeMap.t = (* takes as input a sequence of RISC commands and computes a map to the right labels for easier execution *) - let rec helper + let rec aux (prg: RISC.RISCAssembly.risci list) (current: RISC.RISCAssembly.risci list) (current_label: string) @@ -33,27 +33,27 @@ let convert (prg: RISC.RISCAssembly.t) (fun _ _ _ -> failwith "Two labels are the same") (CodeMap.singleton current_label current) map) - | Label l :: tl -> helper tl ([]) l + | Label l :: tl -> aux tl ([]) l (CodeMap.union (fun _ _ _ -> failwith "Two labels are the same") (CodeMap.singleton current_label current) map) - | instr :: tl -> helper tl (current @ [instr]) current_label map + | instr :: tl -> aux tl (current @ [instr]) current_label map in match prg.code with - | Label "main" :: tl -> helper tl [] "main" CodeMap.empty + | Label "main" :: tl -> aux tl [] "main" CodeMap.empty | _ -> failwith "Program should begind with label main" let label_order (prg: RISC.RISCAssembly.t) : string list = - let rec helper + let rec aux (prg: RISC.RISCAssembly.risci list) : string list = match prg with [] -> [] - | Label l :: tl -> l :: (helper tl) - | _ :: tl -> (helper tl) + | Label l :: tl -> l :: (aux tl) + | _ :: tl -> (aux tl) in - helper (prg.code) + aux (prg.code) let reduce_instructions (prg: RISCArchitecture.t) (lo: string list) : int = let match_operator_r (brop: RISC.RISCAssembly.brop) = @@ -89,7 +89,7 @@ let reduce_instructions (prg: RISCArchitecture.t) (lo: string list) : int = | MoreEqI -> (Utility.int_more_eq) in - let rec helper + let rec aux (prg: RISCArchitecture.t) (current: RISC.RISCAssembly.risci list) (current_label: string) @@ -101,7 +101,7 @@ let reduce_instructions (prg: RISCArchitecture.t) (lo: string list) : int = None -> prg (* should never happen *) | Some i -> if i + 1 < (List.length lo) then - helper + aux prg (CodeMap.find (List.nth lo (i+1)) prg.code) (List.nth lo (i+1)) @@ -109,13 +109,13 @@ let reduce_instructions (prg: RISCArchitecture.t) (lo: string list) : int = prg ) | Nop :: tl -> - helper prg tl current_label + aux prg tl current_label | BRegOp (brop, r1, r2, r3) :: tl -> ( let n = (match_operator_r brop) (RegisterMap.find {index = r1.index} prg.registers) (RegisterMap.find {index = r2.index} prg.registers) in - helper { prg with + aux { prg with registers = RegisterMap.add {index = r3.index} n prg.registers} tl current_label ) @@ -124,7 +124,7 @@ let reduce_instructions (prg: RISCArchitecture.t) (lo: string list) : int = (RegisterMap.find {index = r1.index} prg.registers) i in - helper { prg with + aux { prg with registers = RegisterMap.add {index = r3.index} n prg.registers} tl current_label ) @@ -132,7 +132,7 @@ let reduce_instructions (prg: RISCArchitecture.t) (lo: string list) : int = match urop with | Copy -> ( let n = RegisterMap.find {index = r1.index} prg.registers in - helper { prg with + aux { prg with registers = RegisterMap.add {index = r3.index} n prg.registers } tl current_label @@ -141,7 +141,7 @@ let reduce_instructions (prg: RISCArchitecture.t) (lo: string list) : int = let n = Utility.int_not (RegisterMap.find {index = r1.index} prg.registers) in - helper { prg with + aux { prg with registers = RegisterMap.add {index = r3.index} n prg.registers } tl current_label @@ -150,7 +150,7 @@ let reduce_instructions (prg: RISCArchitecture.t) (lo: string list) : int = let n = Random.int (RegisterMap.find {index = r1.index} prg.registers) in - helper { prg with + aux { prg with registers = RegisterMap.add {index = r3.index} n prg.registers } tl current_label @@ -162,40 +162,40 @@ let reduce_instructions (prg: RISCArchitecture.t) (lo: string list) : int = (RegisterMap.find {index = r1.index} prg.registers) prg.memory in - helper { prg with + aux { prg with registers = RegisterMap.add {index = r3.index} n prg.registers} tl current_label ) | LoadI (i, r3) :: tl -> ( let n = i in - helper { prg with + aux { prg with registers = RegisterMap.add {index = r3.index} n prg.registers} tl current_label ) | Store (r1, r3) :: tl -> ( let n = RegisterMap.find {index = r1.index} prg.registers in let n1 = RegisterMap.find {index = r3.index} prg.registers in - helper + aux { prg with memory = MemoryMap.add n1 n prg.memory } tl current_label ) - | Jump l :: _ -> helper prg (CodeMap.find l prg.code) l + | Jump l :: _ -> aux prg (CodeMap.find l prg.code) l | CJump (r, l1, l2) :: _ -> ( let br = (RegisterMap.find {index = r.index} prg.registers) > 0 in if br then - helper prg (CodeMap.find l1 prg.code) l1 + aux prg (CodeMap.find l1 prg.code) l1 else - helper prg (CodeMap.find l2 prg.code) l2 + aux prg (CodeMap.find l2 prg.code) l2 ) - | Label _ :: tl -> helper prg tl current_label + | Label _ :: tl -> aux prg tl current_label in match RegisterMap.find_opt prg.outputreg - (helper prg (CodeMap.find "main" prg.code) "main").registers + (aux prg (CodeMap.find "main" prg.code) "main").registers with Some x -> x | None -> failwith "Output register not found" diff --git a/lib/miniImp/Semantics.ml b/lib/miniImp/Semantics.ml index 7884884..75fe81e 100644 --- a/lib/miniImp/Semantics.ml +++ b/lib/miniImp/Semantics.ml @@ -152,8 +152,8 @@ let reduce (program: p_exp) (iin : int) : (int, [> error]) result = Main (vin, vout, expression) -> ( let mem : memory = { assignments = (VariableMap.empty |> VariableMap.add vin iin) } in - let* resultmem : memory = evaluate mem expression in - match VariableMap.find_opt vout resultmem.assignments with + let* result_mem : memory = evaluate mem expression in + match VariableMap.find_opt vout result_mem.assignments with | None -> Error (`AbsentAssignment ("The output variable is not defined (" ^ vout ^ ")")) diff --git a/lib/miniImp/Types.mli b/lib/miniImp/Types.mli index f318615..df015bd 100644 --- a/lib/miniImp/Types.mli +++ b/lib/miniImp/Types.mli @@ -1,35 +1,37 @@ type variable = string type p_exp = - Main of variable * variable * c_exp (* def main with input x output y as c *) + Main of variable * variable * c_exp + (* def main with input x output y as c *) and c_exp = Skip - | Assignment of variable * a_exp (* x := a *) - | Sequence of c_exp * c_exp (* c; c *) - | If of b_exp * c_exp * c_exp (* if b then c else c *) - | While of b_exp * c_exp (* while b do c *) - | For of c_exp * b_exp * c_exp * c_exp (* for (c; b; c) do c *) + | Assignment of variable * a_exp (* x := a *) + | Sequence of c_exp * c_exp (* c; c *) + | If of b_exp * c_exp * c_exp (* if b then c else c *) + | While of b_exp * c_exp (* while b do c *) + | For of c_exp * b_exp * c_exp * c_exp + (* for (c; b; c) do c *) and b_exp = - Boolean of bool (* v *) - | BAnd of b_exp * b_exp (* b && b *) - | BOr of b_exp * b_exp (* b || b *) - | BNot of b_exp (* not b *) - | BCmp of a_exp * a_exp (* a == a *) - | BCmpLess of a_exp * a_exp (* a < a *) - | BCmpLessEq of a_exp * a_exp (* a <= a *) - | BCmpGreater of a_exp * a_exp (* a > a *) - | BCmpGreaterEq of a_exp * a_exp (* a >= a *) + Boolean of bool (* v *) + | BAnd of b_exp * b_exp (* b && b *) + | BOr of b_exp * b_exp (* b || b *) + | BNot of b_exp (* not b *) + | BCmp of a_exp * a_exp (* a == a *) + | BCmpLess of a_exp * a_exp (* a < a *) + | BCmpLessEq of a_exp * a_exp (* a <= a *) + | BCmpGreater of a_exp * a_exp (* a > a *) + | BCmpGreaterEq of a_exp * a_exp (* a >= a *) and a_exp = - Variable of variable (* x *) - | Integer of int (* n *) - | Plus of a_exp * a_exp (* a + a *) - | Minus of a_exp * a_exp (* a - a *) - | Times of a_exp * a_exp (* a * a *) - | Division of a_exp * a_exp (* a / a *) - | Modulo of a_exp * a_exp (* a % a *) - | Power of a_exp * a_exp (* a ^ a *) - | PowerMod of a_exp * a_exp * a_exp (* a ^ a % a *) - | Rand of a_exp (* rand(0, a) *) + Variable of variable (* x *) + | Integer of int (* n *) + | Plus of a_exp * a_exp (* a + a *) + | Minus of a_exp * a_exp (* a - a *) + | Times of a_exp * a_exp (* a * a *) + | Division of a_exp * a_exp (* a / a *) + | Modulo of a_exp * a_exp (* a % a *) + | Power of a_exp * a_exp (* a ^ a *) + | PowerMod of a_exp * a_exp * a_exp (* a ^ a % a *) + | Rand of a_exp (* rand(0, a) *) val pp_p_exp : Format.formatter -> p_exp -> unit diff --git a/lib/miniImp/definedVariables.ml b/lib/miniImp/definedVariables.ml index 6e4b74f..4a74b04 100644 --- a/lib/miniImp/definedVariables.ml +++ b/lib/miniImp/definedVariables.ml @@ -5,7 +5,7 @@ module Variable = struct let pp (ppf: out_channel) (v: t) : unit = Printf.fprintf ppf "%s" v - let pplist (ppf: out_channel) (vv: t list) : unit = + let pp_list (ppf: out_channel) (vv: t list) : unit = List.iter (Printf.fprintf ppf "%s, ") vv let compare a b = @@ -19,7 +19,7 @@ module DVCeltSet = Set.Make(Variable) let variables (instr : DVCfg.elt) : DVCfg.internal list = - let helper (acc: DVCeltSet.t) (instr: DVCfg.elt) = + let aux (acc: DVCeltSet.t) (instr: DVCfg.elt) = match instr with | Nop -> acc @@ -37,7 +37,7 @@ let variables (instr : DVCfg.elt) : DVCfg.internal list = DVCeltSet.add r3.index acc in - helper DVCeltSet.empty instr |> DVCeltSet.to_list + aux DVCeltSet.empty instr |> DVCeltSet.to_list let variables_all (instructions : DVCfg.elt list) : DVCfg.internal list = List.fold_left (fun (acc: DVCeltSet.t) (instr: DVCfg.elt) -> @@ -45,7 +45,7 @@ let variables_all (instructions : DVCfg.elt list) : DVCfg.internal list = ) DVCeltSet.empty instructions |> DVCeltSet.to_list let variables_used (instr : DVCfg.elt) : DVCfg.internal list = - let helper (acc: DVCeltSet.t) (instr: DVCfg.elt) = + let aux (acc: DVCeltSet.t) (instr: DVCfg.elt) = match instr with | Nop | LoadI (_, _) -> @@ -60,7 +60,7 @@ let variables_used (instr : DVCfg.elt) : DVCfg.internal list = DVCeltSet.add r1.index acc in - helper DVCeltSet.empty instr |> DVCeltSet.to_list + aux DVCeltSet.empty instr |> DVCeltSet.to_list let variables_used_all (instructions : DVCfg.elt list) : DVCfg.internal list = List.fold_left (fun (acc: DVCeltSet.t) (instr: DVCfg.elt) -> @@ -69,7 +69,7 @@ let variables_used_all (instructions : DVCfg.elt list) : DVCfg.internal list = let variables_defined (instructions : DVCfg.elt) : DVCfg.internal list = - let helper (acc: DVCeltSet.t) (instr: DVCfg.elt) = + let aux (acc: DVCeltSet.t) (instr: DVCfg.elt) = match instr with | Nop | Store (_, _) -> acc @@ -81,116 +81,118 @@ let variables_defined (instructions : DVCfg.elt) : DVCfg.internal list = DVCeltSet.add r3.index acc in - helper DVCeltSet.empty instructions |> DVCeltSet.to_list + aux DVCeltSet.empty instructions |> DVCeltSet.to_list (* init function, assign the bottom to everything *) -let _init_bottom : (DVCfg.elt list -> DVCfg.internalnode) = - (fun l -> {internalin = []; - internalout = []; - internalbetween = (List.init (List.length l) (fun _ -> ([], [])))}) +let _init_bottom : (DVCfg.elt list -> DVCfg.internal_node) = + (fun l -> {internal_in = []; + internal_out = []; + internal_between = (List.init (List.length l) (fun _ -> ([], [])))} + ) (* init function, assign the top to everything *) -let init_top (all_variables) : (DVCfg.elt list -> DVCfg.internalnode) = - (fun l -> {internalin = all_variables; - internalout = all_variables; - internalbetween = (List.init (List.length l) +let init_top (all_variables) : (DVCfg.elt list -> DVCfg.internal_node) = + (fun l -> {internal_in = all_variables; + internal_out = all_variables; + internal_between = (List.init (List.length l) (fun _ -> (all_variables, all_variables)))}) -let lub (t: DVCfg.t) (node: Cfg.Node.t) : DVCfg.internalnode = - let previnternalvar = Cfg.NodeMap.find node t.internalvar in +let lub (t: DVCfg.t) (node: Cfg.Node.t) : DVCfg.internal_node = + let prev_internal_var = Cfg.NodeMap.find node t.internal_var in let code = match Cfg.NodeMap.find_opt node t.t.content with None -> [] | Some c -> c in - let newinternalbetween = ( + let new_internal_between = ( List.map (fun (code, (i, _o)) -> (i, Utility.unique_union i (variables_defined code))) - (List.combine code previnternalvar.internalbetween) + (List.combine code prev_internal_var.internal_between) ) in - let newinternalout = - match newinternalbetween with + let new_internal_out = + match new_internal_between with | [] -> - previnternalvar.internalin + prev_internal_var.internal_in | _ -> - let _, newinternalout = (Utility.last_list newinternalbetween) in + let _, newinternalout = (Utility.last_list new_internal_between) in newinternalout in - { previnternalvar with - internalbetween = newinternalbetween; - internalout = newinternalout } + { prev_internal_var with + internal_between = new_internal_between; + internal_out = new_internal_out } -let lucf (t: DVCfg.t) (node: Cfg.Node.t) : DVCfg.internalnode = - let previnternalvar = Cfg.NodeMap.find node t.internalvar in +let lucf (t: DVCfg.t) (node: Cfg.Node.t) : DVCfg.internal_node = + let prev_internal_var = Cfg.NodeMap.find node t.internal_var in if Option.equal (=) (Some node) t.t.initial then (* if L is initial set dvin to the "in" register *) - let newinternalin = ( - match t.t.inputOutputVar with + let new_internal_in = ( + match t.t.input_output_var with Some (i, _) -> [i] | None -> [] ) in - let newinternalbetween = ( (* set the dvin of each to the previous dvout *) - match previnternalvar.internalbetween with + let new_internal_between = ( + (* set the dvin of each to the previous dvout *) + match prev_internal_var.internal_between with [] -> [] - | [(_i, o)] -> [(newinternalin, o)] + | [(_i, o)] -> [(new_internal_in, o)] | (_i, o) :: btwrest -> - (newinternalin, o) :: ( + (new_internal_in, o) :: ( List.map (fun ((_i, o), (_previ, prevo)) -> (prevo, o)) - (Utility.combine_twice btwrest previnternalvar.internalbetween) + (Utility.combine_twice btwrest prev_internal_var.internal_between) ) ) in - { previnternalvar with - internalin = newinternalin; - internalbetween = newinternalbetween } + { prev_internal_var with + internal_in = new_internal_in; + internal_between = new_internal_between } else (* if L is not initial set dvin to the intersection of the previous node's dvouts *) - let prevnodes = Cfg.NodeMap.find node t.t.reverseEdges in - let newinternalin = ( - match prevnodes with + let prev_nodes = Cfg.NodeMap.find node t.t.reverse_edges in + let new_internal_in = ( + match prev_nodes with | [] -> [] | [prevnode] -> - (Cfg.NodeMap.find prevnode t.internalvar).internalout + (Cfg.NodeMap.find prevnode t.internal_var).internal_out | [prevnode1; prevnode2] -> Utility.unique_intersection - (Cfg.NodeMap.find prevnode1 t.internalvar).internalout - (Cfg.NodeMap.find prevnode2 t.internalvar).internalout + (Cfg.NodeMap.find prevnode1 t.internal_var).internal_out + (Cfg.NodeMap.find prevnode2 t.internal_var).internal_out | prevnode :: restnodes -> List.fold_left (* intersection of all previous nodes' dvout *) (fun acc prevnode -> Utility.unique_intersection acc - (Cfg.NodeMap.find prevnode t.internalvar).internalout) - (Cfg.NodeMap.find prevnode t.internalvar).internalout + (Cfg.NodeMap.find prevnode t.internal_var).internal_out) + (Cfg.NodeMap.find prevnode t.internal_var).internal_out restnodes ) in - let newinternalbetween = - match previnternalvar.internalbetween with + let new_internal_between = + match prev_internal_var.internal_between with [] -> [] - | [(_i, o)] -> [(newinternalin, o)] + | [(_i, o)] -> [(new_internal_in, o)] | (_i, o) :: btwrest -> - (newinternalin, o) :: ( + (new_internal_in, o) :: ( List.map (fun ((_i, o), (_previ, prevo)) -> (prevo, o)) - (Utility.combine_twice btwrest previnternalvar.internalbetween) + (Utility.combine_twice btwrest prev_internal_var.internal_between) ) in - { previnternalvar with - internalin = newinternalin; - internalbetween = newinternalbetween } + { prev_internal_var with + internal_in = new_internal_in; + internal_between = new_internal_between } -let update (t: DVCfg.t) (node: Cfg.Node.t) : DVCfg.internalnode = +let update (t: DVCfg.t) (node: Cfg.Node.t) : DVCfg.internal_node = let newt = - {t with internalvar = (Cfg.NodeMap.add node (lucf t node) t.internalvar)} + {t with internal_var = (Cfg.NodeMap.add node (lucf t node) t.internal_var)} in lub newt node @@ -204,7 +206,7 @@ let compute_defined_variables (cfg: RISCCfg.t) : DVCfg.t = (Cfg.NodeMap.to_list cfg.content) in let all_variables = - match cfg.inputOutputVar with + match cfg.input_output_var with | None -> all_variables | Some (i, o) -> Utility.unique_union all_variables [i;o] in @@ -215,18 +217,18 @@ let compute_defined_variables (cfg: RISCCfg.t) : DVCfg.t = let check_undefined_variables (dvcfg: DVCfg.t) : Variable.t list option = (* returns all undefined variables previously computed *) - let helper (node: Cfg.Node.t) (dvcfg: DVCfg.t) : Variable.t list option = + let aux (node: Cfg.Node.t) (dvcfg: DVCfg.t) : Variable.t list option = let code = match Cfg.NodeMap.find_opt node dvcfg.t.content with None -> [] | Some c -> c in - let internalvar = Cfg.NodeMap.find node dvcfg.internalvar in + let internal_var = Cfg.NodeMap.find node dvcfg.internal_var in let vua = variables_used_all code in let outvar = match (Option.equal (=) (Some node) dvcfg.t.terminal, - dvcfg.t.inputOutputVar, - internalvar.internalout) with + dvcfg.t.input_output_var, + internal_var.internal_out) with | (true, Some (_, outvar), vout) -> if List.mem outvar vout then None @@ -235,18 +237,18 @@ let check_undefined_variables (dvcfg: DVCfg.t) : Variable.t list option = None in - if Utility.inclusion vua (internalvar.internalin) then + if Utility.inclusion vua (internal_var.internal_in) then match outvar with None -> None | Some outvar -> Some [outvar] else (* the variable might be defined inside the block, so check all vin and return true only if all variables are properly defined *) - let vuabetween = List.map variables_used code in + let vua_between = List.map variables_used code in let undef_vars = List.fold_left (fun acc (codevars, (vin, _vout)) -> (Utility.subtraction codevars vin) @ acc) [] - (List.combine vuabetween internalvar.internalbetween) + (List.combine vua_between internal_var.internal_between) in match outvar, undef_vars with None, [] -> None @@ -255,7 +257,7 @@ let check_undefined_variables (dvcfg: DVCfg.t) : Variable.t list option = | Some outvar, undef_vars -> Some (outvar :: undef_vars) in Cfg.NodeSet.fold (fun node acc -> - match acc, (helper node dvcfg) with + match acc, (aux node dvcfg) with None, None -> None | None, Some x -> Some x | Some acc, None -> Some acc diff --git a/lib/miniImp/definedVariables.mli b/lib/miniImp/definedVariables.mli index be2577d..b27339c 100644 --- a/lib/miniImp/definedVariables.mli +++ b/lib/miniImp/definedVariables.mli @@ -3,7 +3,7 @@ open Analysis module Variable : sig type t val pp : out_channel -> t -> unit - val pplist : out_channel -> t list -> unit + val pp_list : out_channel -> t list -> unit end module RISCCfg = CfgRISC.RISCCfg diff --git a/lib/miniImp/liveVariables.ml b/lib/miniImp/liveVariables.ml index 35107df..ce82ecf 100644 --- a/lib/miniImp/liveVariables.ml +++ b/lib/miniImp/liveVariables.ml @@ -5,7 +5,7 @@ module Variable = struct let pp (ppf: out_channel) (v: t) : unit = Printf.fprintf ppf "%s" v - let pplist (ppf: out_channel) (vv: t list) : unit = + let pp_list (ppf: out_channel) (vv: t list) : unit = List.iter (Printf.fprintf ppf "%s, ") vv let compare a b = @@ -21,7 +21,7 @@ module DVCeltSet = Set.Make(Variable) let variables_used (instr : DVCfg.elt) : DVCfg.internal list = - let helper (acc: DVCeltSet.t) (instr: DVCfg.elt) = + let aux (acc: DVCeltSet.t) (instr: DVCfg.elt) = match instr with | Nop | LoadI (_, _) -> @@ -36,10 +36,10 @@ let variables_used (instr : DVCfg.elt) DVCeltSet.add r1.index acc in - helper DVCeltSet.empty instr |> DVCeltSet.to_list + aux DVCeltSet.empty instr |> DVCeltSet.to_list let variables_defined (instructions : DVCfg.elt) : DVCfg.internal list = - let helper (acc: DVCeltSet.t) (instr: DVCfg.elt) = + let aux (acc: DVCeltSet.t) (instr: DVCfg.elt) = match instr with | Nop | Store (_, _) -> acc @@ -51,10 +51,10 @@ let variables_defined (instructions : DVCfg.elt) : DVCfg.internal list = DVCeltSet.add r3.index acc in - helper DVCeltSet.empty instructions |> DVCeltSet.to_list + aux DVCeltSet.empty instructions |> DVCeltSet.to_list let variables (instruction : DVCfg.elt) : DVCfg.internal list = - let helper (acc: DVCeltSet.t) (instr: DVCfg.elt) = + let aux (acc: DVCeltSet.t) (instr: DVCfg.elt) = match instr with | Nop -> acc | Store (r1, r2) -> @@ -73,7 +73,7 @@ let variables (instruction : DVCfg.elt) : DVCfg.internal list = DVCeltSet.add r3.index acc in - helper DVCeltSet.empty instruction |> DVCeltSet.to_list + aux DVCeltSet.empty instruction |> DVCeltSet.to_list let variables_all (instructions : DVCfg.elt list) : DVCfg.internal list = List.fold_left (fun (acc: DVCeltSet.t) (instr: DVCfg.elt) -> @@ -81,43 +81,44 @@ let variables_all (instructions : DVCfg.elt list) : DVCfg.internal list = ) DVCeltSet.empty instructions |> DVCeltSet.to_list (* init function, assign the bottom to everything *) -let init : (DVCfg.elt list -> DVCfg.internalnode) = - (fun l -> {internalin = []; - internalout = []; - internalbetween = (List.init (List.length l) (fun _ -> ([], [])))}) +let init : (DVCfg.elt list -> DVCfg.internal_node) = + (fun l -> {internal_in = []; + internal_out = []; + internal_between = (List.init (List.length l) (fun _ -> ([], [])))} + ) -let lub (t: DVCfg.t) (node: Cfg.Node.t) : DVCfg.internalnode = - let previnternalvar = Cfg.NodeMap.find node t.internalvar in +let lub (t: DVCfg.t) (node: Cfg.Node.t) : DVCfg.internal_node = + let prev_internal_var = Cfg.NodeMap.find node t.internal_var in let code = match Cfg.NodeMap.find_opt node t.t.content with None -> [] | Some c -> c in - let newinternalbetween = ( + let new_internal_between = ( List.map (fun (code, (_i, o)) -> (Utility.unique_union (variables_used code) (Utility.subtraction o (variables_defined code)), o)) - (Utility.combine_twice code previnternalvar.internalbetween) + (Utility.combine_twice code prev_internal_var.internal_between) ) in - let newinternalin = - match newinternalbetween with - | [] -> previnternalvar.internalout + let new_internal_in = + match new_internal_between with + | [] -> prev_internal_var.internal_out | (i, _)::_ -> i in - { previnternalvar with - internalbetween = newinternalbetween; - internalin = newinternalin; } + { prev_internal_var with + internal_between = new_internal_between; + internal_in = new_internal_in; } -let lucf (t: DVCfg.t) (node: Cfg.Node.t) : DVCfg.internalnode = - let previnternalvar = Cfg.NodeMap.find node t.internalvar in +let lucf (t: DVCfg.t) (node: Cfg.Node.t) : DVCfg.internal_node = + let prev_internal_var = Cfg.NodeMap.find node t.internal_var in let newinternalout = ( if Option.equal (=) (Some node) t.t.terminal then ( - match t.t.inputOutputVar with + match t.t.input_output_var with Some (_, o) -> [o] | None -> [] ) else ( @@ -125,35 +126,35 @@ let lucf (t: DVCfg.t) (node: Cfg.Node.t) : DVCfg.internalnode = match nextnodes with | None -> [] | Some (node, None) -> - (Cfg.NodeMap.find node t.internalvar).internalin + (Cfg.NodeMap.find node t.internal_var).internal_in | Some (node1, Some node2) -> Utility.unique_union - (Cfg.NodeMap.find node1 t.internalvar).internalin - (Cfg.NodeMap.find node2 t.internalvar).internalin + (Cfg.NodeMap.find node1 t.internal_var).internal_in + (Cfg.NodeMap.find node2 t.internal_var).internal_in ) ) in - let newinternalbetween = ( - match List.rev previnternalvar.internalbetween with + let new_internal_between = ( + match List.rev prev_internal_var.internal_between with | [] -> [] | (i, _o) :: btwrest -> let btwrest = List.rev btwrest in let newbtwrest = List.map2 (fun (i, _o) (nexti, _nexto) -> (i, nexti)) btwrest - (Utility.drop_first_element_list previnternalvar.internalbetween) + (Utility.drop_first_element_list prev_internal_var.internal_between) in newbtwrest @ [(i, newinternalout)] ) in - { previnternalvar with - internalout = newinternalout; - internalbetween = newinternalbetween; } + { prev_internal_var with + internal_out = newinternalout; + internal_between = new_internal_between; } -let update (t: DVCfg.t) (node: Cfg.Node.t) : DVCfg.internalnode = - let newt = {t with internalvar = (Cfg.NodeMap.add node +let update (t: DVCfg.t) (node: Cfg.Node.t) : DVCfg.internal_node = + let newt = {t with internal_var = (Cfg.NodeMap.add node (lucf t node) - t.internalvar)} in + t.internal_var)} in lub newt node @@ -244,38 +245,38 @@ let optimize_cfg (t: DVCfg.t) : DVCfg.t = (t: DVCfg.t) (node: Cfg.Node.t) : (Variable.t VariableMap.t * DVCfg.t) = - let livevars = Cfg.NodeMap.find node t.internalvar in + let livevars = Cfg.NodeMap.find node t.internal_var in let code = match Cfg.NodeMap.find_opt node t.t.content with | None -> [] | Some x -> x in - let newcode, newassignments = + let new_code, new_assignments = (List.fold_left2 (fun (acc, assignments) btw code -> let na, nc = replace_code btw assignments code in (acc @ [nc], na) ) ([], assignments) - livevars.internalbetween + livevars.internal_between code) in let newcontent = Cfg.NodeMap.add node - newcode + new_code t.t.content in let newt = { t with t = { t.t with content = newcontent } } in - (newassignments, newt) + (new_assignments, newt) in (* ------------------- *) (* at least the input variable should be in the mapping *) let assignments = - match t.t.inputOutputVar with + match t.t.input_output_var with None -> VariableMap.empty | Some (i, _o) -> ( VariableMap.get_or_set_mapping VariableMap.empty [] i |> fst @@ -296,7 +297,7 @@ let optimize_cfg (t: DVCfg.t) : DVCfg.t = (* union of all in and out such that v is in the set *) let union : 'a list = List.fold_left - (fun (acc: 'a list) (node, (x: DVCfg.internalnode)) -> + (fun (acc: 'a list) (node, (x: DVCfg.internal_node)) -> (* not interested in internalin or internalout since information is mirrored into internalbetween *) List.fold_left2 @@ -315,12 +316,12 @@ let optimize_cfg (t: DVCfg.t) : DVCfg.t = (Utility.unique_union i o) acc ) acc - x.internalbetween + x.internal_between (Cfg.NodeMap.find_opt node t.t.content |> Option.value ~default:[]) ) [] - (Cfg.NodeMap.to_list t.internalvar) + (Cfg.NodeMap.to_list t.internal_var) in let assignments, _ = VariableMap.get_or_set_mapping assignments union v in @@ -338,8 +339,8 @@ let optimize_cfg (t: DVCfg.t) : DVCfg.t = { newt with t = { newt.t with - inputOutputVar = - match newt.t.inputOutputVar with + input_output_var = + match newt.t.input_output_var with None -> None | Some (i, o) -> ( match VariableMap.find_opt i mapping, diff --git a/lib/miniImp/reduceRegisters.ml b/lib/miniImp/reduceRegisters.ml index 420778e..8d58064 100644 --- a/lib/miniImp/reduceRegisters.ml +++ b/lib/miniImp/reduceRegisters.ml @@ -21,7 +21,7 @@ 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 - let helper (acc: int VariableMap.t) (instr: RISCCfg.elt) : int VariableMap.t = + let aux (acc: int VariableMap.t) (instr: RISCCfg.elt) : int VariableMap.t = match instr with | Nop -> acc @@ -39,7 +39,7 @@ let variables_frequency (instr : RISCCfg.elt) : (Variable.t * int) list = VariableMap.update r3.index add_one acc in - helper VariableMap.empty instr |> VariableMap.to_list + aux VariableMap.empty instr |> VariableMap.to_list (* computes syntactic frequency of all variables in the code *) let variables_all_frequency (instructions : RISCCfg.elt list) @@ -52,7 +52,7 @@ let variables_all_frequency (instructions : RISCCfg.elt list) VariableMap.empty instructions |> VariableMap.to_list -let reduceregisters (n: int) (cfg: RISCCfg.t) : RISCCfg.t = +let reduce_registers (n: int) (cfg: RISCCfg.t) : RISCCfg.t = (if n < 4 then ( failwith "ReduceRegisters: number of registers too small" ) else ()); @@ -68,7 +68,7 @@ let reduceregisters (n: int) (cfg: RISCCfg.t) : RISCCfg.t = (* add one to in and out variables *) let all_variables = - match cfg.inputOutputVar with + match cfg.input_output_var with | None -> all_variables | Some (i, _o) -> ( match List.assoc_opt i all_variables with @@ -78,7 +78,7 @@ let reduceregisters (n: int) (cfg: RISCCfg.t) : RISCCfg.t = in let all_variables = - match cfg.inputOutputVar with + match cfg.input_output_var with | None -> all_variables | Some (_i, o) -> ( match List.assoc_opt o all_variables with @@ -89,7 +89,7 @@ let reduceregisters (n: int) (cfg: RISCCfg.t) : RISCCfg.t = (* replace each operation with a list of operations that have the new registers or load from memory *) - let replaceregisters + let replace_registers (remappedregisters: Variable.t VariableMap.t) (memorymap: int VariableMap.t) (temporaryregisters: Variable.t list) @@ -315,7 +315,7 @@ let reduceregisters (n: int) (cfg: RISCCfg.t) : RISCCfg.t = cfg with content = Cfg.NodeMap.map ( fun x -> - replaceregisters + replace_registers most_frequent_mapping least_frequent_mapping ["1"; "2"] @@ -323,10 +323,10 @@ let reduceregisters (n: int) (cfg: RISCCfg.t) : RISCCfg.t = ) cfg.content} in - if newcfg.inputOutputVar = None + if newcfg.input_output_var = None then newcfg (* if no input or output variables we ignore *) else - let i, o = Option.get newcfg.inputOutputVar in + let i, o = Option.get newcfg.input_output_var in match (VariableMap.find_opt i most_frequent_mapping, VariableMap.find_opt o most_frequent_mapping, VariableMap.find_opt i least_frequent_mapping, @@ -335,15 +335,15 @@ let reduceregisters (n: int) (cfg: RISCCfg.t) : RISCCfg.t = newcfg.terminal ) 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) } + { newcfg with input_output_var = Some (i, o) } | 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 inputOutputVar = Some (i, o)} + { newcfg with input_output_var = Some (i, o)} | 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 terminalcontent = ( + let terminal_content = ( match Cfg.NodeMap.find_opt n newcfg.content with | None -> [] | Some x -> x @@ -351,14 +351,14 @@ let reduceregisters (n: int) (cfg: RISCCfg.t) : RISCCfg.t = Load ({index = "2"}, {index = "2"})] in let content = - Cfg.NodeMap.add n terminalcontent newcfg.content + Cfg.NodeMap.add n terminal_content newcfg.content in { newcfg with - inputOutputVar = Some (i, "2"); + input_output_var = Some (i, "2"); content = content } | None, Some o, Some _, _, _, None -> - { newcfg with inputOutputVar = Some (i, o) } + { newcfg with input_output_var = Some (i, o) } | None, Some o, Some mi, _, _, Some n -> ( (* the input simbol should be stored in memory *) let initialcontent = @@ -371,12 +371,12 @@ let reduceregisters (n: int) (cfg: RISCCfg.t) : RISCCfg.t = in let content = Cfg.NodeMap.add n initialcontent newcfg.content in { newcfg with - inputOutputVar = Some ("1", o); + input_output_var = Some ("1", o); content = content } ) | None, None, Some _, Some _, None, None -> - { newcfg with inputOutputVar = Some (i, o) } + { newcfg with input_output_var = Some (i, o) } | None, None, Some _, Some mo, None, Some n -> (* both simbols should be in memory *) let terminalcontent = ( @@ -390,7 +390,7 @@ let reduceregisters (n: int) (cfg: RISCCfg.t) : RISCCfg.t = Cfg.NodeMap.add n terminalcontent newcfg.content in { newcfg with - inputOutputVar = Some (i, "2"); + input_output_var = Some (i, "2"); content = content } | None, None, Some mi, Some _, Some n, None -> @@ -405,7 +405,7 @@ let reduceregisters (n: int) (cfg: RISCCfg.t) : RISCCfg.t = in let content = Cfg.NodeMap.add n initialcontent newcfg.content in { newcfg with - inputOutputVar = Some ("1", o); + input_output_var = Some ("1", o); content = content } | None, None, Some mi, Some mo, Some ni, Some no -> @@ -432,7 +432,7 @@ let reduceregisters (n: int) (cfg: RISCCfg.t) : RISCCfg.t = Cfg.NodeMap.add no terminalcontent content in { newcfg with - inputOutputVar = Some ("1", "2"); + input_output_var = Some ("1", "2"); content = content } | _ -> failwith ("ReduceRegisters: fail to partition a set, some" ^ diff --git a/lib/miniImp/reduceRegisters.mli b/lib/miniImp/reduceRegisters.mli index 5e04d8d..bed75a5 100644 --- a/lib/miniImp/reduceRegisters.mli +++ b/lib/miniImp/reduceRegisters.mli @@ -1,3 +1,3 @@ module RISCCfg = CfgRISC.RISCCfg -val reduceregisters : int -> RISCCfg.t -> RISCCfg.t +val reduce_registers : int -> RISCCfg.t -> RISCCfg.t diff --git a/test/testingAnalysis.ml b/test/testingAnalysis.ml index 48d1c7a..54d1ec7 100644 --- a/test/testingAnalysis.ml +++ b/test/testingAnalysis.ml @@ -8,7 +8,7 @@ let compute x i = LiveVariables.compute_live_variables |> LiveVariables.optimize_cfg |> LiveVariables.compute_cfg |> - ReduceRegisters.reduceregisters 4 |> + ReduceRegisters.reduce_registers 4 |> RISC.convert |> RISCSemantics.reduce

oPK6+gSchQqkrAf76ZXda(i7~F__KmMo%5D&%;& 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 N1L3osubsubsecition) +\setcounter{tocdepth}{3} +\setcounter{secnumdepth}{3} + +%% use bar instead of arrow for vectors +\renewcommand{\vec}[1]{\bar{#1}} +%% easy norm +\newcommand{\norm}[1]{\left\lvert#1\right\rvert} + +% argmin and argmax +\DeclareMathOperator*{\argmax}{argmax} +\DeclareMathOperator*{\argmin}{argmin} + +%% itemize use less vertical space (use olditemize for default behaviour) +\let\olditemize=\itemize%% old itemize +\let\endolditemize=\enditemize%% old end itemize +\renewenvironment{itemize}{\olditemize\itemsep-0.2em}{\endolditemize} + +%% items in itemize emph+box +%% usage: \ieb{Class:} for simple item +%% \ieb[4cm]{Class:} for specific size of box +\newcommand{\ieb}[2][2cm]{ + \makebox[#1][l]{\emph{#2}} +} %% TODO: replace with description environment (? maybe) + +% less vertical space around align & align* +\newcommand{\zerodisplayskips}{ + \setlength{\abovedisplayskip}{0pt} + \setlength{\belowdisplayskip}{0pt} + \setlength{\abovedisplayshortskip}{0pt} + \setlength{\belowdisplayshortskip}{0pt} +} + +% make dotfill use all the space available +\renewcommand{\dotfill}{ + \leavevmode\cleaders\hbox to 1.00em{\hss .\hss }\hfill\kern0pt } % chktex 1 chktex 26 + +\setlength{\fboxsep}{-\fboxrule} % for debugging + + +%% PACKAGE algorithm +\floatname{algorithm}{Algorithm} + + +%% PACKAGE tabularray +\UseTblrLibrary{amsmath} + + +%% PACKAGE color +\definecolor{red}{rgb}{1, 0.1, 0.1} +\definecolor{lightgreen}{rgb}{0.55, 0.87, 0.47} +\definecolor{gray}{rgb}{0.3, 0.3, 0.3} +\newcommand{\lgt}{\cellcolor{lightgreen}} %% light green in tables +\newcommand{\gry}{\textcolor{gray}} %% gray text +\newcommand{\rd}{\textcolor{red}} %% red text + +%% PACKAGE minipage +\newcommand{\thend}[1]{\begin{center} + \begin{minipage}[c][1em][c]{#1} + \dotfill{} + \end{minipage} +\end{center}} + + +%% PACKAGE thmtools + + +%% ......................................................................... %% +%% local changes +% \setcounter{secnumdepth}{0} + +\newcommand{\defeq}{\vcentcolon=} + +\lstdefinelanguage{miniimp}{ + keywords={if, then, else, skip, while, do, for, rand}, + keywordstyle=\color{blue}\bfseries, + identifierstyle=\color{black}, + sensitive=false, + morecomment=[s]{(*}{*)}, % chktex 9 + commentstyle=\color{gray}\ttfamily, + stringstyle=\color{red}\ttfamily, + escapeinside={£}{£}, + numbers=left, + stepnumber=1 +} + +\lstset{ + language=miniimp, + extendedchars=true, + basicstyle=\footnotesize\ttfamily, + showstringspaces=false, + showspaces=false, + tabsize=2, + breaklines=true, + showtabs=false +} + +%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - %% + +\title{Document} +\author{ + Elvis Rossi +} +\date{\today} + +%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - %% + +\begin{document} + +\input{report} + +\end{document} + +%% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - %% + +%%% Local Variables: +%%% TeX-command-extra-options: "-shell-escape" +%%% End: diff --git a/report/report.tex b/report/report.tex new file mode 100644 index 0000000..d9b9fd2 --- /dev/null +++ b/report/report.tex @@ -0,0 +1,425 @@ +\begin{section}{Semantics} + \begin{subsection}{MiniImp} + The semantic of the MiniImp language is implemented in the \href{../lib/miniImp/Semantics.mli}{Semantics.mli} and \href{../lib/miniImp/Semantics.ml}{Semantics.ml} file. + A \texttt{reduce} function is provided that transforms an AST into the evaluated value or an error. + The AST type is defined in \href{../lib/miniImp/Types.mli}{Types.mli} and in \href{../lib/miniImp/Types.ml}{Types.ml}. + + A program \texttt{p} is defined as follows: + \begin{grammar} +