From cefba8486c1a2c84253ba957f83180d862fd8a54 Mon Sep 17 00:00:00 2001 From: Youssef <youssef.sellami@student-cs.fr> Date: Fri, 14 Mar 2025 10:24:44 +0100 Subject: [PATCH] functions : CFG --- src/cfg.ml | 9 ++++-- src/cfg_gen.ml | 9 ++++++ src/cfg_liveness.ml | 2 ++ src/cfg_nop_elim.ml | 3 +- src/cfg_print.ml | 6 ++-- src/cfg_run.ml | 75 +++++++++++++++++++++++++++++++-------------- src/elang_run.ml | 2 +- 7 files changed, 77 insertions(+), 29 deletions(-) diff --git a/src/cfg.ml b/src/cfg.ml index 7c4cb02..2a60c97 100644 --- a/src/cfg.ml +++ b/src/cfg.ml @@ -8,6 +8,7 @@ type expr = | Eunop of unop * expr | Eint of int | Evar of string + | Ecall of string * expr list type cfg_node = | Cassign of string * expr * int @@ -15,6 +16,7 @@ type cfg_node = | Cprint of expr * int | Ccmp of expr * int * int | Cnop of int + | Ccall of string * expr list * int type cfg_fun = { cfgfunargs: string list; @@ -35,7 +37,7 @@ let succs cfg n = | Some (Creturn _) -> Set.empty | Some (Ccmp (_, s1, s2)) -> Set.of_list [s1;s2] | Some (Cnop s) -> Set.singleton s - + | Some (Ccall (_, _, s)) -> Set.singleton s (* [preds cfg n] donne l'ensemble des prédécesseurs d'un nœud [n] dans un CFG [cfg] *) @@ -44,7 +46,8 @@ let preds cfgfunbody n = match m' with | Cassign (_, _, s) | Cprint (_, s) - | Cnop s -> if s = n then Set.add m acc else acc + | Cnop s + | Ccall (_, _, 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 @@ -62,6 +65,7 @@ let rec size_expr (e: expr) : int = | Eunop (u, e) -> size_unop u (size_expr e) | Eint _ -> 1 | Evar _ -> 1 + | Ecall (_, args) -> 1 + List.fold_left (fun acc arg -> acc + size_expr arg) 0 args let size_instr (i: cfg_node) : int = match (i : cfg_node) with @@ -70,6 +74,7 @@ let size_instr (i: cfg_node) : int = | Cprint (e, _) -> 1 + (size_expr e) | Ccmp (e, _, _) -> 1 + size_expr e | Cnop _ -> 1 + | Ccall (_, args, _) -> 1 + List.fold_left (fun acc arg -> acc + size_expr arg) 0 args let size_fun f = Hashtbl.fold (fun _ v acc -> acc + size_instr v) f 0 diff --git a/src/cfg_gen.ml b/src/cfg_gen.ml index 18509ef..ff2aa1b 100644 --- a/src/cfg_gen.ml +++ b/src/cfg_gen.ml @@ -25,6 +25,9 @@ let rec cfg_expr_of_eexpr (e: Elang.expr) : expr res = | Elang.Eint i -> OK (Eint i) | Elang.Evar v -> OK (Evar v) + | Elang.Ecall (f, args) -> + list_map_res cfg_expr_of_eexpr args >>= fun es -> + OK (Ecall (f, es)) (* [cfg_node_of_einstr next cfg succ i] builds the CFG node(s) that correspond to the E instruction [i]. @@ -70,6 +73,11 @@ let rec cfg_node_of_einstr (next: int) (cfg : (int, cfg_node) Hashtbl.t) cfg_expr_of_eexpr e >>= fun e -> Hashtbl.replace cfg next (Cprint (e,succ)); OK (next, next + 1) + | Elang.Icall (f, args) -> + list_map_res cfg_expr_of_eexpr args >>= fun es -> + Hashtbl.replace cfg next (Ccall (f, es, succ)); + OK (next, next + 1) + (* Some nodes may be unreachable after the CFG is entirely generated. The [reachable_nodes n cfg] constructs the set of node identifiers that are @@ -86,6 +94,7 @@ let rec reachable_nodes n (cfg: (int,cfg_node) Hashtbl.t) = | Some (Creturn _) -> reach | Some (Ccmp (_, s1, s2)) -> reachable_aux s1 (reachable_aux s2 reach) + | Some (Ccall (_, _, succ)) -> reachable_aux succ reach in reachable_aux n Set.empty (* [cfg_fun_of_efun f] builds the CFG for E function [f]. *) diff --git a/src/cfg_liveness.ml b/src/cfg_liveness.ml index 609e3ad..aa40206 100644 --- a/src/cfg_liveness.ml +++ b/src/cfg_liveness.ml @@ -12,6 +12,7 @@ let rec vars_in_expr (e: expr) = | Evar s -> Set.singleton s | Ebinop (b, e1, e2) -> Set.union (vars_in_expr e1) (vars_in_expr e2) | Eunop (u, e) -> vars_in_expr e + | Ecall (f, args) -> set_concat (List.map vars_in_expr args) (* [live_after_node cfg n] renvoie l'ensemble des variables vivantes après le nœud [n] dans un CFG [cfg]. [lives] est l'état courant de l'analyse, @@ -34,6 +35,7 @@ let live_cfg_node (node: cfg_node) (live_after: string Set.t) = | Cprint (e, i) -> vars_in_expr e | Ccmp (e, i1, i2) -> vars_in_expr e | Cnop (i) -> Set.empty + | Ccall (f, args, i) -> vars_in_expr (Ecall (f, args)) in let def node = match node with | Cassign (s, e, i) -> Set.singleton s diff --git a/src/cfg_nop_elim.ml b/src/cfg_nop_elim.ml index 142d096..dba2b51 100644 --- a/src/cfg_nop_elim.ml +++ b/src/cfg_nop_elim.ml @@ -67,7 +67,8 @@ let replace_succs nop_succs (n: cfg_node) = | Cprint (e, i) -> Cprint (e, replace_succ nop_succs i) | Ccmp (e, i1, i2) -> Ccmp (e, replace_succ nop_succs i1, replace_succ nop_succs i2) | Cnop i -> Cnop (replace_succ nop_succs i) - | _ -> n + | Creturn e -> Creturn e + | Ccall (f, args, i) -> Ccall (f, args, replace_succ nop_succs i) (* [nop_elim_fun f] applique la fonction [replace_succs] à chaque nœud du CFG. *) let nop_elim_fun ({ cfgfunargs; cfgfunbody; cfgentry } as f: cfg_fun) = diff --git a/src/cfg_print.ml b/src/cfg_print.ml index 6ec810a..a452410 100644 --- a/src/cfg_print.ml +++ b/src/cfg_print.ml @@ -8,6 +8,7 @@ let rec dump_cfgexpr : expr -> string = function | Eunop(u, e) -> Format.sprintf "(%s %s)" (dump_unop u) (dump_cfgexpr e) | Eint i -> Format.sprintf "%d" i | Evar s -> Format.sprintf "%s" s + | Ecall (f, args) -> Format.sprintf "%s(%s)" f (String.concat ", " (List.map dump_cfgexpr args)) let dump_list_cfgexpr l = l |> List.map dump_cfgexpr |> String.concat ", " @@ -17,7 +18,8 @@ let dump_arrows oc fname n (node: cfg_node) = match node with | Cassign (_, _, succ) | Cprint (_, succ) - | Cnop succ -> + | Cnop succ + | Ccall (_, _, succ) -> Format.fprintf oc "n_%s_%d -> n_%s_%d\n" fname n fname succ | Creturn _ -> () | Ccmp (_, succ1, succ2) -> @@ -32,7 +34,7 @@ let dump_cfg_node oc (node: cfg_node) = | Creturn e -> Format.fprintf oc "return %s" (dump_cfgexpr e) | Ccmp (e, _, _) -> Format.fprintf oc "%s" (dump_cfgexpr e) | Cnop _ -> Format.fprintf oc "nop" - + | Ccall (f, args, _) -> Format.fprintf oc "%s(%s)" f (String.concat ", " (List.map dump_cfgexpr args)) let dump_liveness_state oc ht state = Hashtbl.iter (fun n cn -> diff --git a/src/cfg_run.ml b/src/cfg_run.ml index 6557acd..e2e4212 100644 --- a/src/cfg_run.ml +++ b/src/cfg_run.ml @@ -7,52 +7,81 @@ open Cfg open Utils open Builtins -let rec eval_cfgexpr st (e: expr) : int res = +let rec eval_cfgexpr oc st cp (e: expr) : (int * int state) res = match e with | Ebinop(b, e1, e2) -> - eval_cfgexpr st e1 >>= fun v1 -> - eval_cfgexpr st e2 >>= fun v2 -> + eval_cfgexpr oc st cp e1 >>= fun (v1, st') -> + eval_cfgexpr oc st' cp e2 >>= fun (v2, st'') -> let v = eval_binop b v1 v2 in - OK v + OK (v, st'') | Eunop(u, e) -> - eval_cfgexpr st e >>= fun v1 -> + eval_cfgexpr oc st cp e >>= fun (v1, st') -> let v = (eval_unop u v1) in - OK v - | Eint i -> OK i + OK (v, st') + | Eint i -> OK (i, st) | Evar s -> begin match Hashtbl.find_option st.env s with - | Some v -> OK v + | Some v -> OK (v, st) | None -> Error (Printf.sprintf "Unknown variable %s\n" s) end - -let rec eval_cfginstr oc st ht (n: int): (int * int state) res = + | Ecall (f, args) -> + List.fold_left ( + fun (acc : (int list * int state) res) (arg : expr) -> + match acc with + | Error msg -> Error msg + | OK (l, st') -> + match eval_cfgexpr oc st' cp arg with + | Error msg -> Error msg + | OK (i, st'') -> OK ((l@[i]), st'') + ) (OK([], st)) args >>= fun (int_args, st') -> + find_function cp f >>= fun found_f -> + match eval_cfgfun oc st' cp f found_f int_args with + | Error msg -> Error msg + | OK (None, st'') -> Error (Format.sprintf "CFG: Function %s doesn't have a return value.\n" f) + | OK (Some ret, st'') -> OK (ret, st'') + +and eval_cfginstr oc st cp ht (n: int): (int * int state) res = match Hashtbl.find_option ht n with | None -> Error (Printf.sprintf "Invalid node identifier\n") | Some node -> match node with | Cnop succ -> - eval_cfginstr oc st ht succ + eval_cfginstr oc st cp ht succ | Cassign(v, e, succ) -> - eval_cfgexpr st e >>= fun i -> - Hashtbl.replace st.env v i; - eval_cfginstr oc st ht succ + eval_cfgexpr oc st cp e >>= fun (i, st') -> + Hashtbl.replace st'.env v i; + eval_cfginstr oc st' cp ht succ | Ccmp(cond, i1, i2) -> - eval_cfgexpr st cond >>= fun i -> - if i = 0 then eval_cfginstr oc st ht i2 else eval_cfginstr oc st ht i1 + eval_cfgexpr oc st cp cond >>= fun (i, st') -> + if i = 0 then eval_cfginstr oc st' cp ht i2 else eval_cfginstr oc st' cp ht i1 | Creturn(e) -> - eval_cfgexpr st e >>= fun e -> - OK (e, st) + eval_cfgexpr oc st cp e >>= fun (e, st') -> + OK (e, st') | Cprint(e, succ) -> - eval_cfgexpr st e >>= fun e -> + eval_cfgexpr oc st cp e >>= fun (e, st') -> Format.fprintf oc "%d\n" e; - eval_cfginstr oc st ht succ + eval_cfginstr oc st' cp ht succ + | Ccall (f, args, succ) -> + List.fold_left ( + fun (acc : (int list * int state) res) (arg : expr) -> + match acc with + | Error msg -> Error msg + | OK (l, st') -> + match eval_cfgexpr oc st' cp arg with + | Error msg -> Error msg + | OK (i, st'') -> OK ((l@[i]), st'') + ) (OK([], st)) args + >>= fun (int_args, st') -> + find_function cp f >>= fun found_f -> + eval_cfgfun oc st' cp f found_f int_args >>= fun (ret, st'') -> + eval_cfginstr oc st'' cp ht succ -let eval_cfgfun oc st cfgfunname { cfgfunargs; +and eval_cfgfun oc st cp cfgfunname { cfgfunargs; cfgfunbody; cfgentry} vargs = let st' = { st with env = Hashtbl.create 17 } in match List.iter2 (fun a v -> Hashtbl.replace st'.env a v) cfgfunargs vargs with - | () -> eval_cfginstr oc st' cfgfunbody cfgentry >>= fun (v, st') -> + | () -> eval_cfginstr oc st' cp cfgfunbody cfgentry >>= fun (v, st') -> OK (Some v, {st' with env = st.env}) | exception Invalid_argument _ -> Error (Format.sprintf "CFG: Called function %s with %d arguments, expected %d.\n" @@ -64,7 +93,7 @@ let eval_cfgprog oc cp memsize params = find_function cp "main" >>= fun f -> let n = List.length f.cfgfunargs in let params = take n params in - eval_cfgfun oc st "main" f params >>= fun (v, st) -> + eval_cfgfun oc st cp "main" f params >>= fun (v, st) -> OK v diff --git a/src/elang_run.ml b/src/elang_run.ml index e4509f6..880e938 100644 --- a/src/elang_run.ml +++ b/src/elang_run.ml @@ -100,7 +100,7 @@ and eval_einstr oc (st: int state) (ep: eprog) (ins: instr) : | Iif (e, i1, i2) -> (match eval_eexpr oc st ep e with | Error msg -> Error msg - | OK (v, st') -> if v = 1 then eval_einstr oc st' ep i1 else eval_einstr oc st' ep i2) + | OK (v, st') -> if v = 0 then eval_einstr oc st' ep i2 else eval_einstr oc st' ep i1) | Iwhile (e, i) -> (match eval_eexpr oc st ep e with | Error msg -> Error msg -- GitLab