diff --git a/.gitignore b/.gitignore index 18688d3d73c77f7fd791dacec3f54b205e563a59..8102f28a3b108b408666fdc397bdeb8ae8617a5d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ tests/**/*.cfg tests/**/*.cfg0 tests/**/*.cfg1 tests/**/*.cfg2 +tests/**/*.cfg3 tests/**/*.e.dump tests/**/*.e.html tests/**/*.exe diff --git a/Makefile b/Makefile index 5cbca0c5160443df7a06ede809f73f73e9c695f6..1cac3804801bbfad3bd8f9844b2e2217cdaf3fc1 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ main.native: -t grammar.html ./configure make -C src - cp src/main.native main.native + ln -sf src/main.native main.native clean: diff --git a/Sujet.pdf b/Sujet.pdf index 91b343c098fa8b5ffdb6a8055a32342e9d93248b..6cd99d9db240f72fa9761f1fbbec548db85b35dd 100644 Binary files a/Sujet.pdf and b/Sujet.pdf differ diff --git a/src/Makefile b/src/Makefile index 5626ef61adfad4bdad8d937bc415e6472ba56618..d90147d46204cbbc892890ff40cab1ae34baf963 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,10 +1,11 @@ -SRC=archi.ml ast.ml builtins.ml config.ml cfg_constprop.ml cfg_dead_assign.ml \ -cfg.ml cfg_print.ml cfg_gen.ml cfg_nop_elim.ml cfg_run.ml elang.ml \ -elang_print.ml elang_gen.ml elang_run.ml e_regexp.ml generated_parser.ml \ -lexer_generator.ml linear_dse.ml linear_liveness.ml linear.ml linear_print.ml \ -linear_gen.ml linear_run.ml ltl.ml ltl_print.ml ltl_gen.ml ltl_run.ml \ -ltl_debug.ml main.ml options.ml parser.ml prog.ml regalloc.ml report.ml \ -riscv.ml rtl.ml rtl_print.ml rtl_gen.ml rtl_run.ml symbols.ml utils.ml +SRC=archi.ml ast.ml builtins.ml config.ml cfg_constprop.ml cfg_dead_assign.ml \ +cfg.ml cfg_print.ml cfg_gen.ml cfg_liveness.ml cfg_loops.ml cfg_nop_elim.ml \ +cfg_run.ml elang.ml elang_print.ml elang_gen.ml elang_run.ml e_regexp.ml \ +generated_parser.ml lexer_generator.ml linear_dse.ml linear_liveness.ml \ +linear.ml linear_print.ml linear_gen.ml linear_run.ml ltl.ml ltl_print.ml \ +ltl_gen.ml ltl_run.ml ltl_debug.ml main.ml options.ml parser.ml prog.ml \ +regalloc.ml report.ml riscv.ml rtl.ml rtl_print.ml rtl_gen.ml rtl_run.ml \ +symbols.ml utils.ml TG = main.native diff --git a/src/cfg.ml b/src/cfg.ml index f03588be3d22cf08796d66b63306abb379595ebc..a174394316f394d4f1b9d8d4bd382d2e99d462e0 100644 --- a/src/cfg.ml +++ b/src/cfg.ml @@ -26,6 +26,29 @@ type cfg_fun = { type cprog = cfg_fun prog +(* [succs cfg n] gives the successors of a node [n] in a CFG [cfg]. *) +let succs cfg n = + match Hashtbl.find_option cfg n with + | None -> [] + | Some (Cprint (_, s)) + | Some (Cassign (_, _, s)) -> [s] + | Some (Creturn _) -> [] + | Some (Ccmp (_, s1, s2)) -> [s1;s2] + | Some (Cnop s) -> [s] + + +(* [preds n] gives the list of predecessors of a node [n]. *) +let preds cfgfunbody n = + Hashtbl.fold (fun m m' acc -> + match m' with + | Cassign (_, _, s) + | Cprint (_, s) + | Cnop s -> if s = n then Set.add m acc else acc + | Creturn _ -> acc + | Ccmp (_, s1, s2) -> if s1 = n || s2 = n then Set.add m acc else acc + ) cfgfunbody Set.empty + + let size_binop b e1 e2 = 1 + e1 + e2 @@ -49,3 +72,22 @@ let rec size_instr (i: cfg_node) : int = let size_fun f = Hashtbl.fold (fun k v acc -> acc + size_instr v) f 0 + +let fixpoint + (transfer: (int, cfg_node) Hashtbl.t -> (int, 'a) Hashtbl.t -> int -> 'a) + (init: (int, cfg_node) Hashtbl.t -> int -> cfg_node -> 'a) + (cfg: (int, cfg_node) Hashtbl.t) : + (int, 'a) Hashtbl.t = + let res = Hashtbl.map (fun k v -> init cfg k v) cfg in + let iter res = + Hashtbl.fold (fun n oldstate changed -> + let newstate = transfer cfg res n in + Hashtbl.replace res n newstate; + changed || not (Set.equal newstate oldstate) + ) res false in + let rec fix () = + if iter res + then fix () + else () in + fix (); + res diff --git a/src/cfg_dead_assign.ml b/src/cfg_dead_assign.ml index 2d8128451dc9ac55b330b330724b3c8a61e202cf..c94352c820fc90baf6146294ebc6dd7e5e6e30ce 100644 --- a/src/cfg_dead_assign.ml +++ b/src/cfg_dead_assign.ml @@ -2,54 +2,8 @@ open Batteries open Cfg open Prog open Utils - -(* Liveness analysis *) - -(* [vars_in_expr e] returns the set of variables appearing in [e]. *) -let rec vars_in_expr (e: expr) = - Set.empty - -(* [live_cfg_node node live_after] gives the set of variables live before the - node [node], given the set [live_after] of variables live after this node. *) -let live_cfg_node (node: cfg_node) (live_after: string Set.t) = - live_after - -(* [succs cfg n] gives the successors of a node [n] in a CFG [cfg]. *) -let succs cfg n = - match Hashtbl.find_option cfg n with - | None -> [] - | Some (Cprint (_, s)) - | Some (Cfg.Cassign (_, _, s)) -> [s] - | Some (Cfg.Creturn _) -> [] - | Some (Cfg.Ccmp (_, s1, s2)) -> [s1;s2] - | Some (Cnop s) -> [s] - -(* Computes the set of live variables after a node [n] in a CFG [cfg]. - [lives] is a mapping from CFG node identifier to the set of variables that - are live before this node. -*) -let live_after_node cfg n (lives: (int, string Set.t) Hashtbl.t) : string Set.t = - Set.empty - -(* [live_cfg_nodes cfg lives] makes one iteration of the fixpoint computation. - - This returns a boolean that is true if some progress has been made in this - iteration (the set of variables live at at least one node has changed), false - otherwise. *) -let live_cfg_nodes cfg (lives : (int, string Set.t) Hashtbl.t) = - false - -(* [live_cfg_fun f] computes the set of live variables at each point by - iterating [live_cfg_nodes] as long as progress is made. *) -let live_cfg_fun ({ cfgfunargs; cfgfunbody; cfgentry }: cfg_fun) = - let lives : (int, string Set.t) Hashtbl.t = Hashtbl.create 17 in - let rec aux () = - if live_cfg_nodes cfgfunbody lives - then aux () - else () in - aux (); - lives - +open Cfg_liveness + (* Dead Assign Elimination *) (* [dead_assign_elimination_fun f] performs DAE on function [f]. *) diff --git a/src/cfg_liveness.ml b/src/cfg_liveness.ml new file mode 100644 index 0000000000000000000000000000000000000000..63f3a650956da0dacbf7323cd2d91e3a0b7ed74a --- /dev/null +++ b/src/cfg_liveness.ml @@ -0,0 +1,50 @@ +open Batteries +open Cfg +open Prog +open Utils + +(* Liveness analysis *) + +(* [vars_in_expr e] returns the set of variables appearing in [e]. *) +let rec vars_in_expr (e: expr) = + Set.empty + +(* [live_cfg_node node live_after] gives the set of variables live before the + node [node], given the set [live_after] of variables live after this node. *) +let live_cfg_node (node: cfg_node) (live_after: string Set.t) = + live_after + +(* Computes the set of live variables after a node [n] in a CFG [cfg]. + [lives] is a mapping from CFG node identifier to the set of variables that + are live before this node. +*) +let live_after_node cfg n (lives: (int, string Set.t) Hashtbl.t) : string Set.t = + Set.empty + +(* [live_cfg_nodes cfg lives] makes one iteration of the fixpoint computation. + + This returns a boolean that is true if some progress has been made in this + iteration (the set of variables live at at least one node has changed), false + otherwise. *) +let live_cfg_nodes cfg (lives : (int, string Set.t) Hashtbl.t) = + false + +(* [live_cfg_fun f] computes the set of live variables at each point by + iterating [live_cfg_nodes] as long as progress is made. *) +let live_cfg_fun ({ cfgfunargs; cfgfunbody; cfgentry }: cfg_fun) = + let lives : (int, string Set.t) Hashtbl.t = Hashtbl.create 17 in + let rec aux () = + if live_cfg_nodes cfgfunbody lives + then aux () + else () in + aux (); + lives + +let live_cfg_fun ({ cfgfunargs; cfgfunbody; cfgentry }: cfg_fun) = + fixpoint (fun cfg lives n -> + match Hashtbl.find_option cfg n with + | Some cn -> live_cfg_node cn (live_after_node cfg n lives) + | None -> failwith "Unknown node" + ) + (fun cfg n cn -> Set.empty) + cfgfunbody diff --git a/src/cfg_loops.ml b/src/cfg_loops.ml new file mode 100644 index 0000000000000000000000000000000000000000..9175654367eaba4f9802629c335f52084a528bef --- /dev/null +++ b/src/cfg_loops.ml @@ -0,0 +1,11 @@ +open Batteries +open Elang +open Cfg_gen +open Cfg_print +open Cfg_liveness +open Cfg +open Utils +open Prog + + +let optimize_loop_cfg cfg = cfg diff --git a/src/cfg_run.ml b/src/cfg_run.ml index 137e622a5e2ca636af8c3e299c78f18815c2c325..e09ccf9875519ee45d8d8020cc9667662ea3138b 100644 --- a/src/cfg_run.ml +++ b/src/cfg_run.ml @@ -7,7 +7,8 @@ open Cfg open Utils open Builtins -let rec eval_cfgexpr st : expr -> int res = function +let rec eval_cfgexpr st (e: expr) : int res = + match e with | Ebinop(b, e1, e2) -> eval_cfgexpr st e1 >>= fun v1 -> eval_cfgexpr st e2 >>= fun v2 -> diff --git a/src/e_regexp.ml b/src/e_regexp.ml index 53ad47274840914e55992e7827f4552917a81744..1872417beb4b354c0cebbd57061a78486f2df761 100644 --- a/src/e_regexp.ml +++ b/src/e_regexp.ml @@ -108,7 +108,11 @@ let list_regexp = (Cat (char_regexp '\'', Cat (char_range (List.filter (fun c -> c <> '\'' && c <> '\\') alphabet), char_regexp '\'')), - fun s -> Some (SYM_CHARACTER (String.get s 1))); + fun s -> + match String.get s 1 with + | a -> Some (SYM_CHARACTER a) + | exception Invalid_argument _ -> Some (SYM_CHARACTER '\x00') + ); (Cat (char_regexp '\'', Cat (char_regexp '\\', Cat (char_range (char_list_of_string "\\tn0"), char_regexp '\''))), @@ -118,6 +122,7 @@ let list_regexp = | 't' -> Some (SYM_CHARACTER '\t') | '0' -> Some (SYM_CHARACTER '\x00') | _ -> None + | exception _ -> Some (SYM_CHARACTER '\x00') ); (Cat (char_regexp '"', Cat (Star ( diff --git a/src/main.ml b/src/main.ml index a86df17b1069f6431ac463a8db06ccaebad571a9..1314160c8637843ce07c128d0a52b09543e9ab3e 100644 --- a/src/main.ml +++ b/src/main.ml @@ -36,6 +36,8 @@ open Report open Options open Lexer_generator +open Cfg_loops + let tokenize file = Lexer_generator.tokenize_file file >>= fun tokens -> OK (List.map (fun tok -> (tok, None)) tokens) @@ -49,6 +51,7 @@ let speclist = ("-e-run", Arg.Set e_run, "Run Elang program."); ("-cfg-dump", Arg.String (fun s -> cfg_dump := Some s), "Output CFG file."); ("-cfg-run", Arg.Set cfg_run, "Run CFG program."); + ("-cfg-run-after-loop", Arg.Set cfg_run_after_loop, "Run CFG program after loop optimization."); ("-cfg-run-after-cp", Arg.Set cfg_run_after_cp, "Run CFG program after constant propagation."); ("-cfg-run-after-dae", Arg.Set cfg_run_after_dae, "Run CFG program after dead assign elimination."); ("-cfg-run-after-ne", Arg.Set cfg_run_after_ne, "Run CFG program after nop elimination."); @@ -67,6 +70,7 @@ let speclist = ("-all-run", Arg.Unit (fun () -> e_run := true; cfg_run := true; + cfg_run_after_loop := true; cfg_run_after_cp := true; cfg_run_after_dae := true; cfg_run_after_ne := true; @@ -289,27 +293,37 @@ let _ = run "Elang" !e_run eval_eprog ep; cfg_prog_of_eprog ep >>! fun cfg -> + record_compile_result ~data:([(`Assoc (List.map (fun (fname,Prog.Gfun cfgfun) -> (fname, `Int (Cfg.size_fun cfgfun.cfgfunbody))) cfg))]) "CFG"; + dump !cfg_dump dump_cfg_prog cfg (call_dot "cfg" "CFG"); run "CFG" !cfg_run eval_cfgprog cfg; - let cfg = constant_propagation cfg in - record_compile_result "ConstProp"; + let cfg = optimize_loop_cfg cfg in + record_compile_result ~data:([(`Assoc (List.map (fun (fname,Prog.Gfun cfgfun) -> (fname, `Int (Cfg.size_fun cfgfun.cfgfunbody))) cfg))]) "CFG loops"; dump (!cfg_dump >*> fun s -> s ^ "0") dump_cfg_prog cfg + (call_dot "cfg-after-loop" "CFG after loop optim"); + run "CFG after loop optim" !cfg_run_after_loop eval_cfgprog cfg; + + + let cfg = constant_propagation cfg in + record_compile_result ~data:([(`Assoc (List.map (fun (fname,Prog.Gfun cfgfun) -> (fname, `Int (Cfg.size_fun cfgfun.cfgfunbody))) cfg))]) "Constprop"; + dump (!cfg_dump >*> fun s -> s ^ "1") dump_cfg_prog cfg (call_dot "cfg-after-cstprop" "CFG after Constant Propagation"); run "CFG after constant_propagation" !cfg_run_after_cp eval_cfgprog cfg; let cfg = dead_assign_elimination cfg in - record_compile_result "DeadAssign"; - dump (!cfg_dump >*> fun s -> s ^ "1") dump_cfg_prog cfg + record_compile_result ~data:([(`Assoc (List.map (fun (fname,Prog.Gfun cfgfun) -> (fname, `Int (Cfg.size_fun cfgfun.cfgfunbody))) cfg))]) "DeadAssign"; + dump (!cfg_dump >*> fun s -> s ^ "2") dump_cfg_prog cfg (call_dot "cfg-after-dae" "CFG after DAE"); run "CFG after dead_assign_elimination" !cfg_run_after_dae eval_cfgprog cfg; let cfg = nop_elimination cfg in - record_compile_result "NopElim"; - dump (!cfg_dump >*> fun s -> s ^ "2") dump_cfg_prog cfg + record_compile_result ~data:([(`Assoc (List.map (fun (fname,Prog.Gfun cfgfun) -> (fname, `Int (Cfg.size_fun cfgfun.cfgfunbody))) cfg))]) "NopElim"; + dump (!cfg_dump >*> fun s -> s ^ "3") dump_cfg_prog cfg (call_dot "cfg-after-nop" "CFG after NOP elim"); run "CFG after nop_elimination" !cfg_run_after_ne eval_cfgprog cfg; + let rtl = rtl_of_cfg cfg in dump !rtl_dump dump_rtl_prog rtl (fun file () -> add_to_report "rtl" "RTL" (Code (file_contents file))); diff --git a/src/options.ml b/src/options.ml index 39fd3f8ea014713fbafea899ea81b5905f2c0b2c..63572f6f206a59b6e4c1d4af823c49616789ad03 100644 --- a/src/options.ml +++ b/src/options.ml @@ -7,6 +7,7 @@ let e_dump : string option ref = ref None let e_run = ref false let cfg_dump : string option ref = ref None let cfg_run = ref false +let cfg_run_after_loop = ref false let cfg_run_after_cp = ref false let cfg_run_after_dae = ref false let cfg_run_after_ne = ref false diff --git a/src/utils.ml b/src/utils.ml index 3fa505c8085735f0bf9bbb9e91668eb23d8f065c..3b1b122340e4ba34b0e8abe98c26e4a55ba80196 100644 --- a/src/utils.ml +++ b/src/utils.ml @@ -276,4 +276,13 @@ let string_of_int_list l = let string_of_int_set s = string_of_int_list (Set.to_list s) +let string_of_string_set v = + String.concat ", " (Set.to_list v) +let string_of_int_int_set v = + String.concat ", " (List.map (fun (x,y) -> Printf.sprintf "(%d,%d)" x y) (Set.to_list v)) + +let string_of_int_option v = + match v with + | None -> "undef" + | Some x -> string_of_int x diff --git a/tests/basic/loop.e b/tests/basic/loop.e new file mode 100644 index 0000000000000000000000000000000000000000..24652c93c8907d546c77f66446068755466bedd4 --- /dev/null +++ b/tests/basic/loop.e @@ -0,0 +1,15 @@ +main(){ + i = 10; + a = 2; + res = 0; + while(i > 0){ + x = a + a; + if(a > 4){ + res = res + x; + } else { + res = res - x; + } + i = i - 1; + } + return res; +} diff --git a/tests/basic/loop.e.expect_14_12_3_8_12 b/tests/basic/loop.e.expect_14_12_3_8_12 new file mode 100644 index 0000000000000000000000000000000000000000..919b014322ed6e46ea283467b56f755be461e3c5 --- /dev/null +++ b/tests/basic/loop.e.expect_14_12_3_8_12 @@ -0,0 +1 @@ +{"output": "", "error": null, "retval": -40} \ No newline at end of file diff --git a/tests/basic/loop.e.expect_1_2_3 b/tests/basic/loop.e.expect_1_2_3 new file mode 100644 index 0000000000000000000000000000000000000000..919b014322ed6e46ea283467b56f755be461e3c5 --- /dev/null +++ b/tests/basic/loop.e.expect_1_2_3 @@ -0,0 +1 @@ +{"output": "", "error": null, "retval": -40} \ No newline at end of file diff --git a/tests/basic/loop2.e b/tests/basic/loop2.e new file mode 100644 index 0000000000000000000000000000000000000000..985a3053936c14f6507c8142a5b0b59990eec3e9 --- /dev/null +++ b/tests/basic/loop2.e @@ -0,0 +1,12 @@ +main(){ + i = 20; + while(i > 0){ + if(i < 5){ + a = 5; + } else { + a = 6; + } + i = i - 1; + } + return i; +} diff --git a/tests/basic/loop2.e.expect_14_12_3_8_12 b/tests/basic/loop2.e.expect_14_12_3_8_12 new file mode 100644 index 0000000000000000000000000000000000000000..42f3b7be56a5a32dc517bc2d95cd06ba49b8acae --- /dev/null +++ b/tests/basic/loop2.e.expect_14_12_3_8_12 @@ -0,0 +1 @@ +{"output": "", "error": null, "retval": 0} \ No newline at end of file diff --git a/tests/basic/loop2.e.expect_1_2_3 b/tests/basic/loop2.e.expect_1_2_3 new file mode 100644 index 0000000000000000000000000000000000000000..42f3b7be56a5a32dc517bc2d95cd06ba49b8acae --- /dev/null +++ b/tests/basic/loop2.e.expect_1_2_3 @@ -0,0 +1 @@ +{"output": "", "error": null, "retval": 0} \ No newline at end of file diff --git a/tests/basic/loop3.e b/tests/basic/loop3.e new file mode 100644 index 0000000000000000000000000000000000000000..a99573b44587eb67ff019e540007894f357b019d --- /dev/null +++ b/tests/basic/loop3.e @@ -0,0 +1,14 @@ +main(){ + i = 20; + c = 8; + a = 5; + b = 0; + while(i > 0){ + if(i < 5){ + a = 4 + c; + } + b = b + a; + i = i - 1; + } + return b; +} diff --git a/tests/basic/loop3.e.expect_14_12_3_8_12 b/tests/basic/loop3.e.expect_14_12_3_8_12 new file mode 100644 index 0000000000000000000000000000000000000000..feb4775c473e7c8f42a997c5dc5a04ce52c73eaa --- /dev/null +++ b/tests/basic/loop3.e.expect_14_12_3_8_12 @@ -0,0 +1 @@ +{"output": "", "error": null, "retval": 128} \ No newline at end of file diff --git a/tests/basic/loop3.e.expect_1_2_3 b/tests/basic/loop3.e.expect_1_2_3 new file mode 100644 index 0000000000000000000000000000000000000000..feb4775c473e7c8f42a997c5dc5a04ce52c73eaa --- /dev/null +++ b/tests/basic/loop3.e.expect_1_2_3 @@ -0,0 +1 @@ +{"output": "", "error": null, "retval": 128} \ No newline at end of file diff --git a/tests/basic/loop4.e b/tests/basic/loop4.e new file mode 100644 index 0000000000000000000000000000000000000000..53bd91a153e7077c6e48bf7fa6799b84ce0b65a7 --- /dev/null +++ b/tests/basic/loop4.e @@ -0,0 +1,20 @@ +main(){ + i = 10; + a = 2; + res = 0; + while(i > 0){ + x = a + a; + y = 1 + x; + if(i > 4){ + res = res + x; + } else { + if (i < 2){ + res = res - y; + } else { + res = res + x - y; + } + } + i = i - 1; + } + return res; +} diff --git a/tests/basic/loop4.e.expect_14_12_3_8_12 b/tests/basic/loop4.e.expect_14_12_3_8_12 new file mode 100644 index 0000000000000000000000000000000000000000..4c5f6717cdc873d39fe41829359cd3390a60c009 --- /dev/null +++ b/tests/basic/loop4.e.expect_14_12_3_8_12 @@ -0,0 +1 @@ +{"output": "", "error": null, "retval": 16} \ No newline at end of file diff --git a/tests/basic/loop4.e.expect_1_2_3 b/tests/basic/loop4.e.expect_1_2_3 new file mode 100644 index 0000000000000000000000000000000000000000..4c5f6717cdc873d39fe41829359cd3390a60c009 --- /dev/null +++ b/tests/basic/loop4.e.expect_1_2_3 @@ -0,0 +1 @@ +{"output": "", "error": null, "retval": 16} \ No newline at end of file diff --git a/tests/test.py b/tests/test.py index 4a70644bd914583cf60c43f1c1b85261cd6eb582..3ec9d0f27c56dcdd86bf327c513a23ab950bbd26 100755 --- a/tests/test.py +++ b/tests/test.py @@ -21,6 +21,7 @@ parser.add_argument("-p", "--passes", nargs='+', default=["e-run", "cfg-run", + "cfg-run-after-loop", "cfg-run-after-cp", "cfg-run-after-dae", "cfg-run-after-ne",