From a724f260cf9bf1dc919f597b2ff272b67e39b933 Mon Sep 17 00:00:00 2001 From: Valentin Perrelle <valentin.perrelle@cea.fr> Date: Wed, 28 Sep 2022 20:19:03 +0200 Subject: [PATCH] [Cil builder] Implements some statements generation for Pure builder --- .../ast_building/cil_builder.ml | 429 +++++++++++++----- .../ast_building/cil_builder.mli | 27 +- tests/misc/cil_builder.i | 8 + tests/misc/cil_builder.ml | 21 + tests/misc/oracle/cil_builder.res.oracle | 10 + 5 files changed, 365 insertions(+), 130 deletions(-) create mode 100644 tests/misc/cil_builder.i create mode 100644 tests/misc/cil_builder.ml create mode 100644 tests/misc/oracle/cil_builder.res.oracle diff --git a/src/kernel_services/ast_building/cil_builder.ml b/src/kernel_services/ast_building/cil_builder.ml index c5fb0d3fd01..6bb2b495ebd 100644 --- a/src/kernel_services/ast_building/cil_builder.ml +++ b/src/kernel_services/ast_building/cil_builder.ml @@ -112,6 +112,34 @@ struct end +(* --- Variables scoping --- *) + +module Scope = +struct + type id = int64 + + let new_id : unit -> id = + let last_id = ref Int64.zero in + fun () -> + last_id := Int64.(add !last_id one); + !last_id + + module IdMap = Map.Make (Int64) + + type t = Cil_types.varinfo IdMap.t + + exception OutOfScope of string + + let empty : t = IdMap.empty + let add : id -> Cil_types.varinfo -> t -> t = IdMap.add + + let resolve (scope : t) (id : id) (name : string) : Cil_types.varinfo = + match IdMap.find_opt id scope with + | Some vi -> vi + | None -> raise (OutOfScope name) +end + + (* --- C & Logic expressions builder --- *) module Exp = @@ -135,7 +163,8 @@ struct | Integer of Integer.t | CilConstant of Cil_types.constant and var' = - Cil_types.varinfo + | CilVar of Cil_types.varinfo + | NewVar of Scope.id * string * Cil_types.typ and lval' = | CilLval of Cil_types.lval | Var of var' @@ -183,8 +212,9 @@ struct | Int i -> Format.pp_print_int fmt i | Integer i -> Integer.pretty fmt i | CilConstant c -> Printer.pp_constant fmt c - and pretty_var fmt v = - Printer.pp_varinfo fmt v + and pretty_var fmt = function + | CilVar vi -> Printer.pp_varinfo fmt vi + | NewVar (_id,name,_typ) -> Format.pp_print_string fmt name and pretty_lval fmt = function | CilLval lv -> Printer.pp_lval fmt lv | Var v -> pretty_var fmt v @@ -305,7 +335,7 @@ struct (* Lvalues *) - let var v = `var v + let var v = `var (CilVar v) let of_lval lv = `lval (CilLval lv) (* Expressions *) @@ -407,6 +437,7 @@ struct exception NotAPredicate of exp exception NotAFunction of Cil_types.logic_info exception Typing_error of string + exception OutOfScope = Scope.OutOfScope let typing_error s = raise (Typing_error s) @@ -423,16 +454,20 @@ struct | Int i -> build_constant (Integer (Integer.of_int i)) | Integer i -> Cil_types.(CInt64 (i, IInt, None)) - and build_lval ~loc = function + and build_var ~scope = function + | CilVar vi -> vi + | NewVar (vid, name,_typ) -> Scope.resolve scope vid name + + and build_lval ~scope ~loc = function | Result as lv -> raise (LogicInC (`lval lv)) | CilLval lval -> lval - | Var v -> Cil_types.(Var v, NoOffset) + | Var v -> Cil_types.(Var (build_var ~scope v), NoOffset) | Mem e -> - let e' = build_exp ~loc e in + let e' = build_exp ~scope ~loc e in Cil.mkMem ~addr:e' ~off:Cil_types.NoOffset | Index (lv, e) -> - let (host, offset) as lv' = build_lval ~loc lv - and e' = build_exp ~loc e in + let (host, offset) as lv' = build_lval ~scope ~loc lv + and e' = build_exp ~scope ~loc e in begin match Cil.(unrollType (typeOfLval lv')) with | TArray _ -> let offset' = Cil_types.Index (e', NoOffset) in @@ -445,7 +480,7 @@ struct or a pointer" end | (Field (lv,_) | FieldNamed (lv,_)) as e -> - let (host, offset) as lv' = build_lval ~loc lv in + let (host, offset) as lv' = build_lval ~scope ~loc lv in let host', offset', ci = match Cil.(unrollTypeDeep (typeOfLval lv')) with | TComp (ci,_) -> host, offset, ci | TPtr (TComp (ci,_),_) -> @@ -461,16 +496,16 @@ struct let offset'' = Cil_types.(Field (f, NoOffset)) in host', Cil.addOffset offset'' offset' - and build_exp ~loc = function + and build_exp ~scope ~loc = function | CilTerm _ | Range _ | App _ | Pred _ as e -> raise (LogicInC (`exp e)) | CilExp exp -> exp | CilExpCopy exp -> Cil.copy_exp exp | Const c-> Cil.new_exp ~loc (Cil_types.Const (build_constant c)) | Lval lval -> - Cil.new_exp ~loc (Cil_types.Lval (build_lval ~loc lval)) + Cil.new_exp ~loc (Cil_types.Lval (build_lval ~scope ~loc lval)) | Unop (op,e) -> - let e' = build_exp ~loc e in + let e' = build_exp ~scope ~loc e in let oldt = Cil.typeOf e' in let newt = Cil.integralPromotion oldt in Cil.(new_exp ~loc (Cil_types.UnOp (op, mkCastT ~oldt ~newt e', oldt))) @@ -478,8 +513,8 @@ struct let is_pointer_type e = Cil.(isPointerType (typeOf e)) in - let e1' = build_exp ~loc e1 - and e2' = build_exp ~loc e2 in + let e1' = build_exp ~scope ~loc e1 + and e2' = build_exp ~scope ~loc e2 in let op' = match op with (* Normalize operation *) | PlusA when is_pointer_type e1' -> Cil_types.PlusPI | MinusA when is_pointer_type e1' -> Cil_types.MinusPI @@ -489,22 +524,23 @@ struct in Cil.mkBinOp ~loc op' e1' e2' | Cast (Cil_types.Ctype newt, e) -> - Cil.mkCast ~force:false ~newt (build_exp ~loc e) + Cil.mkCast ~force:false ~newt (build_exp ~scope ~loc e) | Cast _ -> raise NotACType | Addr lv -> - Cil.mkAddrOrStartOf ~loc (build_lval ~loc lv) + Cil.mkAddrOrStartOf ~loc (build_lval ~scope ~loc lv) - let rec build_term_lval ~loc ~restyp = function + let rec build_term_lval ~scope ~loc ~restyp = function | Result -> Cil_types.(TResult (Option.get restyp), TNoOffset) | CilLval _ as lv -> raise (CInLogic (`lval lv)) - | Var v -> Cil_types.(TVar (Cil.cvar_to_lvar v), TNoOffset) + | Var v -> + Cil_types.(TVar (Cil.cvar_to_lvar (build_var ~scope v)), TNoOffset) | Mem t -> - let t' = build_term ~loc ~restyp t in + let t' = build_term ~scope ~loc ~restyp t in Cil_types.(TMem t', TNoOffset) | Index (tlv, t) -> - let (host, offset) as tlv' = build_term_lval ~loc ~restyp tlv - and t' = build_term ~loc ~restyp t in + let (host, offset) as tlv' = build_term_lval ~scope ~loc ~restyp tlv + and t' = build_term ~scope ~loc ~restyp t in let lty = Cil.typeOfTermLval tlv' in begin match Logic_utils.unroll_type lty with | Ctype (TArray _) -> @@ -518,7 +554,7 @@ struct array or a C pointer" end | (Field (tlv,_) | FieldNamed (tlv,_)) as t -> - let (host, offset) as tlv' = build_term_lval ~loc ~restyp tlv in + let (host, offset) as tlv' = build_term_lval ~scope ~loc ~restyp tlv in let lty = match Logic_utils.unroll_type (Cil.typeOfTermLval tlv') with | Ctype cty -> Cil_types.Ctype (Cil.unrollTypeDeep cty) | lty -> lty @@ -538,7 +574,7 @@ struct let offset'' = Cil_types.(TField (f, TNoOffset)) in host', Logic_const.addTermOffset offset'' offset' - and build_term ~loc ~restyp = function + and build_term ~scope ~loc ~restyp = function | Const (CilConstant _) | CilExp _ | CilExpCopy _ as e -> raise (CInLogic (`exp e)) | Pred _ as e -> @@ -549,16 +585,16 @@ struct | Const (Integer i) -> Logic_const.tint ~loc i | Lval lval -> - let tlval = build_term_lval ~loc ~restyp lval in + let tlval = build_term_lval ~scope ~loc ~restyp lval in Logic_const.term ~loc Cil_types.(TLval tlval) (Cil.typeOfTermLval tlval) | Unop (op,t) -> - let t' = build_term t ~loc ~restyp in + let t' = build_term t ~scope ~loc ~restyp in let ty = t'.Cil_types.term_type in (* TODO: type conversion *) Logic_const.term ~loc Cil_types.(TUnOp (op,t')) ty | Binop (op,t1,t2) -> - let t1' = build_term ~loc ~restyp t1 - and t2' = build_term ~loc ~restyp t2 in + let t1' = build_term ~scope ~loc ~restyp t1 + and t2' = build_term ~scope ~loc ~restyp t2 in let ty = t1'.Cil_types.term_type in let op' = match op with (* Normalize operation *) | PlusA when Logic_utils.isLogicPointer t1' -> Cil_types.PlusPI @@ -570,25 +606,25 @@ struct (* TODO: type conversion *) Logic_const.term ~loc Cil_types.(TBinOp (op',t1',t2')) ty | Cast (Ctype ct, t) -> - let t' = build_term ~loc ~restyp t in + let t' = build_term ~scope ~loc ~restyp t in Logic_utils.mk_cast ~loc ct t' | Cast (ty, t) -> - let t' = build_term ~loc ~restyp t in + let t' = build_term ~scope ~loc ~restyp t in Logic_utils.numeric_coerce ty t' | Addr lval -> - let tlval = build_term_lval ~loc ~restyp lval in + let tlval = build_term_lval ~scope ~loc ~restyp lval in let ty = Cil.typeOfTermLval tlval in Logic_utils.mk_logic_AddrOf ~loc tlval ty | Range (t1,t2) -> - let t1' = Option.map (build_term ~loc ~restyp) t1 - and t2' = Option.map (build_term ~loc ~restyp) t2 in + let t1' = Option.map (build_term ~scope ~loc ~restyp) t1 + and t2' = Option.map (build_term ~scope ~loc ~restyp) t2 in Logic_const.trange ~loc (t1',t2') | App (logic_info, labels, args) -> let ty = match logic_info.l_type with | None -> raise (NotAFunction logic_info) | Some ty -> ty in - let args' = List.map (build_term ~loc ~restyp) args in + let args' = List.map (build_term ~scope ~loc ~restyp) args in Logic_const.term ~loc (Tapp (logic_info, labels, args')) ty and build_relation e = function @@ -600,83 +636,91 @@ struct | Cil_types.Ne -> Cil_types.Rneq | _ -> raise (NotAPredicate (`exp e)) - and build_pred_node ~loc ~restyp = function + and build_pred_node ~scope ~loc ~restyp = function | Unop (Cil_types.LNot, p) -> - let p' = build_pred ~loc ~restyp p in + let p' = build_pred ~scope ~loc ~restyp p in Cil_types.Pnot p' | Binop (Cil_types.LAnd, p1, p2) -> - let p1' = build_pred ~loc ~restyp p1 - and p2' = build_pred ~loc ~restyp p2 in + let p1' = build_pred ~scope ~loc ~restyp p1 + and p2' = build_pred ~scope ~loc ~restyp p2 in Cil_types.Pand (p1',p2') | Binop (Cil_types.LOr, p1, p2) -> - let p1' = build_pred ~loc ~restyp p1 - and p2' = build_pred ~loc ~restyp p2 in + let p1' = build_pred ~scope ~loc ~restyp p1 + and p2' = build_pred ~scope ~loc ~restyp p2 in Cil_types.Por (p1',p2') | Binop (binop, t1, t2) as e -> let rel = build_relation e binop - and t1' = build_term ~loc ~restyp t1 - and t2' = build_term ~loc ~restyp t2 in + and t1' = build_term ~scope ~loc ~restyp t1 + and t2' = build_term ~scope ~loc ~restyp t2 in Cil_types.Prel (rel, t1', t2') | Const _ | CilExp _ | CilExpCopy _ | CilTerm _ | Lval _ | Unop _ | Cast _ | Addr _ | Range _ as e -> raise (NotAPredicate (`exp e)) | App (logic_info, labels, args) -> - let args' = List.map (build_term ~loc ~restyp) args in + let args' = List.map (build_term ~scope ~loc ~restyp) args in Cil_types.Papp (logic_info, labels, args') | Pred (ObjectPointer (l, t)) -> - Cil_types.Pobject_pointer (l, build_term ~loc ~restyp t) + Cil_types.Pobject_pointer (l, build_term ~scope ~loc ~restyp t) | Pred (Valid (l, t)) -> - Cil_types.Pvalid (l, build_term ~loc ~restyp t) + Cil_types.Pvalid (l, build_term ~scope ~loc ~restyp t) | Pred (ValidRead (l, t)) -> - Cil_types.Pvalid_read (l, build_term ~loc ~restyp t) + Cil_types.Pvalid_read (l, build_term ~scope ~loc ~restyp t) | Pred (Initialized (l, t)) -> - Cil_types.Pinitialized (l, build_term ~loc ~restyp t) + Cil_types.Pinitialized (l, build_term ~scope ~loc ~restyp t) | Pred (Dangling (l, t)) -> - Cil_types.Pdangling (l, build_term ~loc ~restyp t) + Cil_types.Pdangling (l, build_term ~scope ~loc ~restyp t) | Pred (Allocable (l, t)) -> - Cil_types.Pallocable (l, build_term ~loc ~restyp t) + Cil_types.Pallocable (l, build_term ~scope ~loc ~restyp t) | Pred (Freeable (l, t)) -> - Cil_types.Pfreeable (l, build_term ~loc ~restyp t) + Cil_types.Pfreeable (l, build_term ~scope ~loc ~restyp t) | Pred (Fresh (l1, l2, t1, t2)) -> - let t1' = build_term ~loc ~restyp t1 - and t2' = build_term ~loc ~restyp t2 in + let t1' = build_term ~scope ~loc ~restyp t1 + and t2' = build_term ~scope ~loc ~restyp t2 in Cil_types.Pfresh (l1, l2, t1', t2') - and build_pred ~loc ~restyp t = - Logic_const.unamed ~loc (build_pred_node ~loc ~restyp t) + and build_pred ~scope ~loc ~restyp t = + Logic_const.unamed ~loc (build_pred_node ~scope ~loc ~restyp t) - let rec build_init ~loc = function + let rec build_init ~scope ~loc = function | CilInit init -> init | SingleInit e -> - Cil_types.SingleInit (build_exp ~loc e) + Cil_types.SingleInit (build_exp ~scope ~loc e) | CompoundInit (typ,l) -> let index i = Cil_types.(Index (Cil.integer ~loc i, NoOffset)) in - let initl = List.mapi (fun i sub -> index i, build_init ~loc sub) l in + let initl = + List.mapi (fun i sub -> index i, build_init ~scope ~loc sub) l + in Cil_types.CompoundInit (typ, initl) (* Export *) let cil_logic_label label = label - let cil_varinfo v = harden_var v let cil_constant c = build_constant (harden_const c) - let cil_lval ~loc lv = build_lval ~loc (harden_lval lv) + let cil_varinfo v = build_var ~scope:Scope.empty (harden_var v) + let cil_lval ~loc lv = build_lval ~scope:Scope.empty ~loc (harden_lval lv) let cil_lval_opt ~loc lv = - Option.map (build_lval ~loc) (harden_lval_opt lv) - let cil_exp ~loc e = build_exp ~loc (harden_exp e) - let cil_exp_opt ~loc e = Option.map (build_exp ~loc) (harden_exp_opt e) + Option.map (build_lval ~scope:Scope.empty ~loc) (harden_lval_opt lv) + let cil_exp ~loc e = build_exp ~scope:Scope.empty ~loc (harden_exp e) + let cil_exp_opt ~loc e = + Option.map (build_exp ~scope:Scope.empty ~loc) (harden_exp_opt e) let cil_exp_list ~loc l = List.map (cil_exp ~loc) l let cil_term_lval ~loc ?restyp lv = - build_term_lval ~loc ~restyp (harden_lval lv) - let cil_term ~loc ?restyp e = build_term ~loc ~restyp (harden_exp e) + build_term_lval ~scope:Scope.empty ~loc ~restyp (harden_lval lv) + let cil_term ~loc ?restyp e = + build_term ~scope:Scope.empty ~loc ~restyp (harden_exp e) let cil_iterm ~loc ?restyp e = Logic_const.new_identified_term (cil_term ~loc ?restyp e) - let cil_pred ~loc ?restyp e = build_pred ~loc ~restyp (harden_exp e) + let cil_pred ~loc ?restyp e = + build_pred ~scope:Scope.empty ~loc ~restyp (harden_exp e) let cil_ipred ~loc ?restyp e = Logic_const.new_predicate (cil_pred ~loc ?restyp e) - let cil_init ~loc i = build_init ~loc (harden_init i) + let cil_init ~loc i = build_init ~scope:Scope.empty ~loc (harden_init i) - let cil_typeof (`var vi) = vi.Cil_types.vtype + let cil_typeof (`var v) = + match v with + | CilVar vi -> vi.Cil_types.vtype + | NewVar (_id,_name,typ) -> typ end @@ -686,33 +730,30 @@ module Pure = struct include Exp + exception DeclarationOutsideOfFunction + + type ghost = NoGhost | Ghost + type instr' = | CilInstr of Cil_types.instr | Skip | Assign of lval' * exp' | Call of lval' option * exp' * exp' list + | Local of var' * init' option * ghost + | LocalCopy of Cil_types.varinfo * var' * init' option * ghost type stmt' = | CilStmt of Cil_types.stmt | CilStmtkind of Cil_types.stmtkind | Instr of instr' | Sequence of stmt' list - | Ghost of stmt' + | Block of stmt' list + | GhostSection of stmt' + | If of exp' * stmt' list * stmt' list type instr = [ `instr of instr' ] type stmt = [ instr | `stmt of stmt' ] - (* Sequences *) - - let flatten_sequences l = - let rec add_one acc = function - | Sequence l -> add_list acc l - | stmt -> stmt :: acc - and add_list acc l = - List.fold_left add_one acc l - in - List.rev (add_list [] l) - (* Depolymorphize *) let harden_instr i = @@ -724,6 +765,9 @@ struct | #instr as instr -> Instr (harden_instr instr) | `stmt stmt -> stmt + let harden_stmt_list l = + List.map harden_stmt l + (* Build *) let of_instr i = `instr (CilInstr i) @@ -736,48 +780,163 @@ struct let of_stmtkind sk = `stmt (CilStmtkind sk) let of_stmt s = `stmt (CilStmt s) let of_stmts l = `stmt (Sequence (List.map (fun s -> CilStmt s) l)) - let block l = `stmt (Sequence (List.map harden_stmt l)) - let ghost s = `stmt (Ghost (harden_stmt s)) + let sequence l = `stmt (Sequence (List.map harden_stmt l)) + let block l = `stmt (Block (List.map harden_stmt l)) + let ghost s = `stmt (GhostSection (harden_stmt s)) + + let if_ cond ~then_ ~else_ = + `stmt (If (harden_exp cond, harden_stmt_list then_, harden_stmt_list else_)) + + let local' ?(ghost=false) ?init typ name = + let var = NewVar (Scope.new_id (), name, typ) in + let ghost = if ghost then Ghost else NoGhost in + let instr = Local (var, Option.map harden_init init, ghost) in + `var var, `instr instr + let local ?ghost ?init ty name = + let init = Option.map (values ty) init in + local' ?ghost ?init (cil_typ ty) name + + let local_copy ?(ghost=false) ?(suffix="_tmp") v = + let name, typ, vi = + match harden_var v with + | NewVar (_id, name, typ) -> name, typ, None + | CilVar vi -> vi.vname, vi.vtype, Some vi + in + let var = NewVar (Scope.new_id (), name ^ suffix, typ) in + let ghost = if ghost then Ghost else NoGhost in + let instr = match vi with + | None -> Local (var, None, ghost) + | Some vi -> LocalCopy (vi, var, None, ghost) + in + `var var, `instr instr (* Convert *) - let build_instr ~loc = function - | CilInstr i -> i + (* block: refers to the innermost englobing block where locals must be added + fundec: refers to the owner function where locals must also be added *) + + let build_local_definition ~scope ~loc ~block ~fundec v init ghost copied_vi = + let vi, scope = match v with + | CilVar vi -> vi, scope + | NewVar (id, name, typ) -> + let temp = false and global = false and formal = false in + let ghost = match ghost with Ghost -> true | NoGhost -> false in + let vi = + match copied_vi with + | None -> Cil.makeVarinfo ~temp ~ghost global formal name typ + | Some vi -> Cil.copyVarinfo vi name + in + let block, fundec = match block, fundec with + | Some block, Some fundec -> block, fundec + | None, _ | _, None -> raise DeclarationOutsideOfFunction + in + (* Register the variable *) + Cil.refresh_local_name fundec vi; + vi.vdecl <- loc; + fundec.slocals <- fundec.slocals @ [vi]; + block.Cil_types.blocals <- vi :: block.Cil_types.blocals; + vi, Scope.add id vi scope + in + (* Initialization *) + let initialization = + match init with + | None -> Cil_types.Skip loc + | Some init -> + vi.vdefined <- true; + let local_init = Cil_types.AssignInit (build_init ~scope ~loc init) in + Cil_types.Local_init (vi, local_init, loc) + in + initialization, scope + + let build_instr ~scope ~loc ~block ~fundec = function + | CilInstr i -> i, scope | Skip -> - Cil_types.Skip (loc) + Cil_types.Skip (loc), scope | Assign (dest,src) -> - let dest' = build_lval ~loc dest - and src' = build_exp ~loc src in + let dest' = build_lval ~scope ~loc dest + and src' = build_exp ~scope ~loc src in let src' = Cil.mkCast ~newt:(Cil.typeOfLval dest') src' in - Cil_types.Set (dest', src', loc) + Cil_types.Set (dest', src', loc), scope | Call (dest,callee,args) -> - let dest' = Option.map (build_lval ~loc) dest - and callee' = build_exp ~loc callee - and args' = List.map (build_exp ~loc) args in - Cil_types.Call (dest', callee', args', loc) - - let rec build_stmtkind ~loc ~ghost = function - | CilStmt s -> s.Cil_types.skind - | CilStmtkind sk -> sk - | Instr i -> Cil_types.Instr (build_instr ~loc i) - | Sequence l -> Cil_types.Block (build_block ~loc ~ghost l) - | Ghost s -> Cil_types.Block (build_block ~loc ~ghost:true [s]) - - and build_stmt ~loc ~ghost = function - | CilStmt s -> s - | Ghost s -> build_stmt ~loc ~ghost:true s - | stmt -> Cil.mkStmt ~ghost (build_stmtkind ~loc ~ghost stmt) - - and build_block ~loc ~ghost l = - let bstmts = List.map (build_stmt ~ghost ~loc) (flatten_sequences l) in - Cil.mkBlock bstmts + let dest' = Option.map (build_lval ~scope ~loc) dest + and callee' = build_exp ~scope ~loc callee + and args' = List.map (build_exp ~scope ~loc) args in + Cil_types.Call (dest', callee', args', loc), scope + | Local (v, init, ghost) -> + build_local_definition ~scope ~loc ~block ~fundec v init ghost None + | LocalCopy (vi, v, init, ghost) -> + build_local_definition ~scope ~loc ~block ~fundec v init ghost (Some vi) + + let rec build_stmtkind ~scope ~loc ~ghost ~block ~fundec = function + | CilStmtkind sk -> sk, scope + | Instr i -> + let instr, scope = build_instr ~scope ~loc ~block ~fundec i in + Cil_types.Instr instr, scope + | If (exp, then_stmts, else_stmt) -> + Cil_types.If ( + build_exp ~scope ~loc exp, + build_block ~scope ~loc ~ghost ~fundec then_stmts, + build_block ~scope ~loc ~ghost ~fundec else_stmt, + loc + ), + scope + | Sequence s | Block s -> + Cil_types.Block (build_block ~scope ~loc ~ghost ~fundec s), scope + | (CilStmt _) as s | GhostSection s -> + Cil_types.Block (build_block ~scope ~loc ~ghost:true ~fundec [s]), scope + + and build_stmtlist_rev ~scope ~loc ~ghost ~block ~fundec acc l = + let add_one (acc,scope) stmt = + match stmt with + | CilStmt s -> s :: acc, scope + | Sequence s -> (* do not build a block if unecessary *) + build_stmtlist_rev ~scope ~loc ~ghost ~block ~fundec acc s + | GhostSection stmt -> (* do not build a block if unecessary *) + build_stmtlist_rev ~scope ~loc ~ghost:true ~block ~fundec acc [stmt] + | _ -> + let stmtkind, scope = + build_stmtkind ~scope ~loc ~ghost ~block ~fundec stmt + in + match stmtkind with + | Instr (Cil_types.Skip _) -> acc, scope (* Filter skips out *) + | stmtkind -> + Cil.mkStmt ~ghost stmtkind :: acc, scope + in + List.fold_left add_one (acc,scope) l + + and build_stmt ~scope ~loc ~ghost ~block ~fundec = function + | CilStmt s -> s, scope + | GhostSection s -> build_stmt ~scope ~loc ~ghost:true ~block ~fundec s + | stmt -> + let stmtkind, scope = + build_stmtkind ~scope ~loc ~ghost ~block ~fundec stmt + in + Cil.mkStmt ~ghost stmtkind, scope + + and build_block ~scope ~loc ~ghost ~fundec l = + let block = Cil.mkBlock [] in + let bstmts, _scope = + build_stmtlist_rev ~scope ~loc ~ghost ~block:(Some block) ~fundec [] l + in + block.bstmts <- List.rev bstmts; + block + (* Export *) - let cil_instr ~loc i = build_instr ~loc (harden_instr i) - let cil_stmtkind ~loc s = build_stmtkind ~loc ~ghost:false (harden_stmt s) - let cil_stmt ~loc s = build_stmt ~loc ~ghost:false (harden_stmt s) + let top_block = + Option.map (fun fundec -> fundec.Cil_types.sbody) + + let cil_instr ?into:fundec ~loc i = + let scope = Scope.empty and block = top_block fundec in + fst (build_instr ~scope ~loc ~block ~fundec (harden_instr i)) + let cil_stmtkind ?into:fundec ~loc s = + let scope = Scope.empty and block = top_block fundec in + fst (build_stmtkind ~scope ~loc ~block ~fundec ~ghost:false (harden_stmt s)) + let cil_stmt ?into:fundec ~loc s = + let scope = Scope.empty and block = top_block fundec in + fst (build_stmt ~scope ~loc ~block ~fundec ~ghost:false (harden_stmt s)) (* Operators *) @@ -785,6 +944,9 @@ struct let (:=) = assign let (+=) lv e = assign lv (add lv e) let (-=) lv e = assign lv (sub lv e) + + let (let+) (var, stmt) f = stmt :: f var + let (and+) var1 var2 = (var1, var2) end @@ -987,13 +1149,13 @@ struct (* Blocks *) let new_block ?(loc=current_loc ()) ?(ghost=false) scope_type = - { - loc; - scope_type; - ghost; - stmts = []; - vars = []; - } + { + loc; + scope_type; + ghost; + stmts = []; + vars = []; + } let extract_ifthen_block b = match b.scope_type with @@ -1010,7 +1172,7 @@ struct | Function {fundec} -> fundec | _ -> raise (WrongContext "not in a opened function context") - let open_function ?(loc=current_loc ()) ?ghost ?vorig_name name = + let open_function ?(loc=current_loc ()) ?ghost ?vorig_name name = check_empty (); let vorig_name = Option.value ~default:name vorig_name in let fundec = Cil.emptyFunction vorig_name in @@ -1018,7 +1180,7 @@ struct fundec.svar.vname <- name; set_owner fundec; push (new_block ~loc ?ghost (Function {fundec})); - `var fundec.Cil_types.svar + `var (CilVar fundec.Cil_types.svar) let open_block ?(loc=current_loc ()) ?into ?ghost () = Option.iter set_owner into; @@ -1203,20 +1365,33 @@ struct v.vdefined <- true end; append_local v; - `var v + `var (CilVar v) let local ?ghost ?init ty name = let init = Option.map (values ty) init in local' ?ghost ?init (cil_typ ty) name - let local_copy ?(ghost=false) ?(suffix="_tmp") (`var vi) = + let local_copy ?(ghost=false) ?(suffix="_tmp") v = + let name, typ, vi = + match harden_var v with + | NewVar (_id, name, typ) -> name, typ, None + | CilVar vi -> vi.vname, vi.vtype, Some vi + in + let name = name ^ suffix in let fundec = get_owner () and b = top () in let ghost = ghost || b.ghost in - let v = Cil.copyVarinfo vi (vi.Cil_types.vname ^ suffix) in + let v = + match vi with + | None -> + let temp = false and global = false and formal = false in + Cil.makeVarinfo ~temp ~ghost global formal name typ + | Some vi -> + Cil.copyVarinfo vi name + in v.vghost <- v.vghost || ghost; Cil.refresh_local_name fundec v; append_local v; - `var v + `var (CilVar v) let parameter ?(ghost=false) ?(attributes=[]) typ name = let fundec = get_owner () and b = top () in @@ -1224,7 +1399,7 @@ struct let loc = current_loc () in let v = Cil.makeFormalVar ~ghost ~loc fundec name typ in v.Cil_types.vattr <- attributes; - `var v + `var (CilVar v) (* Instructions *) @@ -1252,8 +1427,10 @@ struct let pure exp = let loc = current_loc () in let exp' = cil_exp ~loc exp in - let `var v = local' (Cil.typeOf exp') "tmp" ~init:(Exp.of_exp exp') in - v.vdescr <- Some (Format.asprintf "%a" !Cil.pp_exp_ref exp') + match local' (Cil.typeOf exp') "tmp" ~init:(Exp.of_exp exp') with + | `var (CilVar v) -> + v.vdescr <- Some (Format.asprintf "%a" !Cil.pp_exp_ref exp') + | _ -> assert false let skip () = let loc = current_loc () in diff --git a/src/kernel_services/ast_building/cil_builder.mli b/src/kernel_services/ast_building/cil_builder.mli index 7160ebff8a0..a412b44a128 100644 --- a/src/kernel_services/ast_building/cil_builder.mli +++ b/src/kernel_services/ast_building/cil_builder.mli @@ -204,6 +204,7 @@ sig exception NotAPredicate of exp exception NotAFunction of Cil_types.logic_info exception Typing_error of string + exception OutOfScope of string val cil_logic_label : label -> Cil_types.logic_label val cil_constant : [< const] -> Cil_types.constant @@ -245,18 +246,37 @@ sig val incr : [< lval] -> [> instr] val call : [< lval | `none] -> [< exp] -> [< exp] list -> [> instr] + val local : ?ghost:bool -> ?init:'v -> (init,'v) typ -> string -> + [> var] * [> instr] + val local' : ?ghost:bool -> ?init:init -> Cil_types.typ -> string -> + [> var] * [> instr] + val local_copy : ?ghost:bool -> ?suffix:string -> [< var] -> + [> var] * [> instr] + (* Statements *) val of_stmtkind : Cil_types.stmtkind -> [> stmt] val of_stmt : Cil_types.stmt -> [> stmt] val of_stmts : Cil_types.stmt list -> [> stmt] val block : [< stmt] list -> [> stmt] + val sequence : [< stmt] list -> [> stmt] (* does not generate block *) val ghost : [< stmt] -> [> stmt] + val if_ : [< exp] -> then_:[< stmt] list -> else_:[< stmt] list -> [> stmt] + + (* Conversion to Cil *) - val cil_instr : loc:Cil_types.location -> instr -> Cil_types.instr - val cil_stmtkind : loc:Cil_types.location -> stmt -> Cil_types.stmtkind - val cil_stmt : loc:Cil_types.location -> stmt -> Cil_types.stmt + (* for the three following function into is mandatory if the built ast + contains locals declarations *) + val cil_instr : ?into:Cil_types.fundec -> + loc:Cil_types.location -> instr -> Cil_types.instr + val cil_stmtkind : ?into:Cil_types.fundec -> + loc:Cil_types.location -> stmt -> Cil_types.stmtkind + val cil_stmt : ?into:Cil_types.fundec -> + loc:Cil_types.location -> stmt -> Cil_types.stmt (* Operators *) + val (let+) : var * stmt -> (var -> stmt list) -> stmt list + val (and+) : var -> var -> var * var + val (:=) : [< lval] -> [< exp] -> [> instr] (* assign *) val (+=) : [< lval] -> [< exp] -> [> instr] val (-=) : [< lval] -> [< exp] -> [> instr] @@ -342,4 +362,3 @@ sig val (+=) : [< lval] -> [< exp] -> unit val (-=) : [< lval] -> [< exp] -> unit end - diff --git a/tests/misc/cil_builder.i b/tests/misc/cil_builder.i new file mode 100644 index 00000000000..3dde1fd899c --- /dev/null +++ b/tests/misc/cil_builder.i @@ -0,0 +1,8 @@ +/* run.config +MODULE: @PTEST_NAME@ + OPT: +*/ + +void main(void) { + // This will be filled by the OCaml Module +} diff --git a/tests/misc/cil_builder.ml b/tests/misc/cil_builder.ml new file mode 100644 index 00000000000..571edd890e7 --- /dev/null +++ b/tests/misc/cil_builder.ml @@ -0,0 +1,21 @@ +module Build = Cil_builder.Pure + +let run () = + let kf, _ = Globals.entry_point () in + let stmt = Kernel_function.find_first_stmt kf in + let loc = Kernel_function.get_location kf in + let into = Kernel_function.get_definition kf in + stmt.skind <- Build.( + cil_stmtkind ~into ~loc @@ sequence @@ + let+ i = local int "x" in [ + i := (of_int 1); + if_ (i < of_int 3) + ~then_:[incr i] + ~else_:[i := zero] + ] + ); + Kernel.result "%a" Printer.pp_file (Ast.get ()) + + +let () = + Db.Main.extend run diff --git a/tests/misc/oracle/cil_builder.res.oracle b/tests/misc/oracle/cil_builder.res.oracle new file mode 100644 index 00000000000..c4f1e7ee9c3 --- /dev/null +++ b/tests/misc/oracle/cil_builder.res.oracle @@ -0,0 +1,10 @@ +[kernel] Parsing cil_builder.i (no preprocessing) +[kernel] /* Generated by Frama-C */ + void main(void) + { + { + int x; + x = 1; + if (x < 3) x ++; else x = 0; + } + } -- GitLab