diff options
-rw-r--r-- | doc.txt | 19 | ||||
-rw-r--r-- | src/Makefile | 6 | ||||
-rw-r--r-- | src/codegen.ml | 729 | ||||
-rw-r--r-- | src/main.ml | 3 | ||||
-rw-r--r-- | src/mips.ml | 6 | ||||
-rw-r--r-- | src/mips.mli | 6 | ||||
-rw-r--r-- | src/pretty_typing.ml | 23 | ||||
-rwxr-xr-x | src/test.sh | 73 | ||||
-rw-r--r-- | src/typing.ml | 169 | ||||
-rw-r--r-- | tests/exec/args1.cpp | 13 | ||||
-rw-r--r-- | tests/exec/args1.out | 2 | ||||
-rw-r--r-- | tests/exec/args2.cpp | 15 | ||||
-rw-r--r-- | tests/exec/args2.out | 2 |
13 files changed, 968 insertions, 98 deletions
@@ -0,0 +1,19 @@ +Pour une fonction f(x, y) on a sur la pile : + + | ..... | + | y | construit par + $fp--> | x | l'appelant + ---------------------------------- + | sauv. $fp | construit par + | sauv. $ra | l'appelé + | z | + | ..... | + +La valeur renvoyée par une fonction est passée dans le registre a0. + + +--- + +Tous les calculs se font en utilisant a0 comme sommet de pile (les seuls valeurs +plus grosses qu'un registre ne sont jamais manipulées en tant que valeurs mais toujours +en tant qu'adresses... donc tout va bien) diff --git a/src/Makefile b/src/Makefile index c7ff839..619fac9 100644 --- a/src/Makefile +++ b/src/Makefile @@ -2,9 +2,9 @@ BIN=minic++ all: $(BIN) -$(BIN): main.ml ast.ml parser.mly lexer.mll pretty.ml typing.ml pretty_typing.ml - ocamlbuild main.byte - mv main.byte $(BIN) +$(BIN): main.ml ast.ml parser.mly lexer.mll pretty.ml typing.ml pretty_typing.ml codegen.ml + ocamlbuild main.native + mv main.native $(BIN) clean: rm -r _build diff --git a/src/codegen.ml b/src/codegen.ml index 2709526..6ada77a 100644 --- a/src/codegen.ml +++ b/src/codegen.ml @@ -1,6 +1,731 @@ open Mips +open Typing + +exception Very_bad_error of string + +exception Reference_register of register + +(* Convention pour les registres : + - a0, a1, a2, a3 : contiennent les (éventuels) 4 premiers arguments de la fonction + - v0 : contient la valeur de retour des fonctions (rien de particulier pour un constructeur) + - v0-v1, t0-t9, s0-s1 : utilisés pour les calculs + - fp contient un pointeur de frame mis à jour pour chaque appel + de fonction + - sp n'est pas tenu à jour en fonction de l'état de la pile, par contre il est + utilisé lors d'un appel de fonction pour pouvoir définir le nouvea fp, + il est donc mis à jour avant chaque appel de fonction pour effectivement refléter + l'état d'utilisation de la pile (pile sur laquelle il peut d'ailleurs y avoir + des arguments excedentaires) + Tous les registres doivent être sauvés par l'appellant sauf fp + Les registres a0, a1, a2, a3 sont susceptibles d'être modifiés par la fonction appellée. + **sauf dans le cas où a0 représente this** !! +*) + +(* Environnement pour accéder aux variables *) +type whereis_var = + | VGlobal + | VStack of int (* position relative à $fp *) + | VStackByRef of int + | VRegister of register + | VRegisterByRef of register + +type cg_env = { + c_penv : env; + c_names : whereis_var Smap.t; + c_ret_ref : bool; + c_ret_lbl : string; + c_fp_used : int; + c_need_fp : bool ref; + c_save_regs : register list; + c_free_regs : register list; +} + +let env_push n e = + if n <> 0 then e.c_need_fp := true; + let kk = e.c_fp_used + n in + { c_penv = e.c_penv; + c_names = e.c_names; + c_ret_ref = e.c_ret_ref; + c_ret_lbl = e.c_ret_lbl; + c_need_fp = e.c_need_fp; + c_fp_used = kk; + c_free_regs = e.c_free_regs; + c_save_regs = e.c_save_regs }, -kk + +let env_add_var vid vv e = + { c_penv = e.c_penv; + c_names = Smap.add vid vv e.c_names; + c_ret_ref = e.c_ret_ref; + c_ret_lbl = e.c_ret_lbl; + c_save_regs = e.c_save_regs; + c_free_regs = e.c_free_regs; + c_fp_used = e.c_fp_used; + c_need_fp = e.c_need_fp; } + +let env_get_free_reg e = + let r, more = List.hd e.c_free_regs, List.tl e.c_free_regs in + { c_penv = e.c_penv; + c_names = e.c_names; + c_ret_ref = e.c_ret_ref; + c_ret_lbl = e.c_ret_lbl; + c_need_fp = e.c_need_fp; + c_fp_used = e.c_fp_used; + c_free_regs = more; + c_save_regs = r::e.c_save_regs }, r + +let globals_env = ref Smap.empty + +(* Chaînes de caractères utilisées dans le programme *) +let strings = Hashtbl.create 12 (* string -> label *) + +(* Identifiants uniques pour divers objets - essentiellement labels *) +let id = + let last = ref 0 in + fun prefix -> (last := !last + 1; prefix ^ (string_of_int !last)) + +(* Doit-on se préparer à faire des appels de fonction ? Ie sauvegarder $ra *) +let rec expr_does_call e = match e.te_desc with + | TEInt _ | TENull | TEThis | TEIdent _ -> false + | TEAssign(a, b) -> expr_does_call a || expr_does_call b + | TECallFun (_, _, _) -> true + | TECallVirtual (_, _, _, _) -> true + | TEUnary (_, e) -> expr_does_call e + | TEBinary (a, _, b) -> expr_does_call a || expr_does_call b + | TEMember (e, _) -> expr_does_call e + | TEPointerCast(e, _) -> expr_does_call e + | TENew(_, _, _) -> true +let rec stmt_does_call = function + | TSEmpty | TSReturn(None) -> false + | TSExpr(e) -> expr_does_call e + | TSIf (e, sa, sb) -> expr_does_call e || stmt_does_call sa || stmt_does_call sb + | TSWhile(e, s) -> expr_does_call e || stmt_does_call s + | TSFor(e, f, g, s) -> (List.exists expr_does_call e) || (match f with | None -> false | Some k -> expr_does_call k) + || (List.exists expr_does_call g) || stmt_does_call s + | TSBlock(k) -> List.exists stmt_does_call k + | TSReturn(Some k) -> expr_does_call k + | TSDeclare(TClass _, _) -> true + | TSDeclare (_, _) -> false + | TSDeclareAssignExpr(_, _, e) -> expr_does_call e + | TSDeclareAssignConstructor(_, _, _, _) -> true + | TSWriteCout(l) -> List.exists (function | TSEExpr e -> expr_does_call e | TSEStr _ -> false) l + + +(* La génération de code, enfin ! *) + +(* Arguments de la fonction gen_expr : + - un environnement : permet de savoir plein de choses, par exemple combien de place est + utilisée sur la pile en-dessous de $fp + - une liste de registres disponnibles pour faire le calcul + *qui doit toujours contenir au moins un registre* + - une liste de registres à sauvegarder dans tous les cas + - l'expression pour laquelle on veut générer du code + + À l'issue d'un appel à gen_expr, il y a plusieurs possibilités, exprimées + par le type union expr_type décrit ci-dessus : + - le premier registre de la liste des registres disponnibles (noté r) contient + une adresse qui est l'adresse de la valeur dénotée par l'expression + - la valeur dénotée est stockée dans x(reg) pour un certain reg et un certain x + - la valeur est stockée dans un certain registre a, qui est son + "registre de référence", ie si on doit affecter à cette valeur on peut + modifier ce registre + - la valeur est stockée dans le registre r + Dans tous les cas sauf le dernier, on peut modifier la valeur dénotée par + l'expression (par exemple lors d'une affectation). + Si le typage nous garantit que l'expression ne peut pas être affectée, on peut + utiliser l'artifice de dire qu'une valeur est placée dans un registre comme + "registre de référence" même lorsque ce n'est pas le cas (= jouer avec le feu). +*) + +(* possibilités pour ce qui est généré par gen_expr *) +type expr_type = + | Addr (* top register contains address of value *) + | AddrByReg of int * register (* value at int(register) *) + | Value of register (* other register is home to the value *) + | Copy (* top register contains copy of value *) + +(* on a fait un appel à gen_expr, maintenant on veut être sûr d'avoir + soit l'adresse soit la valeur dans tel ou tel registre *) +let cla r a = match a with + | Addr -> nop + | AddrByReg(x, rg) -> la r areg (x, rg) + | Value r -> raise (Reference_register r) + | _ -> assert false +let cr r a = match a with (* conditionnally read *) + | Addr -> lw r areg (0, r) + | AddrByReg(x, rg) -> lw r areg (x, rg) + | Copy -> nop + | Value k -> if r <> k then move r k else nop +let crb r q a = match a with + | Value k -> q, k + | _ -> q ++ cr r a, r + +let spare_reg = s0 +let spare_reg2 = s1 + +(* Cette fonction prévoit de l'espace sur la pile pour enregistrer les + valeurs de tous les registres save_regs à sauvegarder (elle donne un nouvel + environnement où la place nécessaire est réservée) et génère le code + nécessaire à la sauvegarde et à la restauration. + Le nouvel environnement est également modifié de manière à ce que de futurs + appels à des valeurs qui devaient être enregistrées dans des registres sauvegardés + soient maintenant fait en prenant en compte la relocalisation de ces valeurs + sur la pile. *) +let saver env save_regs = + List.fold_left + (fun (code, more_code, env) r -> + let new_fp_used = env.c_fp_used + 4 in + let pos = - new_fp_used in + env.c_need_fp := true; + code ++ sw r areg (pos, fp), lw r areg (pos, fp) ++ more_code, + { c_penv = env.c_penv; + c_names = Smap.map + (function + | VRegister k when k = r -> VStack (pos) + | VRegisterByRef k when k = r -> VStackByRef(pos) + | a -> a) env.c_names; + c_ret_ref = env.c_ret_ref; + c_ret_lbl = env.c_ret_lbl; + c_fp_used = new_fp_used; + c_need_fp = env.c_need_fp; + c_free_regs = env.c_free_regs; + c_save_regs = (List.filter (fun k -> k <> r) env.c_save_regs) } + ) + (nop, nop, env) save_regs + +(* + renvoie le résultat dans le premier registre de free_regs + ou autre (cf ci-dessus) +*) +let rec gen_expr env free_regs save_regs e = + (* register management *) + let r = List.hd free_regs in (* register where to put result *) + let more = List.tl free_regs in + (* generate the code... *) + match e.te_desc with + | TEInt(k) -> li r k, Copy + | TENull -> nop, Value zero + | TEThis -> (* convention : this is always the first argument, so in a0 *) + begin match Smap.find "this" env.c_names with + | VRegister(k) when k <> r -> nop, Value k + | VStack(i) -> nop, AddrByReg(i, fp) + | _ -> assert false + end + | TEIdent(i) -> + begin match Smap.find i env.c_names with + | VGlobal -> la r alab i, Addr + | VStack(i) -> nop, AddrByReg(i, fp) + | VStackByRef(i) -> lw r areg (i, fp), Addr + | VRegister(k) -> nop, Value k + | VRegisterByRef(k) -> nop, AddrByReg(0, k) + end + | TEAssign(e1, e2) -> + begin match more with + | [] -> + let t1, ae1 = gen_expr env free_regs save_regs e1 in + let env2, tspot = env_push 4 env in + let t2, ae2 = gen_expr env2 free_regs save_regs e2 in + let t2 = t2 ++ cr r ae2 in + begin match ae1 with + | Addr -> t1 ++ sw r areg (tspot, fp) ++ t2 ++ lw spare_reg areg (tspot, fp) ++ sw r areg (0, spare_reg), Copy + | AddrByReg (x, rg) when t1 = nop -> t2 ++ sw r areg (x, rg), Copy + | Value k when t1 = nop && k <> r -> t2 ++ move k r, Copy + | _ -> assert false + end + | b::_ -> + let t1, ae1 = gen_expr env more (r::save_regs) e1 in + let t2, ae2 = gen_expr env free_regs save_regs e2 in + let t2, r2 = crb r t2 ae2 in + let tr = if r2 = r then Copy else Value r2 in + begin match ae1 with + | Addr -> t2 ++ t1 ++ sw r2 areg (0, b), tr + | AddrByReg (x, rg) when t1 = nop -> t2 ++ sw r2 areg (x, rg), tr + | Value k when t1 = nop && k <> r2 -> t2 ++ move k r2, tr + | _ -> assert false + end + end + | TECallFun(id, args, b) -> + let keep_result_in_v0 = (not (List.mem v0 save_regs)) || r = v0 in + let code_save_regs, code_restore_regs, env_regs_saved = saver env save_regs in + let args_code, _, env_args = code_for_args env_regs_saved args [ a0; a1; a2; a3 ] in + code_save_regs + ++ args_code + ++ la sp areg (-env_args.c_fp_used, fp) ++ jal id + ++ (if keep_result_in_v0 then nop else move r v0) + ++ code_restore_regs, + if b + then (if keep_result_in_v0 then AddrByReg (0, v0) else Addr) + else (if keep_result_in_v0 then Value(v0) else Copy) + | TECallVirtual(obj, fi, args, b) -> + let keep_result_in_v0 = (not (List.mem v0 save_regs)) || r = v0 in + let code_save_regs, code_restore_regs, env_regs_saved = saver env save_regs in + let args_code, sr, env_args = code_for_args env_regs_saved ((obj, true)::args) [ a0; a1; a2; a3 ] in + code_save_regs + ++ args_code + ++ lw v0 areg (0, a0) ++ lw v0 areg (fi, v0) + ++ la sp areg (-env_args.c_fp_used, fp) ++ jalr v0 + ++ (if keep_result_in_v0 then nop else move r v0) + ++ code_restore_regs, + if b + then (if keep_result_in_v0 then AddrByReg (0, v0) else Addr) + else (if keep_result_in_v0 then Value(v0) else Copy) + | TEUnary (x, e) -> + let t, a = gen_expr env free_regs save_regs e in + begin match x with + | Ast.Deref -> + begin match a with + | Value r -> t, AddrByReg (0, r) + | _ -> t ++ cr r a, Addr + end + | Ast.Ref -> + t ++ cla r a, Copy + | Ast.Plus -> t ++ cr r a, Copy + | Ast.Minus -> t ++ cr r a ++ neg r r, Copy + | Ast.Not -> t ++ cr r a ++ not_ r r, Copy + | Ast.PreIncr | Ast.PreDecr -> + let delta = if x = Ast.PreIncr then 1 else -1 in + begin match a with + | Addr -> + t ++ move spare_reg r ++ lw r areg (0, spare_reg) + ++ add r r oi delta ++ sw r areg (0, spare_reg), Copy + | AddrByReg (k, rg) when t = nop && r <> rg -> + lw r areg (k, rg) + ++ add r r oi delta ++ sw r areg (k, rg), Copy + | Value v when t = nop && v <> r -> + add v v oi delta ++ move r v, Copy + | _ -> assert false + end + | Ast.PostIncr | Ast.PostDecr -> + let delta = if x = Ast.PostIncr then 1 else -1 in + begin match a with + | Addr -> + t ++ move spare_reg r + ++ lw r areg(0, spare_reg) + ++ add spare_reg2 r oi delta + ++ sw spare_reg2 areg(0, spare_reg), Copy + | AddrByReg (k, rg) when t = nop && r <> rg -> + lw r areg (k, rg) + ++ add spare_reg r oi delta + ++ sw spare_reg areg (k, rg), Copy + | Value v when t = nop && v <> r -> + move r v ++ add v v oi delta, Copy + | _ -> assert false + end + end + | TEBinary(e1, op, e2) when op <> Ast.Lor && op <> Ast.Land -> + let rs, rb, precode = match more with + | [] -> + let env2, tspot = env_push 4 env in + let t1, ae1 = gen_expr env2 free_regs save_regs e1 in + let t2, ae2 = gen_expr env free_regs save_regs e2 in + let t1, r1 = crb r t1 ae1 in + let t2, r2 = crb r t2 ae2 in + r1, spare_reg, t2 ++ sw r2 areg (tspot, fp) ++ t1 ++ lw spare_reg areg (tspot, fp) + | b::_ -> + let t1, ae1 = gen_expr env free_regs save_regs e1 in + let t2, ae2 = gen_expr env more (r::save_regs) e2 in + let t1, rs = crb r t1 ae1 in + let t2, rb = crb b t2 ae2 in + rs, rb, t1 ++ t2 + in + precode ++ (match op with + | Ast.Add -> add r rs oreg rb + | Ast.Sub -> sub r rs oreg rb + | Ast.Mul -> mul r rs oreg rb + | Ast.Div -> div r rs oreg rb + | Ast.Modulo -> rem r rs oreg rb + | Ast.Equal -> seq r rs rb + | Ast.NotEqual -> sne r rs rb + | Ast.Lt -> slt r rs rb + | Ast.Le -> sle r rs rb + | Ast.Gt -> sgt r rs rb + | Ast.Ge -> sge r rs rb + | _ -> assert false + ), Copy + | TEBinary(e1, op, e2) (* when op = Ast.Lor || op = Ast.Land *) -> + let t1, ae1 = gen_expr env free_regs save_regs e1 in + let t2, ae2 = gen_expr env free_regs save_regs e2 in + let t1 = t1 ++ cr r ae1 in + let t2 = t2 ++ cr r ae2 in + let lazy_lbl = id "_lazy" in + t1 ++ (if op = Ast.Lor then bnez r lazy_lbl else beqz r lazy_lbl) + ++ t2 ++ label lazy_lbl ++ sne r r zero, Copy + | TEMember(e, i) -> + let c, a = gen_expr env free_regs save_regs e in + if i <> 0 then begin + match a with + | Addr -> c ++ la r areg (i, r), Addr + | AddrByReg (k, rg) when c = nop -> nop, AddrByReg (k + i, rg) + | _ -> assert false + end else + c, a + | TEPointerCast(e, i) -> + let c, a = gen_expr env free_regs save_regs e in + c ++ cr r a ++ (if i = 0 then nop else la r areg (i, r)), Copy + | TENew(cls, constr, args) -> + let code_save_regs, code_restore_regs, env_regs_saved = saver env save_regs in + let args_code, _, env_args = code_for_args env_regs_saved args [ a1; a2; a3 ] in + code_save_regs ++ args_code + ++ li v0 9 ++ li a0 cls.tc_size ++ syscall ++ move a0 v0 + ++ la sp areg (-env_args.c_fp_used, fp) ++ jal constr + ++ (if r <> a0 then move r a0 else nop) ++ code_restore_regs, Copy +and code_for_args env arg_list regs = + (* assigne registers to possibly in-register arguments *) + let args_in_regs, args_in_stack, _ = List.fold_left + (fun (ir, is, fr) (arg, byref) -> + match fr with + | [] -> ir, (arg, byref)::is, [] + | r::nfr -> (r, (arg, byref))::ir, is, nfr) + ([], [], regs) arg_list in + (* allocate stack for remaining args *) + let stack_use = 4 * List.length args_in_stack in + let kenv, _ = env_push stack_use env in + (* make code for in-stack arguments *) + let args_in_stack = List.rev args_in_stack in + let code_for_stack, _ = List.fold_left + (fun (code, u) (arg, byref) -> + let c, addr = gen_expr kenv (v0::kenv.c_free_regs) [] arg in + (if byref then + c ++ cla v0 addr ++ sw v0 areg (-kenv.c_fp_used + u, fp) ++ code, u+4 + else + let c, freg = crb v0 c addr in + c ++ sw freg areg (-kenv.c_fp_used + u, fp) ++ code, u+4 + ) + ) (nop, 0) args_in_stack in + (* make code for in-register arguments *) + let arg_reg_do_call, arg_reg_dont_call = + List.partition (fun (_, (e, _)) -> expr_does_call e) args_in_regs in + let rec mk_code_callz e = function + | [] -> nop + | (reg, (expr, byref))::more_args -> + let c, addr = gen_expr e (reg::kenv.c_free_regs) [] expr in + if more_args = [] then + c ++ (if byref then cla reg addr else cr reg addr) + else + let e2, pos = env_push 4 e in + (if byref then + c ++ cla reg addr ++ sw reg areg (pos, fp) + else + let tt, r2 = crb reg c addr in + tt ++ sw r2 areg (pos, fp)) + ++ (mk_code_callz e2 more_args) ++ lw reg areg (pos, fp) + in + let code_reg_do_call = mk_code_callz kenv arg_reg_do_call in + let code_reg_dont_call, _ = + List.fold_left + (fun (code, ur) (reg, (expr, byref)) -> + let c, addr = gen_expr kenv (reg::kenv.c_free_regs) ur expr in + code ++ c ++ (if byref then cla reg addr else cr reg addr), reg::ur) + (nop, []) arg_reg_dont_call + in + let code = code_for_stack ++ code_reg_do_call ++ code_reg_dont_call + in code, (List.map fst args_in_regs), kenv + + +let gen_expr_dr dr env = gen_expr env (dr::env.c_free_regs) env.c_save_regs +let gen_expr_v0 = gen_expr_dr v0 + +let rec gen_stmt alloc_vars_in_regs env = function + | TSEmpty -> nop, env + | TSExpr(e) -> + comment "expr" ++ (fst (gen_expr_v0 env e)), env + | TSIf(cond, s1, s2) -> + let c, a = gen_expr_v0 env cond in + let c, reg = crb v0 c a in + let l_else = id "_cond_else" in + let l_end = id "_cond_end" in + let c_then = gen_block env [s1] in + let c_else = gen_block env [s2] in + comment "if" + ++ c ++ beqz reg l_else + ++ c_then ++ b l_end + ++ label l_else ++ c_else + ++ label l_end, env + | TSWhile(cond, body) -> + let c, a = gen_expr_v0 env cond in + let c, reg = crb v0 c a in + let l_begin = id "_while_begin" in + let l_cond = id "_while_cond" in + let c_body = gen_block env [body] in + comment "while" ++ b l_cond + ++ label l_begin ++ c_body + ++ label l_cond ++ c ++ bnez reg l_begin, env + | TSFor(before, cond, after, body) -> + let l_begin = id "_for_begin" in + let l_cond = id "_for_cond" in + let c_before = List.fold_left + (fun code expr -> let c, _ = gen_expr_v0 env expr in code ++ c) nop before in + let c_after = List.fold_left + (fun code expr -> let c, _ = gen_expr_v0 env expr in code ++ c) nop after in + let c_cond = match cond with + | None -> b l_begin + | Some x -> + let c, a = gen_expr_v0 env x in + let c, reg = crb v0 c a in + c ++ bnez reg l_begin in + let c_body = gen_block env [body] in + comment "for" + ++ c_before ++ b l_cond + ++ label l_begin ++ c_body ++ c_after + ++ label l_cond ++ c_cond, env + | TSBlock(b) -> + let c = gen_block env b in + comment "block" ++ c, env + | TSReturn (None) -> + comment "return" ++ b env.c_ret_lbl, env + | TSReturn (Some e) -> + let c, a = gen_expr_v0 env e in + comment "return" + ++ c ++ (if env.c_ret_ref then cla v0 a else cr v0 a) + ++ b env.c_ret_lbl, env + | TSDeclare (ty, id) -> + if num ty && alloc_vars_in_regs && List.length env.c_free_regs > 5 then + (* allocate variable in register *) + let env2, reg = env_get_free_reg env in + comment ("declare " ^ id) ++ move reg zero, + env_add_var id (VRegister reg) env2 + else + let s = type_size env.c_penv ty in + let env2, pos = env_push s env in + let code = match ty with + | TClass(i) -> + let c = get_c env.c_penv i in + let cproto = List.find + (fun p -> p.tp_ret_type = None && p.tp_name = i && p.tp_args = []) c.tc_methods in + let code_save_regs, code_restore_regs, env_regs_saved = saver env2 env.c_save_regs in + code_save_regs + ++ la a0 areg (pos, fp) + ++ la sp areg (-env_regs_saved.c_fp_used, fp) ++ jal cproto.tp_unique_ident + ++ code_restore_regs + | _ -> sw zero areg (pos, fp) + in + comment ("declare " ^ id) ++ code, + env_add_var id (VStack pos) env2 + | TSDeclareAssignConstructor(cls, id, constr, args) -> + let env2, pos = env_push cls.tc_size env in + let code = + let code_save_regs, code_restore_regs, env_regs_saved = saver env2 env.c_save_regs in + let args_code, _, env_args = code_for_args env_regs_saved args [ a1; a2; a3 ] in + code_save_regs + ++ args_code ++ la a0 areg(pos, fp) + ++ la sp areg (-env_args.c_fp_used, fp) ++ jal constr + ++ code_restore_regs + in + comment ("declare " ^ id) ++ code, + env_add_var id (VStack pos) env2 + | TSDeclareAssignExpr ((ty, ref), id, e) -> + assert (ref || num ty); + if alloc_vars_in_regs && List.length env.c_free_regs > 5 then + (* allocate variable in register *) + let env2, reg = env_get_free_reg env in + let code, a = gen_expr env (reg::env2.c_free_regs) env.c_save_regs e in + comment ("declare " ^ id) + ++ code ++ (if ref then cla reg a else cr reg a), + env_add_var id (if ref then VRegisterByRef reg else VRegister reg) env2 + else + let code, a = gen_expr_v0 env e in + let env2, pos = env_push 4 env in + comment ("declare " ^ id) + ++ (if ref then + code ++ cla v0 a ++ sw v0 areg (pos, fp) + else + let k, b = crb v0 code a in + k ++ sw b areg (pos, fp) + ), + env_add_var id (if ref then VStackByRef pos else VStack pos) env2 + | TSWriteCout(sl) -> + let save_code, restore_code, env2 = saver env + (if List.mem a0 env.c_save_regs then [a0] else []) in + let text1 = List.fold_left + (fun text -> function + | TSEExpr(e) -> + let t, a = gen_expr_dr a0 env2 e in + text ++ t ++ cr a0 a ++ li v0 1 ++ syscall + | TSEStr(s) -> + let l = + if Hashtbl.mem strings s then + Hashtbl.find strings s + else + let l = id "_s" in Hashtbl.add strings s l; + l + in + text ++ la a0 alab l ++ li v0 4 ++ syscall) + nop sl in + comment "cout<<..." + ++ save_code ++ text1 ++ restore_code, env +and gen_block env b = + let rec fold env = function + | [] -> nop + | stmt::next -> + let does_call_after = List.exists stmt_does_call next in + try + let tt, ee = gen_stmt (not does_call_after) env stmt in + let more_code = fold ee next in + tt ++ more_code + with Reference_register _ -> + let tt, ee = gen_stmt false env stmt in + let more_code = fold ee next in + tt ++ more_code + in + fold env b + +let gen_decl tenv decl = match decl with + | TDGlobal(ty, id) -> + globals_env := Smap.add id VGlobal !globals_env; + let bytes = type_size tenv ty in + nop, (label id) ++ (dword (let rec a n = if n > 0 then 0::(a (n-4)) else [] in a bytes)) + | TDFunction(proto, block) -> + let regs_for_args, env0 = match proto.tp_class with + | None -> [ a0; a1; a2; a3 ], !globals_env + | Some k -> [ a1; a2; a3 ], Smap.add "this" (VRegister a0) !globals_env + in + let need_fp = ref false in + let names, _, free_regs = List.fold_left + (fun (env, p, regs) ((ty, r), id) -> + assert (r || type_size tenv ty = 4); + match regs with + | reg::more_regs -> + Smap.add id (if r then VRegisterByRef reg else VRegister reg) env, p, more_regs + | [] -> need_fp := true; + Smap.add id (if r then VStackByRef p else VStack p) env, p + 4, regs + ) + (env0, 0, regs_for_args) proto.tp_args in + let env = { + c_penv = tenv; + c_names = names; + c_ret_ref = (match proto.tp_ret_type with | None -> false | Some(_, r) -> r); + c_ret_lbl = "_return_" ^ proto.tp_unique_ident; + c_fp_used = 8; + c_need_fp = need_fp; + c_free_regs = [ t0; t1; t2; t3; t4; t5; t6; t7; t8; t9; v1 ]; + c_save_regs = List.filter (fun r -> not (List.mem r free_regs)) [a0; a1; a2; a3]; + } in + let code_for_constructor, does_calls = match proto.tp_ret_type with + | Some _ -> nop, (List.exists stmt_does_call block) + | None -> let cls_name = (match proto.tp_class with | Some k -> k | None -> assert false) in + la sp areg (-8, fp) ++ jal (cls_name ^ "0"), true + in + let code_for_virtual = match proto.tp_virtual with + | Some (c, _) when c.h_pos <> 0 -> + la a0 areg (-c.h_pos, a0) + | _ -> nop + in + if does_calls + then + let save_code, unsave_code, env2 = + saver env (List.filter (fun x -> x <> a0 || proto.tp_class = None) env.c_save_regs) + in + let text = gen_block env2 block in + label proto.tp_unique_ident + ++ sw fp areg (-4, sp) ++ sw ra areg (-8, sp) ++ move fp sp + ++ code_for_virtual ++ save_code ++ code_for_constructor ++ text + ++ label env.c_ret_lbl ++ move sp fp ++ lw fp areg (-4, sp) ++ lw ra areg (-8, sp) + ++ jr ra, nop + else + let rec bb_fp e = + try + gen_block e block + with Reference_register r -> + let save_code, _, env2 = saver env [r] in + save_code ++ bb_fp env2 + in + let text = bb_fp env in + label proto.tp_unique_ident + ++ (if !need_fp then sw fp areg (-4, sp) ++ move fp sp else nop) + ++ code_for_virtual ++ text + ++ label env.c_ret_lbl ++ (if !need_fp then move sp fp ++ lw fp areg (-4, sp) else nop) + ++ jr ra, nop + | TDClass(c) -> + let constructor_calls_something = ref false in + (* Call default constructor of parent classes *) + let code_parents = List.fold_left + (fun code parent -> + let cn = parent.h_class in + let c = get_c tenv cn in + let proto = List.find + (fun p -> p.tp_ret_type = None && p.tp_args = [] && p.tp_name = cn) + c.tc_methods in + constructor_calls_something := true; + code ++ (if parent.h_pos <> 0 then la a0 areg(parent.h_pos, a0) else nop) + ++ jal proto.tp_unique_ident ++ (if parent.h_pos <> 0 then lw a0 areg (-12, fp) else nop)) + nop c.tc_hier.h_supers in + (* Build vtables and build constructor *) + let rec make_vtables hh = + (* calculate vtable contents *) + let vtable_size = List.fold_left (fun k (p, _) -> max k (p+4)) 0 hh.h_vtable in + let vtable_as_array = Array.make (vtable_size / 4) "_nothing" in + List.iter (fun (p, s) -> vtable_as_array.(p/4) <- s.tp_unique_ident) hh.h_vtable; + let vt_l = Array.to_list vtable_as_array in + (* code for vtable initialization *) + let vtable = + if vt_l = [] + then nop + else label ("_vt_" ^ c.tc_name ^ "_as_" ^ hh.h_class) ++ address vt_l in + let constructor_code = + if vt_l = [] + then nop + else la a1 alab ("_vt_" ^ c.tc_name ^ "_as_" ^ hh.h_class) + ++ sw a1 areg (hh.h_pos, a0) in + (* code for subclasses vtable initialization *) + List.fold_left + (fun (vt, cc) sup -> + let mvt, mcc = make_vtables sup in + vt ++ mvt, cc ++ mcc) + (vtable, constructor_code) hh.h_supers + in + let vtables, vtable_init_code = make_vtables c.tc_hier in + (* Initialize members *) + let init_code_proper = Smap.fold + (fun _ (ty, pos) code -> + code ++ (match ty with + | TClass(s) -> + let cs = get_c tenv s in + let proto = List.find + (fun p -> p.tp_ret_type = None && p.tp_args = [] && p.tp_name = s) + cs.tc_methods in + constructor_calls_something := true; + (if pos <> 0 then la a0 areg (pos, a0) else nop) + ++ la sp areg (-12, fp) + ++ jal proto.tp_unique_ident + ++ (if pos <> 0 then lw a0 areg (-12, fp) else nop) + | _ -> sw zero areg (pos, a0))) + c.tc_members nop + in (* Put it all together *) + label (c.tc_name ^ "0") + ++ (if !constructor_calls_something then + sw fp areg (-4, sp) ++ move fp sp ++ sw ra areg (-8, fp) + ++ sw a0 areg (-12, fp) ++ la sp areg (-12, fp) + else nop) + ++ code_parents ++ vtable_init_code ++ init_code_proper + ++ (if !constructor_calls_something then + lw ra areg (-8, fp) ++ move sp fp ++ lw fp areg (-4, sp) + else nop) + ++ jr ra, vtables let generate p = - { text = nop; - data = nop } + try + let text, data = List.fold_left (fun (text, data) decl -> + let more_text, more_data = gen_decl p.prog_env decl in + text ++ more_text, data ++ more_data) (nop, nop) p.prog_decls in + let text = + label "main" + ++ jal p.prog_main + ++ li v0 10 ++ syscall + ++ label "_nothing" ++ jr ra + ++ text in + let str = Hashtbl.fold + (fun str lbl data -> data ++ label lbl ++ asciiz str) + strings nop in + { text = text; + data = data ++ str } + with + | Assert_failure (k, a, b) -> raise (Very_bad_error ( + "(unexpected) Assertion failure: "^k^" at "^(string_of_int a)^":"^(string_of_int b))) + | Not_found -> raise (Very_bad_error ("(unexpected) Not found")) + | Invalid_argument(k) -> raise (Very_bad_error ("(unexpected) Invalid argument: "^k)) + | Match_failure(k, a, b) -> raise (Very_bad_error ( + "(unexpected) Match failure: "^k^" at "^(string_of_int a)^":"^(string_of_int b))) + | Stack_overflow -> raise (Very_bad_error ("(unexpected) Stack overflow")) + | _ -> raise (Very_bad_error ("(unexpected) Other error")) + + diff --git a/src/main.ml b/src/main.ml index 976586f..a087cb5 100644 --- a/src/main.ml +++ b/src/main.ml @@ -78,6 +78,9 @@ let () = localisation2 loc; eprintf "%s@." msg; exit 2 + | Codegen.Very_bad_error(msg) -> + eprintf "Very bad error: %s@." msg; + exit 3; | _ -> eprintf "Unexpected error...@."; diff --git a/src/mips.ml b/src/mips.ml index 5fe310c..0359728 100644 --- a/src/mips.ml +++ b/src/mips.ml @@ -17,6 +17,12 @@ let t0 : register = "$t0" let t1 : register = "$t1" let t2 : register = "$t2" let t3 : register = "$t3" +let t4 : register = "$t4" +let t5 : register = "$t5" +let t6 : register = "$t6" +let t7 : register = "$t7" +let t8 : register = "$t8" +let t9 : register = "$t9" let s0 : register = "$s0" let s1 : register = "$s1" let ra : register = "$ra" diff --git a/src/mips.mli b/src/mips.mli index a42f1ce..f1e39d6 100644 --- a/src/mips.mli +++ b/src/mips.mli @@ -57,6 +57,12 @@ val t0 : register val t1 : register val t2 : register val t3 : register +val t4 : register +val t5 : register +val t6 : register +val t7 : register +val t8 : register +val t9 : register val s0 : register val s1 : register val ra : register diff --git a/src/pretty_typing.ml b/src/pretty_typing.ml index f9e69ff..a6ec2eb 100644 --- a/src/pretty_typing.ml +++ b/src/pretty_typing.ml @@ -42,20 +42,21 @@ let rec expr_string e = match e.te_desc with | TEThis -> "this" | TEIdent(i) -> i | TEAssign(k, p) -> "(" ^ (expr_string k) ^ " = " ^ (expr_string p) ^ ")" - | TECallFun(i, f) -> i ^ "(" ^ (csl expr_string f) ^ ")" + | TECallFun(i, f, _) -> i ^ "(" ^ (csl expr_string (List.map fst f)) ^ ")" (* ici, le second ast a changé par rapport au premier *) | TEUnary(e, f) -> (unop_str e) ^ (expr_string f) | TEBinary(e1, o, e2) -> "(" ^ (expr_string e1) ^ " " ^ (binop_str o) ^ " " ^ (expr_string e2) ^ ")" | TEMember(e1, i) -> "(" ^ (expr_string e1) ^ ")@" ^ (string_of_int i) + | TEPointerCast(e1, i) -> "(" ^ (expr_string e1) ^ ")+" ^ (string_of_int i) | TENew(c, proto, arg) -> "new " ^ c.tc_name - ^ (match proto with | None -> "" | Some p -> " ." ^ p.tp_unique_ident) - ^ " (" ^ (csl expr_string arg) ^ ")" - | TECallVirtual(exp, pos1, pos2, args) -> - "(" ^ (expr_string exp) ^ ")@" ^ (string_of_int pos1) ^ "#" ^ (string_of_int pos2) ^ "(" ^ (csl expr_string args) ^ ")" + ^ " ." ^ proto + ^ " (" ^ (csl expr_string (List.map fst arg)) ^ ")" + | TECallVirtual(exp, pos2, args, _) -> + "(" ^ (expr_string exp) ^ ")#" ^ (string_of_int pos2) ^ "(" ^ (csl expr_string (List.map fst args)) ^ ")" let rec print_stmt l x = for i = 1 to l do print_string " " done; - match x.ts_desc with + match x with | TSEmpty -> print_string ";\n" | TSExpr(e) -> print_string ((expr_string e) ^ "\n") | TSIf(e, a, b) -> print_string ("if " ^ (expr_string e) ^ "\n"); @@ -74,13 +75,11 @@ let rec print_stmt l x = | TSBlock(b) -> print_block l b | TSReturn(None) -> print_string "return\n" | TSReturn(Some k) -> print_string ("return " ^ (expr_string k) ^ "\n") - | TSDeclare((ty,b), i) -> let addr = (if b then "&" else "") in - print_string (addr ^ i ^ " : " ^ (var_type_str ty) ^ "\n") + | TSDeclare(ty, i) -> print_string (i ^ " : " ^ (var_type_str ty) ^ "\n") | TSDeclareAssignExpr((ty,b), i, e) -> let addr = (if b then "&" else "") in print_string (addr ^ i ^ " : " ^ (var_type_str ty) ^ " = " ^ (expr_string e) ^ "\n") - | TSDeclareAssignConstructor(t, i, _, c, a) -> () (*print_string - (i ^ " : " ^ (var_type_str t) ^ " = " ^ c ^ "(" ^ - (csl expr_string a) ^ ")\n")*) + | TSDeclareAssignConstructor(cls, i, c, a) -> + print_string (i ^ " : " ^ cls.tc_name ^ " = ." ^ c ^ " (" ^(csl expr_string (List.map fst a)) ^ ")\n") | TSWriteCout(k) -> print_string ("std::cout" ^ (List.fold_left (fun x k -> x ^ " << " ^ (match k with | TSEExpr e -> expr_string e @@ -104,7 +103,7 @@ let proto_str p = p.tp_args) ^ ") : " ^ (match p.tp_ret_type with | Some (ty,b) -> var_type_str ty | None -> "constructor") ^ " ." ^ p.tp_unique_ident - ^ (match p.tp_virtual with | None -> "" | Some (k, l) -> " @" ^ (string_of_int k) ^ "#" ^ (string_of_int l)) + ^ (match p.tp_virtual with | None -> "" | Some (k, l) -> " @" ^ (string_of_int k.h_pos) ^ "#" ^ (string_of_int l)) let print_class_decl c = print_string ("class " ^ c.tc_name ^ " (size : " ^ (string_of_int c.tc_size) ^ ") {\n"); diff --git a/src/test.sh b/src/test.sh index 090dc6f..eaec94a 100755 --- a/src/test.sh +++ b/src/test.sh @@ -4,58 +4,75 @@ echo "Testing SYNTAX/" for a in ../tests/syntax/good/*.cpp; do - if ./minic++ --parse-only $a; - then echo "OK $a"; - else echo "FAIL $a"; - fi; + if ./minic++ --parse-only $a; + then echo "OK $a"; + else echo "FAIL $a"; + fi; done; for a in ../tests/syntax/bad/*.cpp; do - if ./minic++ --parse-only $a 2> /dev/null; - then echo "FAIL $a"; - else echo "OK $a"; - fi; + if ./minic++ --parse-only $a 2> /dev/null; + then echo "FAIL $a"; + else echo "OK $a"; + fi; done; echo "---" echo "Testing TYPING/ only against parsing" for a in ../tests/typing/*/*.cpp; do - if ./minic++ --parse-only $a; - then echo "OK $a"; - else echo "FAIL $a"; - fi; + if ./minic++ --parse-only $a; + then echo "OK $a"; + else echo "FAIL $a"; + fi; done; echo "---" echo "Testing EXEC/ only against parsing" for a in ../tests/exec/*.cpp; do - if ./minic++ --parse-only $a; - then echo "OK $a"; - else echo "FAIL $a"; - fi; + if ./minic++ --parse-only $a; + then echo "OK $a"; + else echo "FAIL $a"; + fi; done; echo "---" echo "Testing TYPING/" for a in ../tests/typing/good/*.cpp; do - if ./minic++ --type-only $a; - then echo "OK $a"; - else echo "FAIL $a"; - fi; + if ./minic++ --type-only $a; + then echo "OK $a"; + else echo "FAIL $a"; + fi; done; for a in ../tests/typing/bad/*.cpp; do - if ./minic++ --type-only $a 2> /dev/null; - then echo "FAIL $a"; - else echo "OK $a"; - fi; + if ./minic++ --type-only $a 2> /dev/null; + then echo "FAIL $a"; + else echo "OK $a"; + fi; done; echo "---" echo "Testing EXEC/ only against typing" for a in ../tests/exec/*.cpp; do - if ./minic++ --type-only $a; - then echo "OK $a"; - else echo "FAIL $a"; - fi; + if ./minic++ --type-only $a; + then echo "OK $a"; + else echo "FAIL $a"; + fi; done; + +echo "---" +echo "Testing EXEC/" +for a in ../tests/exec/*.cpp; do + if ./minic++ $a; + then + mars-mips nc se1 ../tests/exec/`basename -s .cpp $a`.s > /tmp/mips_out.txt + if diff -B /tmp/mips_out.txt ../tests/exec/`basename -s .cpp $a`.out > /dev/null + then echo "OK $a" + else echo "FAIL $a" + fi + else echo "TODO $a"; + fi; +done; + + + diff --git a/src/typing.ml b/src/typing.ml index 6b1c801..966057c 100644 --- a/src/typing.ml +++ b/src/typing.ml @@ -48,25 +48,25 @@ and texpr_desc = | TEThis | TEIdent of ident | TEAssign of texpression * texpression - | TECallFun of ident * texpression list (* changé : te -> ident *) + | TECallFun of ident * (texpression * bool) list * bool (* changé : te -> ident *) (* calls to non-virtual methods are compiled using TECallFun, with the object cons'ed at the begining of the arguments expression list *) - | TECallVirtual of texpression * int * int * texpression list (* object * index in vtable * arguments *) + (* for each argument, bool is is argument passed by reference ? *) + (* final bool : is returned value a reference ? *) + | TECallVirtual of texpression * int * (texpression * bool) list * bool + (* object * index in vtable * arguments * is return value a reference? *) | TEUnary of unop * texpression | TEBinary of texpression * binop * texpression | TEMember of texpression * int (* object * position of member *) - | TENew of tcls * tproto option * texpression list + | TEPointerCast of texpression * int (* object * position of member *) + | TENew of tcls * ident * (texpression * bool) list and tstr_expression = | TSEExpr of texpression | TSEStr of string -and tstatement = { - ts_loc: loc; - ts_desc: ts_desc; - } -and ts_desc = +and tstatement = | TSEmpty | TSExpr of texpression | TSIf of texpression * tstatement * tstatement @@ -74,18 +74,15 @@ and ts_desc = | TSFor of texpression list * texpression option * texpression list * tstatement | TSBlock of tblock | TSReturn of texpression option - | TSDeclare of type_ref * ident + | TSDeclare of typ * ident | TSDeclareAssignExpr of type_ref * ident * texpression - | TSDeclareAssignConstructor of typ * ident * tproto option * tident * texpression list (* a faire *) -(* Type of variable, variable name, constructor class name, constructor arguments *) + | TSDeclareAssignConstructor of tcls * ident * ident * (texpression * bool) list +(* Class name of variable, variable name, constructor name, constructor arguments *) | TSWriteCout of tstr_expression list and tblock = tstatement list and tproto = { - tp_virtual : (int * int) option; (* only used for class methods ; if none then not virtual, - if some then gives the index of the method in the vtable (same for all classes - of the hierarchy that have that method) *) - tp_loc : loc; + tp_virtual : (tcls_hier * int) option; (* only used for class methods ; if none then not virtual *) tp_name : ident; tp_unique_ident : ident; (* label de la fonction dans le code assembleur *) tp_class : tident option; (* p_class = none : standalone function *) @@ -96,7 +93,7 @@ and tproto = { and tcls_supers = tcls_hier list and tcls_hier = { h_class : tident; - h_pos : int; + mutable h_pos : int; mutable h_vtable : (int * tproto) list; (* only to be muted during class definition parsing *) h_supers : tcls_supers } @@ -136,6 +133,7 @@ type tdeclaration = type tprogram = { prog_decls : tdeclaration list; prog_env : env; + prog_main : ident; } (* Quelques fonctions utiles : *) @@ -182,10 +180,45 @@ let rec subtype env a b = match a, b with let c = get_c env i in let rec find_in_hier h = h.h_class = j || - (List.exists find_in_hier h.h_supers) + (List.length (List.filter find_in_hier h.h_supers) = 1) in find_in_hier c.tc_hier | _ -> false +let relative_class_position env i j = + let c = get_c env i in + let rec find_in_hier h = + h.h_class = j || + (List.length (List.filter find_in_hier h.h_supers) = 1) + and get_in_hier h = + if h.h_class = j + then h.h_pos + else match List.filter find_in_hier h.h_supers with + | [a] -> get_in_hier a + | _ -> assert false + in get_in_hier c.tc_hier + +let rec upcast env exp dt = (* présupposé : exp.type_expr <= dt *) + match exp.type_expr, dt with + | (T_Int, _, _), T_Int -> exp + | (T_Void, _, _), T_Void -> exp + | (Typenull, _, _), TPoint(_) -> exp + | (TClass(i), a, b), TClass(j) when a||b -> + begin match relative_class_position env i j with + | 0 -> exp + | pos -> + { type_expr = (TClass(j), false, true); te_loc = exp.te_loc; + te_desc = TEMember(exp, pos) } + end + | (TPoint(TClass(i)), a, b), TPoint(TClass(j)) -> + begin match relative_class_position env i j with + | 0 -> exp + | pos -> + { type_expr = (TPoint(TClass(j)), false, true); te_loc = exp.te_loc; + te_desc = TEPointerCast(exp, pos) } + end + | (TPoint(ka), _, _), TPoint(kb) -> exp + | _ -> assert false + let type_size env t = match t with | T_Int | Typenull | TPoint(_) -> 4 | T_Void -> 0 @@ -290,7 +323,7 @@ and compute_type env e = ty_assert (num ty1) "Cannot assign to non-numeric type (pointer type is numeric)"; ty_assert (subtype env.b_pe ty2 ty1) "Incompatible types in assign"; (* type num et ref compatibles ?*) - (TEAssign (te1,te2) ),(ty1,false,false) + (TEAssign (te1,upcast env.b_pe te2 ty1) ),(ty1,false,false) | EUnary (op,e) -> let te,(ty,b1,b2) = get_expr0 env e in (match op with | PreIncr | PostIncr | PreDecr | PostDecr -> @@ -315,14 +348,17 @@ and compute_type env e = | Equal | NotEqual -> ty_assert ((subtype env.b_pe ty1 ty2) || (subtype env.b_pe ty2 ty1)) "Can only apply == or != to two values of compatible type"; - ty_assert (num ty1) "Can only apply == or != to pointers" + ty_assert (num ty1) "Can only apply == or != to pointers"; + let te1 = if subtype env.b_pe ty1 ty2 then upcast env.b_pe te1 ty2 else te1 in + let te2 = if subtype env.b_pe ty2 ty1 then upcast env.b_pe te2 ty1 else te2 in + TEBinary(te1,op,te2),(T_Int,false,false) | Lt | Le | Gt | Ge | Add | Sub | Mul | Div | Modulo | Land | Lor -> ty_assert (ty1 = T_Int) "Left operand of binop is not integer"; - ty_assert (ty2 = T_Int) "Right operand of binop is not integer" - ); (* vérifs *) - TEBinary(te1,op,te2),(T_Int,false,false) + ty_assert (ty2 = T_Int) "Right operand of binop is not integer"; + TEBinary(te1,op,te2),(T_Int,false,false) + ) | ECall (e,e_list) -> (* TODO : look also within parent classes *) let args_values = List.map (get_expr0 env) e_list in @@ -334,8 +370,12 @@ and compute_type env e = begin match env.b_class with | None -> None, closest_proto env.b_pe args_types funs | Some k -> - begin try Some e_this_not_ptr, - closest_proto env.b_pe args_types (find_protos_in_class env.b_pe k.tc_name i) + begin try + let proto = closest_proto env.b_pe args_types (find_protos_in_class env.b_pe k.tc_name i) in + let upcasted = if proto.tp_virtual = None then e_this_not_ptr + else upcast env.b_pe e_this_not_ptr + (TClass (match proto.tp_class with | None -> assert false | Some k -> k)) in + Some upcasted, proto with NoCorrespondingPrototype -> None, closest_proto env.b_pe args_types funs end @@ -344,21 +384,26 @@ and compute_type env e = let e = type_expr env e in begin match e.type_expr with | TClass(k), a, b when a || b -> - Some e, closest_proto env.b_pe args_types (find_protos_in_class env.b_pe k i) + let proto = closest_proto env.b_pe args_types (find_protos_in_class env.b_pe k i) in + let upcasted = if proto.tp_virtual = None then e + else upcast env.b_pe e + (TClass (match proto.tp_class with | None -> assert false | Some k -> k)) in + Some upcasted, proto | _ -> ty_error "Invalid argument type for method call (not a class, or not a lvalue)" end | _ -> ty_error "Calling something that is neither a function nor a method") in let l_te = List.map fst args_values in + let l_te = List.map2 (fun k ((ty, r), _) -> upcast env.b_pe k ty, r) l_te tproto.tp_args in let ty,b = match tproto.tp_ret_type with | None -> ty_error "Constructor cannot be called as function" | Some (ty,b) -> ty,b in begin match tproto.tp_virtual, obj with | None, None -> - TECallFun(tproto.tp_unique_ident,l_te),(ty,b,false) + TECallFun(tproto.tp_unique_ident,l_te,b),(ty,b,false) | None, Some(obj)-> - TECallFun(tproto.tp_unique_ident,obj::l_te),(ty,b,false) - | Some(idx), Some(obj) -> - TECallVirtual(obj, fst idx, snd idx, l_te),(ty,b,false) + TECallFun(tproto.tp_unique_ident,(obj, true)::l_te,b),(ty,b,false) + | Some(hier, idx), Some(obj) -> + TECallVirtual(upcast env.b_pe obj (TClass hier.h_class), idx, l_te,b),(ty,b,false) | _ -> ty_error "(should not happen) Virtual function applied to no object..." end | EMember (e, id) -> @@ -377,14 +422,13 @@ and compute_type env e = let args_types = List.map (fun (e, (t, r, l)) -> t, r||l) args_values in let candidates = List.filter (fun p -> p.tp_ret_type = None) c.tc_methods in begin match candidates with - | [] -> - ty_assert (args = []) "Only default constructor exists and it has 0 arguments"; - TENew(c, None, []), (TPoint(TClass(cls_name)), false, false) + | [] -> assert false (* default constructor should always be in list *) | _ -> let p = closest_proto env.b_pe args_types candidates in (* closest_proto makes sure the prototypes match, no problem here *) let l_te = List.map fst args_values in - TENew(c, Some p, l_te), (TPoint(TClass(cls_name)), false, false) + let l_te = List.map2 (fun k ((ty, r), _) -> upcast env.b_pe k ty, r) l_te p.tp_args in + TENew(c, p.tp_unique_ident, l_te), (TPoint(TClass(cls_name)), false, false) end | EThis -> begin match env.b_class with @@ -396,9 +440,7 @@ and compute_type env e = (* Statements *) let rec type_stm ret_type env s = - err_add_loc s.s_loc (fun () -> - let d, ty = compute_type_stm ret_type env s in - { ts_loc = s.s_loc; ts_desc = d }, ty) + err_add_loc s.s_loc (fun () -> compute_type_stm ret_type env s) and compute_type_stm ret_type env s = match s.s_desc with (* statement -> ts_desc,stm_type *) | SEmpty -> TSEmpty,env @@ -438,12 +480,13 @@ and compute_type_stm ret_type env s = match s.s_desc with (* statement -> ts_des (* pq while n'est pas dans les règles données ? *) | SDeclare(vt,i) -> let ty,b = build_type_or_ref vt in ty_assert (bf env.b_pe ty) "Malformed type"; + ty_assert (not b) "Reference must be assigned at declaration"; ty_assert (not (Smap.mem i env.b_locals) ) "Variable redefinition"; let env0 = { b_pe = env.b_pe; b_locals = Smap.add i (ty,b) env.b_locals; b_class = env.b_class } in - TSDeclare( (ty,b) ,i) , env0 + TSDeclare( ty ,i) , env0 | SDeclareAssignExpr(vt,i,e) -> let ty,b = build_type_or_ref vt in ty_assert (bf env.b_pe ty) "Malformed type"; ty_assert (not (Smap.mem i env.b_locals)) "Variable redefinition"; @@ -454,7 +497,7 @@ and compute_type_stm ret_type env s = match s.s_desc with (* statement -> ts_des { b_pe = env.b_pe; b_locals = Smap.add i (ty,b) env.b_locals; b_class = env.b_class } in - TSDeclareAssignExpr( (ty,b) ,i,te) , env0 + TSDeclareAssignExpr( (ty,b) ,i,upcast env.b_pe te ty) , env0 | SDeclareAssignConstructor(vt,i,ti,e_l) -> let ty, b = build_type_or_ref vt in ty_assert (bf env.b_pe ty) "Malformed type"; @@ -466,18 +509,17 @@ and compute_type_stm ret_type env s = match s.s_desc with (* statement -> ts_des let args_types = List.map (fun (e, (t, r, l)) -> t, r||l) args_values in let candidates = List.filter (fun p -> p.tp_ret_type = None) c.tc_methods in begin match candidates with - | [] -> - ty_assert (e_l = []) "Only default constructor exists and it has 0 arguments"; - TSDeclareAssignConstructor(ty, i, None, ti, []), env + | [] -> assert false (* ... *) | _ -> let p = closest_proto env.b_pe args_types candidates in (* closest_proto makes sure the prototypes match, no problem here *) let l_te = List.map fst args_values in + let l_te = List.map2 (fun k ((ty, r), _) -> upcast env.b_pe k ty, r) l_te p.tp_args in let env0 = { b_pe = env.b_pe; b_locals = Smap.add i (ty,b) env.b_locals; b_class = env.b_class } in - TSDeclareAssignConstructor(ty, i, Some p, ti, l_te), env0 + TSDeclareAssignConstructor(c, i, p.tp_unique_ident, l_te), env0 end | SWriteCout(str_e_list) -> let args = @@ -536,7 +578,6 @@ let get_fun env p b = (* p : proto b : block -> tp, tb, env2*) (* Add to env *) let tproto = { - tp_loc = p.p_loc ; tp_name = name ; tp_unique_ident = name ^ (tproto_unique_number()); tp_class = None ; @@ -633,22 +674,19 @@ let compute_tclass env c = | None -> List.fold_left (fun f (i, p) -> if (p.tp_name = proto.p_name && (List.map fst p.tp_args) = (List.map fst args)) - then Some (i, s) + then Some (s, i) else f) None s.h_vtable in let super = match check_in_super hier with | None -> if virt then (* allocate new spot in vtable of this object *) - Some (List.fold_left (fun n (x, _) -> max n (x+4)) 0 hier.h_vtable, hier) + Some (hier, List.fold_left (fun n (x, _) -> max n (x+4)) 0 hier.h_vtable) else None | Some k -> Some k in (* Build proto *) let tproto = - { tp_virtual = (match super with - | Some(i, c) -> Some(c.h_pos, i) - | None -> None); - tp_loc = proto.p_loc; + { tp_virtual = super; tp_name = proto.p_name; tp_unique_ident = proto.p_name ^ (tproto_unique_number()) ; tp_class = Some(cls_name); @@ -658,12 +696,36 @@ let compute_tclass env c = (* Add to vtable *) begin match super with | None -> () - | Some (i, c) -> + | Some (c, i) -> c.h_vtable <- (i, tproto)::(List.remove_assoc i c.h_vtable) end; tproto) in (mem, mem_u), m::meth ) ((Smap.empty, used), []) c.c_members in + (* make sure class has default constructor *) + let meth = + if List.exists (fun p -> p.tp_ret_type = None && p.tp_name = cls_name) meth + then meth + else + { tp_virtual = None; + tp_name = cls_name; + tp_unique_ident = cls_name ^ "0"; + tp_class = Some cls_name; + tp_ret_type = None; + tp_args = [] }::meth + in + (* if vtable is empty, remove it *) + let mem, mem_u = + if hier.h_vtable = [] then + let rec mv_h h = + h.h_pos <- h.h_pos - 4; + List.iter mv_h h.h_supers + in + List.iter mv_h hier.h_supers; + Smap.map (fun (ty, pos) -> (ty, pos-4)) mem, mem_u - 4 + else + mem, mem_u + in { tc_name = cls_name; tc_size = mem_u; tc_hier = hier; @@ -739,11 +801,12 @@ let prog p = ([],{ e_globals = Smap.empty; e_funs = []; e_classes = Smap.empty }) p ) in - ty_assert (List.exists + let p = try List.find (fun tp -> tp.tp_class = None && tp.tp_name = "main" && tp.tp_args = [] && tp.tp_ret_type = Some (T_Int,false)) - env.e_funs) "No 'int main()' function defined in program..."; - { prog_decls = List.rev decls; prog_env = env } + env.e_funs + with Not_found -> ty_error "No correct main function in program." in + { prog_decls = List.rev decls; prog_env = env; prog_main = p.tp_unique_ident } diff --git a/tests/exec/args1.cpp b/tests/exec/args1.cpp new file mode 100644 index 0000000..52edd82 --- /dev/null +++ b/tests/exec/args1.cpp @@ -0,0 +1,13 @@ +#include <iostream> + +int f(int a, int b, int c, int d, int e, int f) { + std::cout << "a = " << a << ", b = " << b << + ", c = " << c << ", d = " << d << ", e = " << + e << ", f = " << f << "\n"; + return a + b + c + d + e + f; +} + +int main() { + int x = f(1, 2, 3, 4, 5, 6); + std::cout << "x = " << x << "\n"; +} diff --git a/tests/exec/args1.out b/tests/exec/args1.out new file mode 100644 index 0000000..34f8b2d --- /dev/null +++ b/tests/exec/args1.out @@ -0,0 +1,2 @@ +a = 1, b = 2, c = 3, d = 4, e = 5, f = 6 +x = 21 diff --git a/tests/exec/args2.cpp b/tests/exec/args2.cpp new file mode 100644 index 0000000..1d2efee --- /dev/null +++ b/tests/exec/args2.cpp @@ -0,0 +1,15 @@ +#include <iostream> + +int test(int a) { + int *b = &a; + *b = *b + 42; + return a; +} + +int main() { + int x = 0; + int y = test(x); + std::cout << "x = " << x << "\n"; + std::cout << "y = " << y << "\n"; +} + diff --git a/tests/exec/args2.out b/tests/exec/args2.out new file mode 100644 index 0000000..7921196 --- /dev/null +++ b/tests/exec/args2.out @@ -0,0 +1,2 @@ +x = 0 +y = 42 |