From 6df998ceb1b07d0dcc84a9e6edcf41ee64e2458f Mon Sep 17 00:00:00 2001 From: Youssef <youssef.sellami@student-cs.fr> Date: Mon, 3 Mar 2025 12:21:10 +0100 Subject: [PATCH 01/11] lexer and parser --- ecomp | 1 + expr_grammar_action.g | 65 ++++++++++++- src/e_regexp.ml | 58 ++++++------ src/lexer_generator.ml | 156 ++++++++++++++++++++++--------- src/test_lexer.ml | 2 +- tests/basic/just_a_variable_37.e | 4 +- 6 files changed, 205 insertions(+), 81 deletions(-) create mode 120000 ecomp diff --git a/ecomp b/ecomp new file mode 120000 index 0000000..b09fe7b --- /dev/null +++ b/ecomp @@ -0,0 +1 @@ +src/_build/default/main.exe \ No newline at end of file diff --git a/expr_grammar_action.g b/expr_grammar_action.g index 9ab7ecd..d7734d1 100644 --- a/expr_grammar_action.g +++ b/expr_grammar_action.g @@ -20,13 +20,68 @@ axiom S open Batteries open Utils + (* TODO *) - let resolve_associativity term other = + let rec resolve_associativity (term : tree) (other : (tag * tree) list) = (* TODO *) - term - - + match List.rev other with + | [] -> term + | (high_tag, right_side)::rest -> Node(high_tag, [resolve_associativity term (List.rev rest); right_side]) } rules -S -> FUNDEFS SYM_EOF { Node (Tlistglobdef, []) } +S -> FUNDEFS SYM_EOF { Node(Tlistglobdef, $1) } +FUNDEFS -> FUNDEF FUNDEFS { Node(Tfundef, $1)::$2 } +FUNDEFS -> { [] } +FUNDEF -> IDENTIFIER SYM_LPARENTHESIS LPARAMS SYM_RPARENTHESIS SYM_LBRACE LINSTRS SYM_RBRACE { [Node(Tfunname, [$1]); Node(Tfunargs, $3); Node(Tfunbody, [$6])] } + +LPARAMS -> IDENTIFIER REST_PARAMS { $1::$2 } +LPARAMS -> { [] } +REST_PARAMS -> SYM_COMMA LPARAMS { $2 } +REST_PARAMS -> { [] } + +LINSTRS -> INSTR INSTRS { Node(Tblock, $1::$2) } +LINSTRS -> { NullLeaf } +INSTRS -> INSTR INSTRS { $1::$2 } +INSTRS -> { [] } + +INSTR -> SYM_IF SYM_LPARENTHESIS EXPR SYM_RPARENTHESIS SYM_LBRACE LINSTRS SYM_RBRACE ELSE { Node(Tif, [$3; $6; $8]) } +INSTR -> SYM_WHILE SYM_LPARENTHESIS EXPR SYM_RPARENTHESIS SYM_LBRACE LINSTRS SYM_RBRACE { Node(Twhile, [$3; $6]) } +INSTR -> SYM_RETURN EXPR SYM_SEMICOLON { Node(Treturn, [$2]) } +INSTR -> SYM_PRINT SYM_LPARENTHESIS EXPR SYM_RPARENTHESIS SYM_SEMICOLON { Node(Tprint, [$3]) } +INSTR -> IDENTIFIER SYM_ASSIGN EXPR SYM_SEMICOLON { Node(Tassign, [$1; $3]) } + +ELSE -> SYM_ELSE SYM_LBRACE LINSTRS SYM_RBRACE { $3 } +ELSE -> { NullLeaf } + +EXPR -> EQ_EXPR EQ_EXPRS { resolve_associativity $1 $2 } +EQ_EXPR -> CMP_EXPR CMP_EXPRS { resolve_associativity $1 $2 } +CMP_EXPR -> ADD_EXPR ADD_EXPRS { resolve_associativity $1 $2 } +ADD_EXPR -> MUL_EXPR MUL_EXPRS { resolve_associativity $1 $2 } +MUL_EXPR -> FACTOR { $1 } + +EQ_EXPRS -> SYM_EQUALITY EQ_EXPR EQ_EXPRS { (Tceq, $2)::$3 } +EQ_EXPRS -> SYM_NOTEQ EQ_EXPR EQ_EXPRS { (Tne, $2)::$3 } +EQ_EXPRS -> { [] } + +CMP_EXPRS -> SYM_LT CMP_EXPR CMP_EXPRS { (Tclt, $2)::$3 } +CMP_EXPRS -> SYM_LEQ CMP_EXPR CMP_EXPRS { (Tcle, $2)::$3 } +CMP_EXPRS -> SYM_GT CMP_EXPR CMP_EXPRS { (Tcgt, $2)::$3 } +CMP_EXPRS -> SYM_GEQ CMP_EXPR CMP_EXPRS { (Tcge, $2)::$3 } +CMP_EXPRS -> { [] } + +ADD_EXPRS -> SYM_PLUS ADD_EXPR ADD_EXPRS { (Tadd, $2)::$3 } +ADD_EXPRS -> SYM_MINUS ADD_EXPR ADD_EXPRS { (Tsub, $2)::$3 } +ADD_EXPRS -> { [] } + +MUL_EXPRS -> SYM_ASTERISK MUL_EXPR MUL_EXPRS { (Tmul, $2)::$3 } +MUL_EXPRS -> SYM_DIV MUL_EXPR MUL_EXPRS { (Tdiv, $2)::$3 } +MUL_EXPRS -> SYM_MOD MUL_EXPR MUL_EXPRS { (Tmod, $2)::$3 } +MUL_EXPRS -> { [] } + +FACTOR -> INTEGER { $1 } +FACTOR -> IDENTIFIER { $1 } +FACTOR -> SYM_LPARENTHESIS EXPR SYM_RPARENTHESIS { $2 } + +IDENTIFIER -> SYM_IDENTIFIER {StringLeaf $1} +INTEGER -> SYM_INTEGER {IntLeaf $1} diff --git a/src/e_regexp.ml b/src/e_regexp.ml index bbc5b20..a87413d 100644 --- a/src/e_regexp.ml +++ b/src/e_regexp.ml @@ -69,35 +69,35 @@ let list_regexp : (regexp * (string -> token option)) list = (keyword_regexp "while", fun _ -> Some (SYM_WHILE)); (keyword_regexp "int", fun _ -> Some (SYM_INT)); (* begin TODO *) - (Eps, fun _ -> Some (SYM_VOID)); - (Eps, fun _ -> Some (SYM_CHAR)); - (Eps, fun _ -> Some (SYM_IF)); - (Eps, fun _ -> Some (SYM_ELSE)); - (Eps, fun _ -> Some (SYM_RETURN)); - (Eps, fun _ -> Some (SYM_PRINT)); - (Eps, fun _ -> Some (SYM_STRUCT)); - (Eps, fun _ -> Some (SYM_POINT)); - (Eps, fun _ -> Some (SYM_PLUS)); - (Eps, fun _ -> Some (SYM_MINUS)); - (Eps, fun _ -> Some (SYM_ASTERISK)); - (Eps, fun _ -> Some (SYM_DIV)); - (Eps, fun _ -> Some (SYM_MOD)); - (Eps, fun _ -> Some (SYM_LBRACE)); - (Eps, fun _ -> Some (SYM_RBRACE)); - (Eps, fun _ -> Some (SYM_LBRACKET)); - (Eps, fun _ -> Some (SYM_RBRACKET)); - (Eps, fun _ -> Some (SYM_LPARENTHESIS)); - (Eps, fun _ -> Some (SYM_RPARENTHESIS)); - (Eps, fun _ -> Some (SYM_SEMICOLON)); - (Eps, fun _ -> Some (SYM_COMMA)); - (Eps, fun _ -> Some (SYM_ASSIGN)); - (Eps, fun _ -> Some (SYM_EQUALITY)); - (Eps, fun _ -> Some (SYM_NOTEQ)); - (Eps, fun _ -> Some (SYM_LT)); - (Eps, fun _ -> Some (SYM_GT)); - (Eps, fun _ -> Some (SYM_LEQ)); - (Eps, fun _ -> Some (SYM_GEQ)); - (Eps, fun s -> Some (SYM_IDENTIFIER s)); + (keyword_regexp "void", fun _ -> Some (SYM_VOID)); + (keyword_regexp "void", fun _ -> Some (SYM_CHAR)); + (keyword_regexp "if", fun _ -> Some (SYM_IF)); + (keyword_regexp "else", fun _ -> Some (SYM_ELSE)); + (keyword_regexp "return", fun _ -> Some (SYM_RETURN)); + (keyword_regexp "print", fun _ -> Some (SYM_PRINT)); + (keyword_regexp "struct", fun _ -> Some (SYM_STRUCT)); + (char_regexp '.', fun _ -> Some (SYM_POINT)); + (char_regexp '+', fun _ -> Some (SYM_PLUS)); + (char_regexp '-', fun _ -> Some (SYM_MINUS)); + (char_regexp '*', fun _ -> Some (SYM_ASTERISK)); + (char_regexp '/', fun _ -> Some (SYM_DIV)); + (char_regexp '%', fun _ -> Some (SYM_MOD)); + (char_regexp '{', fun _ -> Some (SYM_LBRACE)); + (char_regexp '}', fun _ -> Some (SYM_RBRACE)); + (char_regexp '[', fun _ -> Some (SYM_LBRACKET)); + (char_regexp ']', fun _ -> Some (SYM_RBRACKET)); + (char_regexp '(', fun _ -> Some (SYM_LPARENTHESIS)); + (char_regexp ')', fun _ -> Some (SYM_RPARENTHESIS)); + (char_regexp ';', fun _ -> Some (SYM_SEMICOLON)); + (char_regexp ',', fun _ -> Some (SYM_COMMA)); + (char_regexp '=', fun _ -> Some (SYM_ASSIGN)); + (Cat(char_regexp '=', char_regexp '='), fun _ -> Some (SYM_EQUALITY)); + (Cat(char_regexp '!', char_regexp '='), fun _ -> Some (SYM_NOTEQ)); + (char_regexp '<', fun _ -> Some (SYM_LT)); + (char_regexp '>', fun _ -> Some (SYM_GT)); + (Cat(char_regexp '<', char_regexp '='), fun _ -> Some (SYM_LEQ)); + (Cat(char_regexp '>', char_regexp '='), fun _ -> Some (SYM_GEQ)); + (Cat(Alt(letter_regexp, char_regexp '_'), Star identifier_material), fun s -> Some (SYM_IDENTIFIER s)); (* end TODO *) (Cat(keyword_regexp "//", Cat(Star (char_range (List.filter (fun c -> c <> '\n') alphabet)), diff --git a/src/lexer_generator.ml b/src/lexer_generator.ml index 06192ef..e75d1ee 100644 --- a/src/lexer_generator.ml +++ b/src/lexer_generator.ml @@ -45,41 +45,70 @@ let empty_nfa = (* Concaténation de NFAs. *) let cat_nfa n1 n2 = - (* TODO *) - empty_nfa + (* TODO *) + { + nfa_states = n1.nfa_states @ n2.nfa_states; + nfa_initial = n1.nfa_initial; + nfa_final = n2.nfa_final; + nfa_step = fun q -> + if List.mem q (List.map (fun x -> fst(x)) n1.nfa_final) + then n1.nfa_step(q)@List.map (fun x -> (None, x)) n2.nfa_initial + else n1.nfa_step(q)@n2.nfa_step(q) + } (* Alternatives de NFAs *) let alt_nfa n1 n2 = - (* TODO *) - empty_nfa + (* TODO *) + { + nfa_states = n1.nfa_states @ n2.nfa_states; + nfa_initial = n1.nfa_initial @ n2.nfa_initial; + nfa_final = n1.nfa_final @ n2.nfa_final; + nfa_step = fun q -> n1.nfa_step(q) @ n2.nfa_step(q) + } (* Répétition de NFAs *) (* t est de type [string -> token option] *) let star_nfa n t = - (* TODO *) - empty_nfa - + (* TODO *) + { + nfa_states = n.nfa_states; + nfa_initial = n.nfa_initial; + nfa_final = (List.map (fun x -> fst x, t) n.nfa_final) @ (List.map (fun x -> x, t) n.nfa_initial); + nfa_step = fun q -> + if List.mem q (List.map (fun x -> fst x) n.nfa_final) + then n.nfa_step q @ List.map (fun x -> None, x) n.nfa_initial + else n.nfa_step q + } (* [nfa_of_regexp r freshstate t] construit un NFA qui reconnaît le même langage que l'expression régulière [r]. - [freshstate] correspond à un entier pour lequel il n'y a pas encore d'état dans + [freshstate] correspond à un entier pour leq uel il n'y a pas encore d'état dans le nfa. Il suffit d'incrémenter [freshstate] pour obtenir de nouveaux états non utilisés. [t] est une fonction du type [string -> token option] utile pour les états finaux. *) let rec nfa_of_regexp r freshstate t = match r with | Eps -> { nfa_states = [freshstate]; - nfa_initial = [freshstate]; - nfa_final = [(freshstate,t)]; - nfa_step = fun q -> []}, freshstate + 1 + nfa_initial = [freshstate]; + nfa_final = [(freshstate,t)]; + nfa_step = fun q -> []}, freshstate + 1 | Charset c -> { nfa_states = [freshstate; freshstate + 1]; nfa_initial = [freshstate]; nfa_final = [freshstate + 1, t]; nfa_step = fun q -> if q = freshstate then [(Some c, freshstate + 1)] else [] }, freshstate + 2 - (* TODO *) - | _ -> empty_nfa, freshstate - + (* TODO *) + | Cat(r1, r2) -> + let n1, intermediate_freshstate = nfa_of_regexp r1 freshstate t + in let n2, final_freshstate = nfa_of_regexp r2 intermediate_freshstate t + in (cat_nfa n1 n2, final_freshstate); + | Alt(r1, r2) -> + let n1, intermediate_freshstate = nfa_of_regexp r1 freshstate t + in let n2, final_freshstate = nfa_of_regexp r2 intermediate_freshstate t + in (alt_nfa n1 n2, final_freshstate); + | Star r -> + let n, final_freshstate = nfa_of_regexp r freshstate t + in star_nfa n t, final_freshstate; (* Deterministic Finite Automaton (DFA) *) (* Les états d'un DFA [dfa_state] sont des ensembles d'entiers. @@ -119,21 +148,26 @@ let epsilon_closure (n: nfa) (s: nfa_state) : nfa_state set = (* La fonction [traversal visited s] effectue un parcours de l'automate en partant de l'état [s], et en suivant uniquement les epsilon-transitions. *) let rec traversal (visited: nfa_state set) (s: nfa_state) : nfa_state set = - (* TODO *) - visited - in - traversal Set.empty s + (* TODO *) + let direct_epsilon_closure = List.map (fun x -> snd x) (List.filter (fun x -> fst x == None) (n.nfa_step s)) + in let not_visited_direct_epsilon_closure = List.filter (fun x -> not(Set.mem x visited)) direct_epsilon_closure + in let visited_with_s = Set.add s visited + in if Set.mem s visited + then visited + else List.fold_left (fun acc x -> Set.union (traversal visited_with_s x) acc) visited_with_s not_visited_direct_epsilon_closure + in traversal Set.empty s (* [epsilon_closure_set n ls] calcule l'union des epsilon-fermeture de chacun des états du NFA [n] dans l'ensemble [ls]. *) let epsilon_closure_set (n: nfa) (ls: nfa_state set) : nfa_state set = - (* TODO *) - ls + (* TODO *) + let set_of_epsilon_closures = (Set.map (fun x -> epsilon_closure n x) ls) + in Set.fold (fun acc x -> Set.union x acc) set_of_epsilon_closures Set.empty (* [dfa_initial_state n] calcule l'état initial de l'automate déterminisé. *) let dfa_initial_state (n: nfa) : dfa_state = - (* TODO *) - Set.empty + (* TODO *) + epsilon_closure_set n (Set.of_list n.nfa_initial) (* Construction de la table de transitions de l'automate DFA. *) @@ -180,19 +214,17 @@ let assoc_merge_vals (l : ('a * 'b) list) : ('a * 'b set) list = | Some vl -> (k, Set.add v vl)::List.remove_assoc k acc ) [] l -let rec build_dfa_table (table: (dfa_state, (char * dfa_state) list) Hashtbl.t) - (n: nfa) - (ds: dfa_state) : unit = +let rec build_dfa_table (table: (dfa_state, (char * dfa_state) list) Hashtbl.t) (n: nfa) (ds: dfa_state) : unit = match Hashtbl.find_option table ds with | Some _ -> () | None -> (* [transitions] contient les transitions du DFA construites - * à partir des transitions du NFA comme décrit auparavant *) + * à partir des transitions du NFA comme décrit auparavant *) let transitions : (char * dfa_state) list = - (* TODO *) - [] - in - Hashtbl.replace table ds transitions; + (* TODO *) + let t = Set.fold (fun x acc -> acc @ n.nfa_step x) ds [] + in List.map (fun x -> fst x, epsilon_closure_set n (snd x)) (assoc_merge_vals (assoc_distribute_key (assoc_throw_none t))) + in Hashtbl.replace table ds transitions; List.iter (build_dfa_table table n) (List.map snd transitions) (* Calcul des états finaux de l'automate DFA *) @@ -223,17 +255,31 @@ let priority t = | _ -> 0 (* [min_priority l] renvoie le token de [l] qui a la plus petite priorité, ou - [None] si la liste [l] est vide. *) + [None] si la liste [l] est vide. *) let min_priority (l: token list) : token option = - (* TODO *) - None + (* TODO *) + match l with + | [] -> None + | _ -> Some(List.fold_left (fun x acc -> if priority x < priority acc then x else acc) SYM_EOF l) (* [dfa_final_states n dfa_states] renvoie la liste des états finaux du DFA, accompagnés du token qu'ils reconnaissent. *) let dfa_final_states (n: nfa) (dfa_states: dfa_state list) : (dfa_state * (string -> token option)) list = - (* TODO *) - [] + (* TODO *) + let dfa_final_states_list : nfa_state set list= + let is_final q = List.mem q (List.map (fun x -> fst(x)) n.nfa_final) + in List.filter (fun ds -> Set.exists is_final ds) dfa_states + in let function_of_nfa_state ns = + assoc_opt ns n.nfa_final + in let functions_of_dfa_state (ds : dfa_state) = + (List.filter_map function_of_nfa_state (Set.to_list ds)) (* use List.filter_map instead of Set.filter_map to avoid the error*) + in let constructed_function ds = + fun s -> + let images_of_s : token list + = List.filter_map (fun (t) -> t s) (functions_of_dfa_state ds) + in min_priority images_of_s + in List.map (fun (ds : dfa_state ) -> ds, constructed_function ds) dfa_final_states_list (* Construction de la relation de transition du DFA. *) @@ -241,8 +287,13 @@ let dfa_final_states (n: nfa) (dfa_states: dfa_state list) : est la table générée par [build_dfa_table], définie ci-dessus. *) let make_dfa_step (table: (dfa_state, (char * dfa_state) list) Hashtbl.t) = fun (q: dfa_state) (a: char) -> - (* TODO *) - None + (* TODO *) + match Hashtbl.find_option table q with + | None -> None + | Some l -> + match List.filter (fun x -> fst x = a) l with + | [] -> None + | (c, ds)::rest -> Some ds (* Finalement, on assemble tous ces morceaux pour construire l'automate. La fonction [dfa_of_nfa n] vous est grâcieusement offerte. *) @@ -306,13 +357,29 @@ type lexer_result = *) let tokenize_one (d : dfa) (w: char list) : lexer_result * char list = - let rec recognize (q: dfa_state) (w: char list) - (current_token: char list) (last_accepted: lexer_result * char list) - : lexer_result * char list = - (* TODO *) - last_accepted - in - recognize d.dfa_initial w [] (LRerror, w) + let rec recognize (q: dfa_state) (w: char list) (current_word: char list) (last_accepted: lexer_result * char list) : lexer_result * char list = + (* TODO *) + let token_function ds = + assoc_opt ds d.dfa_final + in let new_accepted = + match token_function q with + | None -> last_accepted + | Some t -> + match (t (string_of_char_list current_word)) with + | None -> LRskip, w + | Some tok -> LRtoken tok, w + in if List.is_empty w + then new_accepted + else let next_state_option = d.dfa_step q (hd w) + in match next_state_option with + | None -> new_accepted + | Some next_state -> recognize next_state (tl w) (current_word@[hd w]) new_accepted + + in recognize d.dfa_initial w [] (LRerror, w) + +(* + recognize 3 "iler" "wh" (SYM_(w), "hile") -> (SYM_WHILE, "r") +*) (* La fonction [tokenize_all d w] répète l'application de [tokenize_one] tant qu'on n'est pas arrivé à la fin du fichier (token [SYM_EOF]). Encore une fois, @@ -326,6 +393,7 @@ let rec tokenize_all (d: dfa) (w: char list) : (token list * char list) = if token = SYM_EOF then ([], w) else tokenize_all d w in + (*print_endline (string_of_symbol token);*) (token :: tokens, w) diff --git a/src/test_lexer.ml b/src/test_lexer.ml index 72ffda4..40c9f8e 100644 --- a/src/test_lexer.ml +++ b/src/test_lexer.ml @@ -42,7 +42,7 @@ let () = ] in (* Décommentez la ligne suivante pour tester sur la vraie liste d'expressions régulières. *) - (* let regexp_list = list_regexp in *) + (*let regexp_list = list_regexp in*) List.iteri (fun i (rg, _) -> Printf.printf "%d: %s\n" i (string_of_regexp rg)) regexp_list; diff --git a/tests/basic/just_a_variable_37.e b/tests/basic/just_a_variable_37.e index 51add73..3551b56 100644 --- a/tests/basic/just_a_variable_37.e +++ b/tests/basic/just_a_variable_37.e @@ -1,4 +1,4 @@ main(){ - just_a_variable = 37; - return just_a_variable; + just_a_variable = 37; + return just_a_variable; } -- GitLab From b0d4b068aacd1924f234083c97a48a1049fc3929 Mon Sep 17 00:00:00 2001 From: Youssef <youssef.sellami@student-cs.fr> Date: Mon, 3 Mar 2025 14:58:18 +0100 Subject: [PATCH 02/11] grammar correction : an instruction can be a block --- expr_grammar_action.g | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/expr_grammar_action.g b/expr_grammar_action.g index d7734d1..d1d0e24 100644 --- a/expr_grammar_action.g +++ b/expr_grammar_action.g @@ -33,7 +33,7 @@ rules S -> FUNDEFS SYM_EOF { Node(Tlistglobdef, $1) } FUNDEFS -> FUNDEF FUNDEFS { Node(Tfundef, $1)::$2 } FUNDEFS -> { [] } -FUNDEF -> IDENTIFIER SYM_LPARENTHESIS LPARAMS SYM_RPARENTHESIS SYM_LBRACE LINSTRS SYM_RBRACE { [Node(Tfunname, [$1]); Node(Tfunargs, $3); Node(Tfunbody, [$6])] } +FUNDEF -> IDENTIFIER SYM_LPARENTHESIS LPARAMS SYM_RPARENTHESIS INSTR { [Node(Tfunname, [$1]); Node(Tfunargs, $3); Node(Tfunbody, [$5])] } LPARAMS -> IDENTIFIER REST_PARAMS { $1::$2 } LPARAMS -> { [] } @@ -46,10 +46,11 @@ INSTRS -> INSTR INSTRS { $1::$2 } INSTRS -> { [] } INSTR -> SYM_IF SYM_LPARENTHESIS EXPR SYM_RPARENTHESIS SYM_LBRACE LINSTRS SYM_RBRACE ELSE { Node(Tif, [$3; $6; $8]) } -INSTR -> SYM_WHILE SYM_LPARENTHESIS EXPR SYM_RPARENTHESIS SYM_LBRACE LINSTRS SYM_RBRACE { Node(Twhile, [$3; $6]) } +INSTR -> SYM_WHILE SYM_LPARENTHESIS EXPR SYM_RPARENTHESIS INSTR { Node(Twhile, [$3; $5]) } INSTR -> SYM_RETURN EXPR SYM_SEMICOLON { Node(Treturn, [$2]) } INSTR -> SYM_PRINT SYM_LPARENTHESIS EXPR SYM_RPARENTHESIS SYM_SEMICOLON { Node(Tprint, [$3]) } INSTR -> IDENTIFIER SYM_ASSIGN EXPR SYM_SEMICOLON { Node(Tassign, [$1; $3]) } +INSTR -> SYM_LBRACE LINSTRS SYM_RBRACE { $2 } ELSE -> SYM_ELSE SYM_LBRACE LINSTRS SYM_RBRACE { $3 } ELSE -> { NullLeaf } -- GitLab From ece99692f775b3cbf9ba5cc2e957d6598d87ecaa Mon Sep 17 00:00:00 2001 From: Youssef <youssef.sellami@student-cs.fr> Date: Mon, 3 Mar 2025 17:15:10 +0100 Subject: [PATCH 03/11] grammar correction : use of Targ tag --- expr_grammar_action.g | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/expr_grammar_action.g b/expr_grammar_action.g index d1d0e24..9d5d588 100644 --- a/expr_grammar_action.g +++ b/expr_grammar_action.g @@ -31,11 +31,11 @@ axiom S rules S -> FUNDEFS SYM_EOF { Node(Tlistglobdef, $1) } -FUNDEFS -> FUNDEF FUNDEFS { Node(Tfundef, $1)::$2 } +FUNDEFS -> FUNDEF FUNDEFS { $1::$2 } FUNDEFS -> { [] } -FUNDEF -> IDENTIFIER SYM_LPARENTHESIS LPARAMS SYM_RPARENTHESIS INSTR { [Node(Tfunname, [$1]); Node(Tfunargs, $3); Node(Tfunbody, [$5])] } +FUNDEF -> IDENTIFIER SYM_LPARENTHESIS LPARAMS SYM_RPARENTHESIS INSTR { Node(Tfundef, [Node(Tfunname, [$1]); Node(Tfunargs, $3); Node(Tfunbody, [$5])]) } -LPARAMS -> IDENTIFIER REST_PARAMS { $1::$2 } +LPARAMS -> IDENTIFIER REST_PARAMS { Node(Targ, [$1])::$2 } LPARAMS -> { [] } REST_PARAMS -> SYM_COMMA LPARAMS { $2 } REST_PARAMS -> { [] } -- GitLab From f54acb726567ce0fb04c0d092a66da41cd726e66 Mon Sep 17 00:00:00 2001 From: Youssef <youssef.sellami@student-cs.fr> Date: Thu, 6 Mar 2025 00:35:06 +0100 Subject: [PATCH 04/11] e-run --- src/elang.ml | 2 +- src/elang_gen.ml | 56 ++++++++++++++++++++++++++++++--- src/elang_run.ml | 80 +++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 128 insertions(+), 10 deletions(-) diff --git a/src/elang.ml b/src/elang.ml index 72b8e18..3e6fcf0 100644 --- a/src/elang.ml +++ b/src/elang.ml @@ -6,7 +6,7 @@ type unop = Eneg type expr = Ebinop of binop * expr * expr - | Eunop of unop * expr + | Eunop of unop * expr (*unused*) | Eint of int | Evar of string diff --git a/src/elang_gen.ml b/src/elang_gen.ml index be904b1..86761b7 100644 --- a/src/elang_gen.ml +++ b/src/elang_gen.ml @@ -44,10 +44,18 @@ let binop_of_tag = let rec make_eexpr_of_ast (a: tree) : expr res = let res = match a with + (* TODO *) + | IntLeaf i -> OK (Eint i) + | StringLeaf s -> OK (Evar s) | Node(t, [e1; e2]) when tag_is_binop t -> - Error (Printf.sprintf "Unacceptable ast in make_eexpr_of_ast %s" - (string_of_ast a)) - | _ -> Error (Printf.sprintf "Unacceptable ast in make_eexpr_of_ast %s" + (let res1 = make_eexpr_of_ast e1 + in let res2 = make_eexpr_of_ast e2 + in match res1, res2 with + | Error msg, _ -> Error msg + | _, Error msg -> Error msg + | OK expr1, OK expr2 -> OK (Ebinop (binop_of_tag t, expr1, expr2))) + | _ -> + Error (Printf.sprintf "Unacceptable ast in make_eexpr_of_ast %s" (string_of_ast a)) in match res with @@ -59,6 +67,43 @@ let rec make_einstr_of_ast (a: tree) : instr res = let res = match a with (* TODO *) + | Node(Tassign, [StringLeaf s; e]) -> + (let res_of_e = make_eexpr_of_ast e + in match res_of_e with + | OK exp -> OK (Iassign (s, exp)) + | Error msg -> Error msg) + | Node(Tif, [e; i1; i2]) -> + (let res_of_e = make_eexpr_of_ast e + in let res_of_i1 = make_einstr_of_ast i1 + in let res_of_i2 = make_einstr_of_ast i2 + in match res_of_e, res_of_i1, res_of_i2 with + | Error msg, _, _ -> Error msg + | _, Error msg, _ -> Error msg + | _, _, Error msg -> Error msg + | OK exp, OK inst1, OK inst2 -> OK (Iif (exp, inst1, inst2))) + | Node(Twhile, [e; i]) -> + (let res_of_e = make_eexpr_of_ast e + in let res_of_i = make_einstr_of_ast i + in match res_of_e, res_of_i with + | Error msg, _ -> Error msg + | _, Error msg -> Error msg + | OK exp, OK inst-> OK (Iwhile (exp, inst))) + | Node(Tblock, i_list) -> + (let res_of_i_list = list_map_res make_einstr_of_ast i_list + in match res_of_i_list with + | Error msg -> Error msg + | OK instr_list -> OK (Iblock instr_list)) + | Node(Treturn, [e]) -> + (let res_of_e = make_eexpr_of_ast e + in match res_of_e with + | OK exp -> OK (Ireturn exp) + | Error msg -> Error msg) + | Node(Tprint, [e]) -> + (let res_of_e = make_eexpr_of_ast e + in match res_of_e with + | OK exp -> OK (Iprint exp) + | Error msg -> Error msg) + | NullLeaf -> OK (Iblock []) | _ -> Error (Printf.sprintf "Unacceptable ast in make_einstr_of_ast %s" (string_of_ast a)) in @@ -76,10 +121,11 @@ let make_ident (a: tree) : string res = let make_fundef_of_ast (a: tree) : (string * efun) res = match a with - | Node (Tfundef, [StringLeaf fname; Node (Tfunargs, fargs); fbody]) -> + | Node (Tfundef, [Node(Tfunname, [StringLeaf fname]); Node (Tfunargs, fargs); Node(Tfunbody, [fbody])]) -> list_map_res make_ident fargs >>= fun fargs -> (* TODO *) - Error "make_fundef_of_ast: Not implemented, yet." + make_einstr_of_ast fbody >>= fun fbody -> + OK (fname, {funargs = fargs; funbody = fbody}) | _ -> Error (Printf.sprintf "make_fundef_of_ast: Expected a Tfundef, got %s." (string_of_ast a)) diff --git a/src/elang_run.ml b/src/elang_run.ml index 494b2c6..13afe7c 100644 --- a/src/elang_run.ml +++ b/src/elang_run.ml @@ -9,17 +9,46 @@ let binop_bool_to_int f x y = if f x y then 1 else 0 et [y]. *) let eval_binop (b: binop) : int -> int -> int = match b with - | _ -> fun x y -> 0 + | Eadd -> fun x y -> x + y + | Emul -> fun x y -> x * y + | Emod -> fun x y -> x mod y + | Exor -> fun x y -> x lxor y + | Ediv -> fun x y -> x / y + | Esub -> fun x y -> x - y + | Eclt -> fun x y -> if x < y then 1 else 0 + | Ecle -> fun x y -> if x <= y then 1 else 0 + | Ecgt -> fun x y -> if x > y then 1 else 0 + | Ecge -> fun x y -> if x >= y then 1 else 0 + | Eceq -> fun x y -> if x = y then 1 else 0 + | Ecne -> fun x y -> if x != y then 1 else 0 + (* [eval_unop u x] évalue l'opération unaire [u] sur l'argument [x]. *) let eval_unop (u: unop) : int -> int = match u with - | _ -> fun x -> 0 + | Eneg -> fun x -> -x (* [eval_eexpr st e] évalue l'expression [e] dans l'état [st]. Renvoie une erreur si besoin. *) let rec eval_eexpr st (e : expr) : int res = - Error "eval_eexpr not implemented yet." + match e with + | Eint i -> OK i + | Evar s -> + (match Hashtbl.find_option st.env s with + | Some i -> OK i + | None -> Error "Variable is not defined") + | Ebinop (b, ex, ey) -> + (let res_x = eval_eexpr st ex + in let res_y = eval_eexpr st ey + in match res_x, res_y with + | Error msg, _ -> Error msg + | _, Error msg -> Error msg + | OK x, OK y -> OK (eval_binop b x y)) + | Eunop (u, ex) -> + (let res_x = eval_eexpr st ex + in match res_x with + | Error msg -> Error msg + | OK x -> OK (eval_unop u x )) (* [eval_einstr oc st ins] évalue l'instrution [ins] en partant de l'état [st]. @@ -35,7 +64,50 @@ let rec eval_eexpr st (e : expr) : int res = - [st'] est l'état mis à jour. *) let rec eval_einstr oc (st: int state) (ins: instr) : (int option * int state) res = - Error "eval_einstr not implemented yet." + match ins with + | Iassign (s, e) -> + (match eval_eexpr st e with + | Error msg -> Error msg + | OK v -> + let replace st s v = + let new_env = Hashtbl.copy st.env + in Hashtbl.replace new_env s v; + {st with env = new_env} + in OK (None, replace st s v)) + | Iif (e, i1, i2) -> + (match eval_eexpr st e with + | Error msg -> Error msg + | OK v -> if v=1 then eval_einstr oc st i1 else eval_einstr oc st i2) + | Iwhile (e, i) -> + (match eval_eexpr st e with + | Error msg -> Error msg + | OK v -> + if v=1 + then (let res_i = eval_einstr oc st i + in match res_i with + | Error msg -> Error msg + | OK (r_opt, next_st) -> match r_opt with + | None -> eval_einstr oc next_st (Iwhile (e, i)) + | Some r -> OK (r_opt, next_st)) + else OK(None, st)) + | Iblock i_list -> + (match i_list with + | [] -> OK (None, st) + | i::rest -> + match eval_einstr oc st i with + | Error msg -> Error msg + | OK (Some r, next_st) -> OK (Some r, next_st) + | OK (None, next_st) -> eval_einstr oc next_st (Iblock rest)) + | Ireturn e -> + (match eval_eexpr st e with + | Error msg -> Error msg + | OK v -> OK(Some v, st)) + | Iprint e -> + (match eval_eexpr st e with + | Error msg -> Error msg + | OK v -> + Format.fprintf oc "%d\n" v; + OK(None, st)) (* [eval_efun oc st f fname vargs] évalue la fonction [f] (dont le nom est [fname]) en partant de l'état [st], avec les arguments [vargs]. -- GitLab From 230a40cd3f6ed654010becd793a33e4259cae0ca Mon Sep 17 00:00:00 2001 From: Youssef <youssef.sellami@student-cs.fr> Date: Sun, 9 Mar 2025 16:01:56 +0100 Subject: [PATCH 05/11] cfg --- src/cfg_dead_assign.ml | 25 ++++++++++++++----------- src/cfg_liveness.ml | 41 ++++++++++++++++++++++++++++++++++------- src/cfg_nop_elim.ml | 24 ++++++++++++++++++------ src/elang.ml | 2 +- 4 files changed, 67 insertions(+), 25 deletions(-) diff --git a/src/cfg_dead_assign.ml b/src/cfg_dead_assign.ml index e35fc47..00c2b5b 100644 --- a/src/cfg_dead_assign.ml +++ b/src/cfg_dead_assign.ml @@ -14,22 +14,25 @@ open Options nouvelle fonction, et [c] est un booléen qui indique si du progrès a été fait. *) let dead_assign_elimination_fun ({ cfgfunbody; _ } as f: cfg_fun) = - let changed = ref false in - let cfgfunbody = - Hashtbl.map (fun (n: int) (m: cfg_node) -> - match m with - (* TODO *) - | _ -> m - ) cfgfunbody in - ({ f with cfgfunbody }, !changed ) + let changed = ref false + in let lives_in = live_cfg_fun f + in let lives_out n = live_after_node cfgfunbody n lives_in + in let cfgfunbody = + Hashtbl.map (fun (n: int) (m: cfg_node) -> + match m with + (* TODO *) + | Cassign (s, e, i) -> if (Set.mem s (lives_out n)) then m else (changed := true; Cnop i) + | _ -> m + ) cfgfunbody + in ({ f with cfgfunbody }, !changed ) (* Applique l'élimination de code mort autant de fois que nécessaire. Testez notamment sur le fichier de test [basic/useless_assigns.e]. *) let rec iter_dead_assign_elimination_fun f = let f, c = dead_assign_elimination_fun f in - (* TODO *) - f - + (* TODO *) + if c then iter_dead_assign_elimination_fun f else f + let dead_assign_elimination_gdef = function Gfun f -> Gfun (iter_dead_assign_elimination_fun f) diff --git a/src/cfg_liveness.ml b/src/cfg_liveness.ml index 194a291..609e3ad 100644 --- a/src/cfg_liveness.ml +++ b/src/cfg_liveness.ml @@ -1,12 +1,17 @@ open Batteries open Cfg +open Utils (* Analyse de vivacité *) (* [vars_in_expr e] renvoie l'ensemble des variables qui apparaissent dans [e]. *) let rec vars_in_expr (e: expr) = (* TODO *) - Set.empty + match e with + | Eint i -> Set.empty + | 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 (* [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, @@ -14,14 +19,26 @@ let rec vars_in_expr (e: expr) = les valeurs sont les ensembles de variables vivantes avant chaque nœud. *) let live_after_node cfg n (lives: (int, string Set.t) Hashtbl.t) : string Set.t = (* TODO *) - Set.empty - + let in_succs_opt = Set.map (fun s -> Hashtbl.find_opt lives s) (succs cfg n) + in let in_succs = Set.filter_map (fun s -> s) in_succs_opt + in set_concat (Set.to_list in_succs) (* [live_cfg_node node live_after] renvoie l'ensemble des variables vivantes avant un nœud [node], étant donné l'ensemble [live_after] des variables vivantes après ce nœud. *) let live_cfg_node (node: cfg_node) (live_after: string Set.t) = (* TODO *) - live_after + let use node = + match node with + | Cassign (s, e, i) -> vars_in_expr e + | Creturn e -> vars_in_expr e + | Cprint (e, i) -> vars_in_expr e + | Ccmp (e, i1, i2) -> vars_in_expr e + | Cnop (i) -> Set.empty + in let def node = + match node with + | Cassign (s, e, i) -> Set.singleton s + | _ -> Set.empty + in Set.union (use node) (Set.diff live_after (def node)) (* [live_cfg_nodes cfg lives] effectue une itération du calcul de point fixe. @@ -31,12 +48,22 @@ let live_cfg_node (node: cfg_node) (live_after: string Set.t) = nœud a changé). *) let live_cfg_nodes cfg (lives : (int, string Set.t) Hashtbl.t) = (* TODO *) - false + let changed = ref false in + Hashtbl.iter (fun n node -> + let new_alive_vars = live_cfg_node node (live_after_node cfg n lives) + in match Hashtbl.find_opt lives n with + | None -> changed := true; Hashtbl.replace lives n new_alive_vars + | Some alive_vars -> if not (Set.equal alive_vars new_alive_vars) then changed := true; Hashtbl.replace lives n new_alive_vars) cfg; !changed + (* [live_cfg_fun f] calcule l'ensemble des variables vivantes avant chaque nœud du CFG en itérant [live_cfg_nodes] jusqu'à ce qu'un point fixe soit atteint. *) let live_cfg_fun (f: cfg_fun) : (int, string Set.t) Hashtbl.t = - let lives = Hashtbl.create 17 in + let lives = Hashtbl.create 17 in (* TODO *) - lives + let cfg = f.cfgfunbody + in while live_cfg_nodes cfg lives do + () + done; + lives diff --git a/src/cfg_nop_elim.ml b/src/cfg_nop_elim.ml index da45a5a..5d8f3c6 100644 --- a/src/cfg_nop_elim.ml +++ b/src/cfg_nop_elim.ml @@ -15,7 +15,9 @@ open Options *) let nop_transitions (cfgfunbody: (int, cfg_node) Hashtbl.t) : (int * int) list = (* TODO *) - [] + Hashtbl.fold (fun n node acc -> match node with + | Cnop i -> (n, i)::acc + | _ -> acc) cfgfunbody [] (* [follow n l visited] donne le premier successeur à partir de [n] qui ne soit @@ -26,9 +28,13 @@ let nop_transitions (cfgfunbody: (int, cfg_node) Hashtbl.t) : (int * int) list = L'ensemble [visited] est utilisé pour éviter les boucles. *) -let rec follow (n: int) (l: (int * int) list) (visited: int Set.t) : int = +let rec follow (nop: int) (l: (int * int) list) (visited: int Set.t) : int = (* TODO *) - n + if Set.mem nop visited + then nop + else match List.assoc_opt nop l with + | None -> nop + | Some m -> follow m l (Set.add nop visited) (* [nop_transitions_closed] contient la liste [(n,s)] telle que l'instruction au nœud [n] est le début d'une chaîne de NOPs qui termine au nœud [s]. Les @@ -48,13 +54,19 @@ let nop_transitions_closed cfgfunbody = liste [nop_succs] (telle que renvoyée par [nop_transitions_closed]). *) let replace_succ nop_succs s = (* TODO *) - s + match List.assoc_opt s nop_succs with + | None -> s + | Some x -> x (* [replace_succs nop_succs n] remplace le nœud [n] par un nœud équivalent où on a remplacé les successeurs, en utilisant la liste [nop_succs]. *) let replace_succs nop_succs (n: cfg_node) = (* TODO *) - n + match n with + | Cassign (s, e, i) -> Cassign (s, e, replace_succ nop_succs i) + | 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) + | _ -> n (* [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) = @@ -69,7 +81,7 @@ let nop_elim_fun ({ cfgfunargs; cfgfunbody; cfgentry } as f: cfg_fun) = *) let cfgfunbody = Hashtbl.filter_map (fun n node -> (* TODO *) - Some node + if Set.is_empty (preds cfgfunbody n) && n!=cfgentry then None else Some (replace_succs nop_transf node) ) cfgfunbody in (* La fonction renvoyée est composée du nouveau [cfgfunbody] que l'on vient de calculer, et le point d'entrée est transformé en conséquence. *) diff --git a/src/elang.ml b/src/elang.ml index 3e6fcf0..7dc3280 100644 --- a/src/elang.ml +++ b/src/elang.ml @@ -6,7 +6,7 @@ type unop = Eneg type expr = Ebinop of binop * expr * expr - | Eunop of unop * expr (*unused*) + | Eunop of unop * expr (*unused in grammar*) | Eint of int | Evar of string -- GitLab From d685e24da2e5a4103361a174918459a196463b87 Mon Sep 17 00:00:00 2001 From: Youssef <youssef.sellami@student-cs.fr> Date: Sun, 9 Mar 2025 16:40:53 +0100 Subject: [PATCH 06/11] nop elimination --- src/cfg_nop_elim.ml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/cfg_nop_elim.ml b/src/cfg_nop_elim.ml index 5d8f3c6..142d096 100644 --- a/src/cfg_nop_elim.ml +++ b/src/cfg_nop_elim.ml @@ -66,6 +66,7 @@ let replace_succs nop_succs (n: cfg_node) = | Cassign (s, e, i) -> Cassign (s, e, replace_succ nop_succs i) | 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 (* [nop_elim_fun f] applique la fonction [replace_succs] à chaque nœud du CFG. *) @@ -79,13 +80,18 @@ let nop_elim_fun ({ cfgfunargs; cfgfunbody; cfgentry } as f: cfg_fun) = (inaccessibles), et appliquer la fonction [replace_succs] aux nœuds qui resteront. *) - let cfgfunbody = Hashtbl.filter_map (fun n node -> (* TODO *) - if Set.is_empty (preds cfgfunbody n) && n!=cfgentry then None else Some (replace_succs nop_transf node) - ) cfgfunbody in + let cfgfunbody_corr_links = Hashtbl.map (fun n node -> + replace_succs nop_transf node + ) cfgfunbody + in let cfgfunbody_wout_inacc = Hashtbl.filter_map (fun n node -> + match node with + | Cnop i -> None + | _ -> if Set.is_empty (preds cfgfunbody_corr_links n) && n!=cfgentry then None else Some node + ) cfgfunbody_corr_links in (* La fonction renvoyée est composée du nouveau [cfgfunbody] que l'on vient de calculer, et le point d'entrée est transformé en conséquence. *) - {f with cfgfunbody; cfgentry = replace_succ nop_transf cfgentry } + {f with cfgfunbody=cfgfunbody_wout_inacc; cfgentry = replace_succ nop_transf cfgentry } let nop_elim_gdef gd = match gd with -- GitLab From 22c3ed78a58447416d09ce734758990e26171313 Mon Sep 17 00:00:00 2001 From: Youssef <youssef.sellami@student-cs.fr> Date: Wed, 12 Mar 2025 11:35:38 +0100 Subject: [PATCH 07/11] rtlgen and regalloc --- src/linear_gen.ml | 34 ++++++++++++++++++++------- src/regalloc.ml | 60 +++++++++++++++++++++++++++++++---------------- src/rtl_gen.ml | 31 +++++++++++++++++++++--- 3 files changed, 94 insertions(+), 31 deletions(-) diff --git a/src/linear_gen.ml b/src/linear_gen.ml index c12bd04..cd83deb 100644 --- a/src/linear_gen.ml +++ b/src/linear_gen.ml @@ -19,23 +19,41 @@ let rec succs_of_rtl_instrs il : int list = (* effectue un tri topologique des blocs. *) let sort_blocks (nodes: (int, rtl_instr list) Hashtbl.t) entry = - let rec add_block order n = - (* TODO *) - List.of_enum (Hashtbl.keys nodes) + let rec add_block order visited n = + (* TODO *) + List.of_enum (Hashtbl.keys nodes) + (*if Set.mem n visited + then order + else let succs = succs_of_rtl_instrs (Hashtbl.find nodes n) + in List.concat( (order@[n]) :: List.map (add_block [] (Set.add n visited)) succs )*) in - add_block [] entry + add_block [] Set.empty entry (* Supprime les jumps inutiles (Jmp à un label défini juste en dessous). *) let rec remove_useless_jumps (l: rtl_instr list) = - (* TODO *) - l + (* TODO *) + l + (*match l with + | [] -> [] + | Rjmp l1::Rlabel l2::rest -> + if l1=l2 + then Rlabel l2::remove_useless_jumps rest + else Rjmp l1::Rlabel l2::remove_useless_jumps rest + | i::rest -> i::remove_useless_jumps rest*) (* Remove labels that are never jumped to. *) let remove_useless_labels (l: rtl_instr list) = - (* TODO *) - l + (* TODO *) + l + (*List.filter (function + Rlabel i -> List.exists ( + function + Rbranch(_, _, _, j) -> j = i + | Rjmp j -> j = i + | _ -> false) l + | _ -> true) l*) let linear_of_rtl_fun ({ rtlfunargs; rtlfunbody; rtlfunentry; rtlfuninfo }: rtl_fun) = diff --git a/src/regalloc.ml b/src/regalloc.ml index c68b9fd..a843912 100644 --- a/src/regalloc.ml +++ b/src/regalloc.ml @@ -55,7 +55,7 @@ let regalloc_on_stack_fun (f: linear_fun) : ((reg, loc) Hashtbl.t * int)= avec le registre-clé. Cela correspond à la relation d'adjacence dans le graphe d'interférence. *) -(* La fonction [add_to_interf rig x y] ajoute [y] à la liste des registres qui +(* La fonction [add_to_interf rig x y] ajoute [y] à la liste des regiNosstres qui interfèrent avec [x] dans le graphe [rig]. On pourra utiliser la fonction [Hashtbl.modify_def] qui permet de modifier la @@ -79,7 +79,8 @@ let regalloc_on_stack_fun (f: linear_fun) : ((reg, loc) Hashtbl.t * int)= let add_interf (rig : (reg, reg Set.t) Hashtbl.t) (x: reg) (y: reg) : unit = (* TODO *) - () + Hashtbl.modify_def Set.empty x (Set.add y) rig; + Hashtbl.modify_def Set.empty y (Set.add x) rig (* [make_interf_live rig live] ajoute des arcs dans le graphe d'interférence @@ -89,7 +90,7 @@ let make_interf_live (rig: (reg, reg Set.t) Hashtbl.t) (live : (int, reg Set.t) Hashtbl.t) : unit = (* TODO *) - () + Hashtbl.iter (fun i regs -> Set.iter (fun x -> Set.iter (fun y -> if x < y then add_interf rig x y) regs) regs) live (* [build_interference_graph live_out] construit, en utilisant les fonctions que vous avez écrites, le graphe d'interférence en fonction de la vivacité des @@ -120,8 +121,11 @@ let build_interference_graph (live_out : (int, reg Set.t) Hashtbl.t) code : (reg (* [remove_from_rig rig v] supprime le sommet [v] du graphe d'interférences [rig]. *) let remove_from_rig (rig : (reg, reg Set.t) Hashtbl.t) (v: reg) : unit = - (* TODO *) - () + (* TODO *) + while Hashtbl.mem rig v do + Hashtbl.remove rig v + done; + Hashtbl.iter (fun x regs -> Hashtbl.modify x (Set.remove v) rig) rig (* Type représentant les différentes décisions qui peuvent être prises par @@ -159,8 +163,8 @@ type regalloc_decision = possédant strictement moins de [n] voisins. Retourne [None] si aucun sommet ne satisfait cette condition. *) let pick_node_with_fewer_than_n_neighbors (rig : (reg, reg Set.t) Hashtbl.t) (n: int) : reg option = - (* TODO *) - None + (* TODO *) + Hashtbl.fold (fun x regs acc -> if (Set.cardinal regs) < n then Some x else acc) rig None (* Lorsque la fonction précédente échoue (i.e. aucun sommet n'a moins de [n] voisins), on choisit un pseudo-registre à évincer. @@ -171,24 +175,30 @@ let pick_node_with_fewer_than_n_neighbors (rig : (reg, reg Set.t) Hashtbl.t) (n: [pick_spilling_candidate rig] retourne donc le pseudo-registre [r] qui a le plus de voisins dans [rig], ou [None] si [rig] est vide. *) let pick_spilling_candidate (rig : (reg, reg Set.t) Hashtbl.t) : reg option = - (* TODO *) - None + (* TODO *) + let candidate_number_of_neighbours = Hashtbl.fold (fun x regs acc -> + match acc with + | None -> Some (x, Set.cardinal regs) + | Some (y, k) -> if Set.cardinal regs > k then Some (x, Set.cardinal regs) else acc + ) rig None + in match candidate_number_of_neighbours with + | None -> None + | Some (y, k) -> Some y (* [make_stack rig stack ncolors] construit la pile, selon l'algorithme vu en cours (slide 26 du cours "Allocation de registres" présent sur Edunao.) *) let rec make_stack (rig : (reg, reg Set.t) Hashtbl.t) (stack : regalloc_decision list) (ncolors: int) : regalloc_decision list = - (* TODO *) - stack + (* TODO *) + match pick_node_with_fewer_than_n_neighbors rig ncolors with + | Some r -> remove_from_rig rig r; make_stack rig (NoSpill r :: stack) ncolors + | None -> + match pick_spilling_candidate rig with + | Some r -> remove_from_rig rig r; make_stack rig (Spill r :: stack) ncolors + | None -> stack (* Maintenant que nous avons une pile de [regalloc_decision], il est temps de - colorer notre graphe, i.e. associer une couleur (un numéro de registre - physique) à chaque pseudo-registre. Nous allons parcourir la pile et pour - chaque décision : - - -  [Spill r] : associer un emplacement sur la pile au pseudo-registre [r]. On - choisira l'emplacement [next_stack_slot]. - + colorer notre graphe, i.e. associer une couleur (un numéro de restack@[NoSpill r] - [NoSpill r] : associer une couleur (un registre) physique au pseudo-registre [r]. On choisira une couleur qui n'est pas déjà associée à un voisin de [r] dans [rig]. @@ -218,8 +228,18 @@ let allocate (allocation: (reg, loc) Hashtbl.t) (rig: (reg, reg Set.t) Hashtbl.t (all_colors: int Set.t) (next_stack_slot: int) (decision: regalloc_decision) : int = - (* TODO *) - next_stack_slot + (* TODO *) + match decision with + | Spill r -> Hashtbl.add allocation r (Stk next_stack_slot); next_stack_slot-1 + | NoSpill r -> let neighbours = Hashtbl.find rig r + in let neighbour_colors = Set.filter_map ( + fun neighbour -> match Hashtbl.find_opt allocation neighbour with + | Some (Reg color) -> Some color + | _ -> None + ) neighbours + in let available_colors = Set.diff all_colors neighbour_colors + in let chosen_color = Set.choose available_colors + in Hashtbl.add allocation r (Reg chosen_color); next_stack_slot (* [regalloc_fun f live_out all_colors] effectue l'allocation de registres pour la fonction [f]. diff --git a/src/rtl_gen.ml b/src/rtl_gen.ml index ce1eb8f..7955b3f 100644 --- a/src/rtl_gen.ml +++ b/src/rtl_gen.ml @@ -43,7 +43,16 @@ let find_var (next_reg, var2reg) v = - [var2reg] est la nouvelle association nom de variable/registre. *) let rec rtl_instrs_of_cfg_expr (next_reg, var2reg) (e: expr) = - (next_reg, [], next_reg, var2reg) + match e with + | Evar v -> let r, next_reg', var2reg' = find_var (next_reg, var2reg) v in (r, [], next_reg', var2reg') + | Eint i -> (next_reg, [Rconst(next_reg, i)], next_reg+1, var2reg) + | Ebinop (b, e1, e2) -> + let r1, l1, next_reg1, var2reg1 = rtl_instrs_of_cfg_expr (next_reg, var2reg) e1 + in let r2, l2, next_reg2, var2reg2 = rtl_instrs_of_cfg_expr (next_reg1, var2reg1) e2 + in (next_reg2, l1@l2@[Rbinop(b, next_reg2, r1, r2)], next_reg2+1, var2reg2) + | Eunop (u, e) -> + let r, l, next_reg', var2reg' = rtl_instrs_of_cfg_expr (next_reg, var2reg) e + in (next_reg', l@[Runop(u, next_reg', r)], next_reg'+1, var2reg') let is_cmp_op = function Eclt -> Some Rclt @@ -64,8 +73,24 @@ let rtl_cmp_of_cfg_expr (e: expr) = let rtl_instrs_of_cfg_node ((next_reg:int), (var2reg: (string*int) list)) (c: cfg_node) = - (* TODO *) - ([], next_reg, var2reg) + (* TODO *) + match c with + | Cassign (s, e, i) -> + let r_e, l, next_reg1, var2reg1 = rtl_instrs_of_cfg_expr (next_reg, var2reg) e + in let r_s, next_reg2, var2reg2 = find_var (next_reg1, var2reg1) s + in (l@[Rmov(r_s, r_e); Rjmp i], next_reg2, var2reg2) + | Creturn e -> + let r_e, l, next_reg', var2reg' = rtl_instrs_of_cfg_expr (next_reg, var2reg) e + in (l@[Rret r_e], next_reg', var2reg') + | Cprint (e, i) -> + let r_e, l, next_reg', var2reg' = rtl_instrs_of_cfg_expr (next_reg, var2reg) e + in (l@[Rprint r_e; Rjmp i], next_reg', var2reg') + | Ccmp (e, i1, i2) -> + let cmp, e1, e2 = rtl_cmp_of_cfg_expr e + in let r1, l1, next_reg1, var2reg1 = rtl_instrs_of_cfg_expr (next_reg, var2reg) e1 + in let r2, l2, next_reg2, var2reg2 = rtl_instrs_of_cfg_expr (next_reg1, var2reg1) e2 + in (l1@l2@[Rbranch(cmp, r1, r2, i1)]@[Rjmp i2], next_reg2, var2reg2) + | Cnop i -> ([Rjmp i], next_reg, var2reg) let rtl_instrs_of_cfg_fun cfgfunname ({ cfgfunargs; cfgfunbody; cfgentry }: cfg_fun) = let (rargs, next_reg, var2reg) = -- GitLab From ebad7c57fc703eb8df6de8b51ec753cc1a10877b Mon Sep 17 00:00:00 2001 From: Youssef <youssef.sellami@student-cs.fr> Date: Thu, 13 Mar 2025 19:03:02 +0100 Subject: [PATCH 08/11] Functions : parser and E --- expr_grammar_action.g | 32 +++++- src/ast.ml | 7 +- src/elang.ml | 4 +- src/elang_gen.ml | 15 +++ src/elang_print.ml | 5 + src/elang_run.ml | 110 ++++++++++++++------- src/regalloc.ml | 6 +- tests/Makefile | 2 +- tests/funcall/argswap.e.expect_lexer | 8 +- tests/funcall/print_and_fun.e.expect_lexer | 4 +- 10 files changed, 142 insertions(+), 51 deletions(-) diff --git a/expr_grammar_action.g b/expr_grammar_action.g index 9d5d588..b871578 100644 --- a/expr_grammar_action.g +++ b/expr_grammar_action.g @@ -10,6 +10,9 @@ non-terminals ADD_EXPRS ADD_EXPR non-terminals MUL_EXPRS MUL_EXPR non-terminals CMP_EXPRS CMP_EXPR non-terminals EQ_EXPRS EQ_EXPR + +non-terminals AFTER_IDENTIFIER LARGS REST_ARGS + axiom S { @@ -20,6 +23,10 @@ axiom S open Batteries open Utils + type after_id = + | Assign of tree + | Funcall of tree list + | Nothing (* TODO *) let rec resolve_associativity (term : tree) (other : (tag * tree) list) = @@ -40,6 +47,11 @@ LPARAMS -> { [] } REST_PARAMS -> SYM_COMMA LPARAMS { $2 } REST_PARAMS -> { [] } +LARGS -> EXPR REST_ARGS { $1::$2 } +LARGS -> { [] } +REST_ARGS -> SYM_COMMA LARGS { $2 } +REST_ARGS -> { [] } + LINSTRS -> INSTR INSTRS { Node(Tblock, $1::$2) } LINSTRS -> { NullLeaf } INSTRS -> INSTR INSTRS { $1::$2 } @@ -49,9 +61,18 @@ INSTR -> SYM_IF SYM_LPARENTHESIS EXPR SYM_RPARENTHESIS SYM_LBRACE LINSTRS SYM_RB INSTR -> SYM_WHILE SYM_LPARENTHESIS EXPR SYM_RPARENTHESIS INSTR { Node(Twhile, [$3; $5]) } INSTR -> SYM_RETURN EXPR SYM_SEMICOLON { Node(Treturn, [$2]) } INSTR -> SYM_PRINT SYM_LPARENTHESIS EXPR SYM_RPARENTHESIS SYM_SEMICOLON { Node(Tprint, [$3]) } -INSTR -> IDENTIFIER SYM_ASSIGN EXPR SYM_SEMICOLON { Node(Tassign, [$1; $3]) } +INSTR -> IDENTIFIER AFTER_IDENTIFIER SYM_SEMICOLON { + match $2 with + | Assign exp -> Node(Tassign, [$1; exp]) + | Funcall args -> Node(Tcall, [$1; Node(Targs, args)]) + | _ -> $1 +} INSTR -> SYM_LBRACE LINSTRS SYM_RBRACE { $2 } +AFTER_IDENTIFIER -> SYM_ASSIGN EXPR { Assign $2 } +AFTER_IDENTIFIER -> SYM_LPARENTHESIS LARGS SYM_RPARENTHESIS { Funcall $2 } +AFTER_IDENTIFIER -> { Nothing } + ELSE -> SYM_ELSE SYM_LBRACE LINSTRS SYM_RBRACE { $3 } ELSE -> { NullLeaf } @@ -59,6 +80,8 @@ EXPR -> EQ_EXPR EQ_EXPRS { resolve_associativity $1 $2 } EQ_EXPR -> CMP_EXPR CMP_EXPRS { resolve_associativity $1 $2 } CMP_EXPR -> ADD_EXPR ADD_EXPRS { resolve_associativity $1 $2 } ADD_EXPR -> MUL_EXPR MUL_EXPRS { resolve_associativity $1 $2 } +ADD_EXPR -> SYM_MINUS MUL_EXPR MUL_EXPRS { resolve_associativity (Node(Tneg, [$2])) $3 } +ADD_EXPR -> SYM_PLUS MUL_EXPR MUL_EXPRS { resolve_associativity $2 $3 } MUL_EXPR -> FACTOR { $1 } EQ_EXPRS -> SYM_EQUALITY EQ_EXPR EQ_EXPRS { (Tceq, $2)::$3 } @@ -81,7 +104,12 @@ MUL_EXPRS -> SYM_MOD MUL_EXPR MUL_EXPRS { (Tmod, $2)::$3 } MUL_EXPRS -> { [] } FACTOR -> INTEGER { $1 } -FACTOR -> IDENTIFIER { $1 } +FACTOR -> IDENTIFIER AFTER_IDENTIFIER { + match $2 with + | Funcall args -> Node(Tcall, [$1; Node(Targs, args)]) + | Nothing -> $1 + | _ -> $1 +} FACTOR -> SYM_LPARENTHESIS EXPR SYM_RPARENTHESIS { $2 } IDENTIFIER -> SYM_IDENTIFIER {StringLeaf $1} diff --git a/src/ast.ml b/src/ast.ml index bfd93a8..cfb67d6 100644 --- a/src/ast.ml +++ b/src/ast.ml @@ -28,9 +28,9 @@ type tag = Tassign | Tif | Twhile | Tblock | Treturn | Tprint | Tclt | Tcgt | Tcle | Tcge | Tceq | Tne | Tneg | Tlistglobdef - | Tfundef | Tfunname | Tfunargs | Tfunbody + | Tfundef | Tfunname | Tfunargs | Tfunbody | Tcall | Tassignvar - | Targ + | Targ | Targs type tree = | Node of tag * tree list | StringLeaf of string @@ -73,7 +73,8 @@ let string_of_tag = function | Tfunbody -> "Tfunbody" | Tassignvar -> "Tassignvar" | Targ -> "Targ" - + | Tcall -> "Tcall" + | Targs -> "Targs" (* Écrit un fichier .dot qui correspond à un AST *) let rec draw_ast a next = diff --git a/src/elang.ml b/src/elang.ml index 7dc3280..38b4f66 100644 --- a/src/elang.ml +++ b/src/elang.ml @@ -6,9 +6,10 @@ type unop = Eneg type expr = Ebinop of binop * expr * expr - | Eunop of unop * expr (*unused in grammar*) + | Eunop of unop * expr | Eint of int | Evar of string + | Ecall of string * expr list type instr = | Iassign of string * expr @@ -17,6 +18,7 @@ type instr = | Iblock of instr list | Ireturn of expr | Iprint of expr + | Icall of string * expr list type efun = { funargs: ( string ) list; diff --git a/src/elang_gen.ml b/src/elang_gen.ml index 86761b7..1d4db2d 100644 --- a/src/elang_gen.ml +++ b/src/elang_gen.ml @@ -54,6 +54,16 @@ let rec make_eexpr_of_ast (a: tree) : expr res = | Error msg, _ -> Error msg | _, Error msg -> Error msg | OK expr1, OK expr2 -> OK (Ebinop (binop_of_tag t, expr1, expr2))) + | Node(Tneg, [e]) -> + (let res = make_eexpr_of_ast e + in match res with + | Error msg -> Error msg + | OK expr -> OK (Eunop (Eneg, expr))) + | Node(Tcall, [StringLeaf f; Node(Targs, args)]) -> + (let res = list_map_res make_eexpr_of_ast args + in match res with + | Error msg -> Error msg + | OK exprs -> OK (Ecall (f, exprs))) | _ -> Error (Printf.sprintf "Unacceptable ast in make_eexpr_of_ast %s" (string_of_ast a)) @@ -103,6 +113,11 @@ let rec make_einstr_of_ast (a: tree) : instr res = in match res_of_e with | OK exp -> OK (Iprint exp) | Error msg -> Error msg) + | Node(Tcall, [StringLeaf f; Node(Targs, args)]) -> + (let res = list_map_res make_eexpr_of_ast args + in match res with + | Error msg -> Error msg + | OK exprs -> OK (Icall (f, exprs))) | NullLeaf -> OK (Iblock []) | _ -> Error (Printf.sprintf "Unacceptable ast in make_einstr_of_ast %s" (string_of_ast a)) diff --git a/src/elang_print.ml b/src/elang_print.ml index 2da36d9..8f7f8cf 100644 --- a/src/elang_print.ml +++ b/src/elang_print.ml @@ -26,6 +26,7 @@ let rec dump_eexpr = function | Eunop(u, e) -> Printf.sprintf "(%s %s)" (dump_unop u) (dump_eexpr e) | Eint i -> Printf.sprintf "%d" i | Evar s -> Printf.sprintf "%s" s + | Ecall (f, args) -> Printf.sprintf "%s(%s)" f (String.concat ", " (List.map dump_eexpr args)) let indent_size = 2 let spaces n = @@ -58,6 +59,10 @@ let rec dump_einstr_rec indent oc i = | Iprint(e) -> print_spaces oc indent; Format.fprintf oc "print %s;\n" (dump_eexpr e) + | Icall(f, args) -> + print_spaces oc indent; + Format.fprintf oc "%s(%s);\n" f (String.concat ", " (List.map dump_eexpr args)) + let dump_einstr oc i = dump_einstr_rec 0 oc i diff --git a/src/elang_run.ml b/src/elang_run.ml index 13afe7c..e4509f6 100644 --- a/src/elang_run.ml +++ b/src/elang_run.ml @@ -30,26 +30,49 @@ let eval_unop (u: unop) : int -> int = (* [eval_eexpr st e] évalue l'expression [e] dans l'état [st]. Renvoie une erreur si besoin. *) -let rec eval_eexpr st (e : expr) : int res = +let rec eval_eexpr oc st (ep: eprog) (e : expr) : (int * int state) res = match e with - | Eint i -> OK i + | Eint i -> OK (i, st) | Evar s -> (match Hashtbl.find_option st.env s with - | Some i -> OK i + | Some i -> OK (i, st) | None -> Error "Variable is not defined") | Ebinop (b, ex, ey) -> - (let res_x = eval_eexpr st ex - in let res_y = eval_eexpr st ey - in match res_x, res_y with - | Error msg, _ -> Error msg - | _, Error msg -> Error msg - | OK x, OK y -> OK (eval_binop b x y)) + (let res_x = eval_eexpr oc st ep ex + in match res_x with + | Error msg -> Error msg + | OK (x, st') -> + let res_y = eval_eexpr oc st' ep ey + in match res_y with + | Error msg -> Error msg + | OK (y, st'') -> OK (eval_binop b x y, st'')) | Eunop (u, ex) -> - (let res_x = eval_eexpr st ex + (let res_x = eval_eexpr oc st ep ex in match res_x with | Error msg -> Error msg - | OK x -> OK (eval_unop u x )) - + | OK (x, st') -> OK (eval_unop u x, st')) + | Ecall (f, args) -> + let (res : (int list * int state) res) = List.fold_left ( + fun (acc : (int list * int state) res) (arg : expr) -> + match acc with + | Error msg -> Error msg + | OK (l, st') -> + match eval_eexpr oc st' ep arg with + | Error msg -> Error msg + | OK (i, st'') -> OK ((l@[i]), st'') + ) (OK([], st)) args + in match res with + | Error msg -> Error msg + | OK (int_args, st') -> + match find_function ep f with + | Error msg -> Error msg + | OK found_f -> + match eval_efun oc st' ep found_f f int_args with + | Error msg -> Error msg + | OK (None, st'') -> Error (Format.sprintf "E: Function %s doesn't have a return value.\n" f) + | OK (Some ret, st'') -> OK (ret, st'') + + (* [eval_einstr oc st ins] évalue l'instrution [ins] en partant de l'état [st]. Le paramètre [oc] est un "output channel", dans lequel la fonction "print" @@ -62,59 +85,78 @@ let rec eval_eexpr st (e : expr) : int res = lieu et que l'exécution doit continuer. - [st'] est l'état mis à jour. *) -let rec eval_einstr oc (st: int state) (ins: instr) : +and eval_einstr oc (st: int state) (ep: eprog) (ins: instr) : (int option * int state) res = match ins with | Iassign (s, e) -> - (match eval_eexpr st e with + (match eval_eexpr oc st ep e with | Error msg -> Error msg - | OK v -> + | OK (v, st') -> let replace st s v = let new_env = Hashtbl.copy st.env in Hashtbl.replace new_env s v; {st with env = new_env} - in OK (None, replace st s v)) + in OK (None, replace st' s v)) | Iif (e, i1, i2) -> - (match eval_eexpr st e with + (match eval_eexpr oc st ep e with | Error msg -> Error msg - | OK v -> if v=1 then eval_einstr oc st i1 else eval_einstr oc st i2) + | OK (v, st') -> if v = 1 then eval_einstr oc st' ep i1 else eval_einstr oc st' ep i2) | Iwhile (e, i) -> - (match eval_eexpr st e with + (match eval_eexpr oc st ep e with | Error msg -> Error msg - | OK v -> - if v=1 - then (let res_i = eval_einstr oc st i + | OK (v, st') -> + if v = 1 + then (let res_i = eval_einstr oc st' ep i in match res_i with | Error msg -> Error msg | OK (r_opt, next_st) -> match r_opt with - | None -> eval_einstr oc next_st (Iwhile (e, i)) + | None -> eval_einstr oc next_st ep (Iwhile (e, i)) | Some r -> OK (r_opt, next_st)) - else OK(None, st)) + else OK(None, st')) | Iblock i_list -> (match i_list with | [] -> OK (None, st) | i::rest -> - match eval_einstr oc st i with + match eval_einstr oc st ep i with | Error msg -> Error msg | OK (Some r, next_st) -> OK (Some r, next_st) - | OK (None, next_st) -> eval_einstr oc next_st (Iblock rest)) + | OK (None, next_st) -> eval_einstr oc next_st ep (Iblock rest)) | Ireturn e -> - (match eval_eexpr st e with + (match eval_eexpr oc st ep e with | Error msg -> Error msg - | OK v -> OK(Some v, st)) + | OK (v, st') -> OK(Some v, st')) | Iprint e -> - (match eval_eexpr st e with + (match eval_eexpr oc st ep e with | Error msg -> Error msg - | OK v -> + | OK (v, st') -> Format.fprintf oc "%d\n" v; - OK(None, st)) + OK(None, st')) + | Icall (f, args) -> + let (res : (int list * int state) res) = List.fold_left ( + fun (acc : (int list * int state) res) (arg : expr) -> + match acc with + | Error msg -> Error msg + | OK (l, st') -> + match eval_eexpr oc st' ep arg with + | Error msg -> Error msg + | OK (i, st'') -> OK ((l@[i]), st'') + ) (OK([], st)) args + in match res with + | Error msg -> Error msg + | OK (int_args, st') -> + match find_function ep f with + | Error msg -> Error msg + | OK found_f -> + match eval_efun oc st' ep found_f f int_args with + | Error msg -> Error msg + | OK (_, st'') -> OK (None, st'') (* [eval_efun oc st f fname vargs] évalue la fonction [f] (dont le nom est [fname]) en partant de l'état [st], avec les arguments [vargs]. Cette fonction renvoie un couple (ret, st') avec la même signification que pour [eval_einstr]. *) -let eval_efun oc (st: int state) ({ funargs; funbody}: efun) +and eval_efun oc (st: int state) ep ({ funargs; funbody}: efun) (fname: string) (vargs: int list) : (int option * int state) res = (* L'environnement d'une fonction (mapping des variables locales vers leurs @@ -126,7 +168,7 @@ let eval_efun oc (st: int state) ({ funargs; funbody}: efun) let env = Hashtbl.create 17 in match List.iter2 (fun a v -> Hashtbl.replace env a v) funargs vargs with | () -> - eval_einstr oc { st with env } funbody >>= fun (v, st') -> + eval_einstr oc { st with env } ep funbody >>= fun (v, st') -> OK (v, { st' with env = env_save }) | exception Invalid_argument _ -> Error (Format.sprintf @@ -157,5 +199,5 @@ let eval_eprog oc (ep: eprog) (memsize: int) (params: int list) (* ne garde que le nombre nécessaire de paramètres pour la fonction "main". *) let n = List.length f.funargs in let params = take n params in - eval_efun oc st f "main" params >>= fun (v, _) -> + eval_efun oc st ep f "main" params >>= fun (v, _) -> OK v diff --git a/src/regalloc.ml b/src/regalloc.ml index a843912..897da01 100644 --- a/src/regalloc.ml +++ b/src/regalloc.ml @@ -92,6 +92,7 @@ let make_interf_live (* TODO *) Hashtbl.iter (fun i regs -> Set.iter (fun x -> Set.iter (fun y -> if x < y then add_interf rig x y) regs) regs) live + (* [build_interference_graph live_out] construit, en utilisant les fonctions que vous avez écrites, le graphe d'interférence en fonction de la vivacité des variables à la sortie des nœuds donné par [live_out]. @@ -122,12 +123,9 @@ let build_interference_graph (live_out : (int, reg Set.t) Hashtbl.t) code : (reg [rig]. *) let remove_from_rig (rig : (reg, reg Set.t) Hashtbl.t) (v: reg) : unit = (* TODO *) - while Hashtbl.mem rig v do - Hashtbl.remove rig v - done; + Hashtbl.remove rig v; Hashtbl.iter (fun x regs -> Hashtbl.modify x (Set.remove v) rig) rig - (* Type représentant les différentes décisions qui peuvent être prises par l'allocateur de registres. diff --git a/tests/Makefile b/tests/Makefile index 7f5a992..ef3fa0a 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,7 +1,7 @@ # if make is launched with a DIR variable, pass it as the -f option to test.py # 'make DIR=basic/mul*.e' launches all the files starting with mul in the basic directory # otherwise, use basic/*.e as a default -FILES := $(if $(DIR),$(DIR),basic/*.e) +FILES := $(if $(DIR),$(DIR),funcall/*.e) OPTS := $(if $(OPTS), $(OPTS),) diff --git a/tests/funcall/argswap.e.expect_lexer b/tests/funcall/argswap.e.expect_lexer index 2c8a87d..89b98c3 100644 --- a/tests/funcall/argswap.e.expect_lexer +++ b/tests/funcall/argswap.e.expect_lexer @@ -5,12 +5,12 @@ SYM_COMMA SYM_IDENTIFIER(b) SYM_RPARENTHESIS SYM_LBRACE -SYM_IDENTIFIER(print) +SYM_PRINT SYM_LPARENTHESIS SYM_IDENTIFIER(a) SYM_RPARENTHESIS SYM_SEMICOLON -SYM_IDENTIFIER(print) +SYM_PRINT SYM_LPARENTHESIS SYM_IDENTIFIER(b) SYM_RPARENTHESIS @@ -30,12 +30,12 @@ SYM_COMMA SYM_IDENTIFIER(b) SYM_RPARENTHESIS SYM_LBRACE -SYM_IDENTIFIER(print) +SYM_PRINT SYM_LPARENTHESIS SYM_IDENTIFIER(a) SYM_RPARENTHESIS SYM_SEMICOLON -SYM_IDENTIFIER(print) +SYM_PRINT SYM_LPARENTHESIS SYM_IDENTIFIER(b) SYM_RPARENTHESIS diff --git a/tests/funcall/print_and_fun.e.expect_lexer b/tests/funcall/print_and_fun.e.expect_lexer index 834218f..d8e4934 100644 --- a/tests/funcall/print_and_fun.e.expect_lexer +++ b/tests/funcall/print_and_fun.e.expect_lexer @@ -21,12 +21,12 @@ SYM_LPARENTHESIS SYM_INTEGER(8) SYM_RPARENTHESIS SYM_SEMICOLON -SYM_IDENTIFIER(print) +SYM_PRINT SYM_LPARENTHESIS SYM_IDENTIFIER(a) SYM_RPARENTHESIS SYM_SEMICOLON -SYM_IDENTIFIER(print) +SYM_PRINT SYM_LPARENTHESIS SYM_IDENTIFIER(b) SYM_RPARENTHESIS -- GitLab 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 09/11] 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 From beadabee7b1c9875ba69142758bf44ad3ce41555 Mon Sep 17 00:00:00 2001 From: Youssef <youssef.sellami@student-cs.fr> Date: Sat, 15 Mar 2025 11:10:55 +0100 Subject: [PATCH 10/11] functions : RTL --- src/linear_gen.ml | 24 ++++++++++++------------ src/rtl.ml | 5 +++++ src/rtl_gen.ml | 18 +++++++++++++++++- src/rtl_print.ml | 4 ++++ src/rtl_run.ml | 19 +++++++++++++++++++ 5 files changed, 57 insertions(+), 13 deletions(-) diff --git a/src/linear_gen.ml b/src/linear_gen.ml index cd83deb..f6a6d14 100644 --- a/src/linear_gen.ml +++ b/src/linear_gen.ml @@ -8,6 +8,8 @@ open Linear_print open Options open Linear_liveness +type 'a set = 'a Set.t + let succs_of_rtl_instr (i: rtl_instr) = match i with | Rtl.Rbranch (_, _, _, s1) -> [s1] @@ -19,41 +21,39 @@ let rec succs_of_rtl_instrs il : int list = (* effectue un tri topologique des blocs. *) let sort_blocks (nodes: (int, rtl_instr list) Hashtbl.t) entry = - let rec add_block order visited n = + let rec add_block order visited n : reg list * reg set = (* TODO *) - List.of_enum (Hashtbl.keys nodes) - (*if Set.mem n visited - then order + (*List.of_enum (Hashtbl.keys nodes)*) + if Set.mem n visited + then order, visited else let succs = succs_of_rtl_instrs (Hashtbl.find nodes n) - in List.concat( (order@[n]) :: List.map (add_block [] (Set.add n visited)) succs )*) + in List.fold_left (fun (ord, vis) s -> add_block ord vis s) (order@[n], Set.add n visited) succs in - add_block [] Set.empty entry + fst (add_block [] Set.empty entry) (* Supprime les jumps inutiles (Jmp à un label défini juste en dessous). *) let rec remove_useless_jumps (l: rtl_instr list) = (* TODO *) - l - (*match l with + match l with | [] -> [] | Rjmp l1::Rlabel l2::rest -> if l1=l2 then Rlabel l2::remove_useless_jumps rest else Rjmp l1::Rlabel l2::remove_useless_jumps rest - | i::rest -> i::remove_useless_jumps rest*) + | i::rest -> i::remove_useless_jumps rest (* Remove labels that are never jumped to. *) let remove_useless_labels (l: rtl_instr list) = (* TODO *) - l - (*List.filter (function + List.filter (function Rlabel i -> List.exists ( function Rbranch(_, _, _, j) -> j = i | Rjmp j -> j = i | _ -> false) l - | _ -> true) l*) + | _ -> true) l let linear_of_rtl_fun ({ rtlfunargs; rtlfunbody; rtlfunentry; rtlfuninfo }: rtl_fun) = diff --git a/src/rtl.ml b/src/rtl.ml index ab703a0..0360732 100644 --- a/src/rtl.ml +++ b/src/rtl.ml @@ -15,6 +15,7 @@ type rtl_instr = Rbinop of binop * reg * reg * reg | Rret of reg | Rlabel of int | Rprint of reg + | Rcall of reg option * string * reg list type rtl_fun = { rtlfunargs: reg list; rtlfunbody: (int, rtl_instr list) Hashtbl.t; @@ -33,6 +34,10 @@ let written_rtl_regs_instr (i: rtl_instr) = | Rlabel _ | Rbranch (_, _, _, _) | Rjmp _ -> Set.empty + | Rcall (rd_opt, _, _) -> + match rd_opt with + | None -> Set.empty + | Some rd -> Set.singleton rd let written_rtl_regs (l: rtl_instr list) = List.fold_left (fun acc i -> Set.union acc (written_rtl_regs_instr i)) diff --git a/src/rtl_gen.ml b/src/rtl_gen.ml index 7955b3f..e49d304 100644 --- a/src/rtl_gen.ml +++ b/src/rtl_gen.ml @@ -53,6 +53,14 @@ let rec rtl_instrs_of_cfg_expr (next_reg, var2reg) (e: expr) = | Eunop (u, e) -> let r, l, next_reg', var2reg' = rtl_instrs_of_cfg_expr (next_reg, var2reg) e in (next_reg', l@[Runop(u, next_reg', r)], next_reg'+1, var2reg') + | Ecall (f, args) -> + let regs, l, next_reg', var2reg' = + List.fold_left + (fun (regs, instrs, next_reg, var2reg) arg -> + let r, l, next_reg', var2reg' = rtl_instrs_of_cfg_expr (next_reg, var2reg) arg + in (regs@[r], instrs@l, next_reg', var2reg')) + ([], [], next_reg, var2reg) args + in (next_reg', l@[Rcall (Some next_reg', f, regs)], next_reg' + 1, var2reg') let is_cmp_op = function Eclt -> Some Rclt @@ -89,8 +97,16 @@ let rtl_instrs_of_cfg_node ((next_reg:int), (var2reg: (string*int) list)) (c: cf let cmp, e1, e2 = rtl_cmp_of_cfg_expr e in let r1, l1, next_reg1, var2reg1 = rtl_instrs_of_cfg_expr (next_reg, var2reg) e1 in let r2, l2, next_reg2, var2reg2 = rtl_instrs_of_cfg_expr (next_reg1, var2reg1) e2 - in (l1@l2@[Rbranch(cmp, r1, r2, i1)]@[Rjmp i2], next_reg2, var2reg2) + in (l1@l2@[Rbranch(cmp, r1, r2, i1); Rjmp i2], next_reg2, var2reg2) | Cnop i -> ([Rjmp i], next_reg, var2reg) + | Ccall (f, args, i) -> + let regs, l, next_reg', var2reg' = + List.fold_left + (fun (regs, instrs, next_reg, var2reg) arg -> + let r, l, next_reg', var2reg' = rtl_instrs_of_cfg_expr (next_reg, var2reg) arg + in (regs@[r], instrs@l, next_reg', var2reg')) + ([], [], next_reg, var2reg) args + in (l@[Rcall (None, f, regs); Rjmp i], next_reg', var2reg') let rtl_instrs_of_cfg_fun cfgfunname ({ cfgfunargs; cfgfunbody; cfgentry }: cfg_fun) = let (rargs, next_reg, var2reg) = diff --git a/src/rtl_print.ml b/src/rtl_print.ml index f0b4734..a9c1224 100644 --- a/src/rtl_print.ml +++ b/src/rtl_print.ml @@ -41,6 +41,10 @@ let dump_rtl_instr name (live_in, live_out) ?(endl="\n") oc (i: rtl_instr) = | Rret r -> Format.fprintf oc "ret %s" (print_reg r) | Rprint r -> Format.fprintf oc "print %s" (print_reg r) | Rlabel n -> Format.fprintf oc "%s_%d:" name n + | Rcall (rd_opt, f, regs) -> + match rd_opt with + | None -> Format.fprintf oc "call %s with args : %s" f (String.concat ", " (List.map print_reg regs)) + | Some rd -> Format.fprintf oc "%s <- call %s with args : %s" (print_reg rd) f (String.concat ", " (List.map print_reg regs)) end; Format.fprintf oc "%s" endl; dump_liveness live_out "after" diff --git a/src/rtl_run.ml b/src/rtl_run.ml index f8dcde8..fb7a809 100644 --- a/src/rtl_run.ml +++ b/src/rtl_run.ml @@ -77,6 +77,25 @@ let rec exec_rtl_instr oc rp rtlfunname f st (i: rtl_instr) = | _ -> Error (Printf.sprintf "Print on undefined register (%s)" (print_reg r)) end | Rlabel n -> OK (None, st) + | Rcall (rd_opt, g, args) -> + begin + let vs_opt = List.fold_left (fun acc arg -> + match acc with + | None -> None + | Some vs -> + (match Hashtbl.find_option st.regs arg with + | None -> None + | Some v -> Some (vs@[v]))) + (Some []) args + in match vs_opt with + | Some params -> find_function rp g >>= fun found_g -> + (match rd_opt, exec_rtl_fun oc rp st g found_g params with + | _, Error msg -> Error msg + | Some rd, OK (Some ret, st') -> exec_rtl_instr oc rp rtlfunname f st' (Rconst (rd, ret)) + | Some rd, OK (None, st') -> Error (Printf.sprintf "Function %s doesn't have a return value" g) + | None, OK (_, st') -> OK(None, st')) + | _ -> Error (Printf.sprintf "Function %s applied on undefined register" g) + end and exec_rtl_instr_at oc rp rtlfunname ({ rtlfunbody; } as f: rtl_fun) st i = match Hashtbl.find_option rtlfunbody i with -- GitLab From 9e90172e13ff96b84f11c6b023e7493b5d846274 Mon Sep 17 00:00:00 2001 From: Youssef <youssef.sellami@student-cs.fr> Date: Sun, 16 Mar 2025 15:30:37 +0100 Subject: [PATCH 11/11] functions : LTL --- src/linear_liveness.ml | 7 +++++-- src/linear_run.ml | 19 +++++++++++++++++++ src/ltl_gen.ml | 35 ++++++++++++++++++++++++++++++----- 3 files changed, 54 insertions(+), 7 deletions(-) diff --git a/src/linear_liveness.ml b/src/linear_liveness.ml index 0a5891e..5082e7f 100644 --- a/src/linear_liveness.ml +++ b/src/linear_liveness.ml @@ -16,18 +16,21 @@ let gen_live (i: rtl_instr) = | Rmov (_, rs) -> Set.singleton rs | Rret r -> Set.singleton r | Rlabel _ -> Set.empty + | Rcall (_, _, args) -> Set.of_list args let kill_live (i: rtl_instr) = match i with | Rbinop (_, rd, _, _) | Runop (_, rd,_) | Rconst (rd, _) - | Rmov (rd,_) -> Set.singleton rd + | Rmov (rd,_) + | Rcall (Some rd, _, _) -> Set.singleton rd | Rbranch (_, _, _, _) | Rprint _ | Rret _ | Rjmp _ - | Rlabel _ -> Set.empty + | Rlabel _ + | Rcall (None, _, _) -> Set.empty let linear_succs (ins: rtl_instr) i labels = match ins with diff --git a/src/linear_run.ml b/src/linear_run.ml index 63b622a..8051790 100644 --- a/src/linear_run.ml +++ b/src/linear_run.ml @@ -60,6 +60,25 @@ let rec exec_linear_instr oc lp fname f st (i: rtl_instr) = | _ -> Error (Printf.sprintf "Ret on undefined register (%s)" (print_reg r)) end | Rlabel n -> OK (None, st) + | Rcall (rd_opt, g, args) -> + begin + let vs_opt = List.fold_left (fun acc arg -> + match acc with + | None -> None + | Some vs -> + (match Hashtbl.find_option st.regs arg with + | None -> None + | Some v -> Some (vs@[v]))) + (Some []) args + in match vs_opt with + | Some params -> find_function lp g >>= fun found_g -> + (match rd_opt, exec_linear_fun oc lp st g found_g params with + | _, Error msg -> Error msg + | Some rd, OK (Some ret, st') -> exec_linear_instr oc lp fname f st' (Rconst (rd, ret)) + | Some rd, OK (None, st') -> Error (Printf.sprintf "Function %s doesn't have a return value" g) + | None, OK (_, st') -> OK(None, st')) + | _ -> Error (Printf.sprintf "Function %s applied on undefined register" g) + end and exec_linear_instr_at oc lp fname ({ linearfunbody; } as f) st i = let l = List.drop_while (fun x -> x <> Rlabel i) linearfunbody in diff --git a/src/ltl_gen.ml b/src/ltl_gen.ml index aa6a9fa..581f0c9 100644 --- a/src/ltl_gen.ml +++ b/src/ltl_gen.ml @@ -51,7 +51,7 @@ let make_loc_mov src dst = [LMov(rdst,rsrc)] (* load_loc tmp allocation r = (l, r'). Loads the equivalent of RTL register r - in a LTL register r'. tmpis used if necessary. *) + in a LTL register r'. tmp is used if necessary. *) let load_loc tmp allocation r = match Hashtbl.find_option allocation r with | None -> @@ -215,26 +215,27 @@ let written_rtl_regs_instr (i: rtl_instr) = | Rbinop (_, rd, _, _) | Runop (_, rd, _) | Rconst (rd, _) - | Rmov (rd, _) -> Set.singleton rd + | Rmov (rd, _) + | Rcall (Some rd, _, _)-> Set.singleton rd | Rprint _ | Rret _ | Rlabel _ | Rbranch (_, _, _, _) - | Rjmp _ -> Set.empty + | Rjmp _ + | Rcall (None, _, _) -> Set.empty let read_rtl_regs_instr (i: rtl_instr) = match i with | Rbinop (_, _, rs1, rs2) | Rbranch (_, rs1, rs2, _) -> Set.of_list [rs1; rs2] - | Rprint rs | Runop (_, _, rs) | Rmov (_, rs) | Rret rs -> Set.singleton rs - | Rlabel _ | Rconst (_, _) | Rjmp _ -> Set.empty + | Rcall (_, _, args) -> Set.of_list args let read_rtl_regs (l: rtl_instr list) = List.fold_left (fun acc i -> Set.union acc (read_rtl_regs_instr i)) @@ -323,6 +324,30 @@ let ltl_instrs_of_linear_instr fname live_out allocation load_loc reg_tmp1 allocation r >>= fun (l,r) -> OK (l @ [LMov (reg_ret, r) ; LJmp epilogue_label]) | Rlabel l -> OK [LLabel (Format.sprintf "%s_%d" fname l)] + + | Rcall (rd_opt, f, rargs) -> + caller_save live_out allocation rargs >>= fun to_save -> + let save_regs_instrs, arg_saved, ofs = save_caller_save (Set.to_list to_save) (-(numspilled + 1)) + in let move_sp_instr1 = LSubi(reg_sp, reg_s0, (Archi.wordsize ()) * -(ofs + 1)) + in pass_parameters rargs allocation arg_saved >>= fun (parameter_passing_instrs, npush) -> + let call_instr = LCall f + in let move_sp_instr2 = LAddi(reg_sp, reg_sp, (Archi.wordsize ()) * npush) + in let return_instrs_and_reg = match rd_opt with + | None -> OK ([], None) + | Some rd -> + match Hashtbl.find_option allocation rd with + | None -> Error (Format.sprintf "Could not find allocation for register %d\n" rd) + | Some (Stk o) -> OK (make_loc_mov (Reg reg_ret) (Stk o) , None) + | Some (Reg r_phy) -> OK (make_loc_mov (Reg reg_ret) (Reg r_phy), Some r_phy) + in return_instrs_and_reg >>= fun (return_instrs, r_ret_opt) -> + let arg_saved_wout_rd = List.filter (fun (reg, stk_loc) -> match r_ret_opt with | None -> true | Some r_ret -> reg != r_ret) arg_saved + in let restore_caller_save_instrs = restore_caller_save arg_saved_wout_rd + in let move_sp_instr3 = LAddi(reg_sp, reg_sp, (Archi.wordsize ()) * -(ofs + 1)) + in OK (save_regs_instrs + @ move_sp_instr1 :: parameter_passing_instrs + @ call_instr :: move_sp_instr2 :: return_instrs + @ restore_caller_save_instrs + @ [move_sp_instr3]) in res >>= fun l -> OK (LComment (Format.asprintf "#<span style=\"background: pink;\"><b>Linear instr</b>: %a #</span>" (Rtl_print.dump_rtl_instr fname (None, None) ~endl:"") ins)::l) -- GitLab