(* * * Copyright (c) 2004, * Jeremy Condit * George C. Necula * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * 3. The names of the contributors may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *) open Cil open Pretty module E = Errormsg let debug = false let numRegions : int = 2 let newGlobals : global list ref = ref [] let curFundec : fundec ref = ref dummyFunDec let curLocation : location ref = ref locUnknown let applyOption (fn : 'a -> 'b) (ao : 'a option) : 'b option = match ao with | Some a -> Some (fn a) | None -> None let getRegion (attrs : attributes) : int = try match List.hd (filterAttributes "region" attrs) with | Attr (_, [AInt i]) -> i | _ -> E.s (bug "bad region attribute") with Failure _ -> 1 let checkRegion (i : int) (attrs : attributes) : bool = (getRegion attrs) = i let regionField (i : int) : string = "r" ^ (string_of_int i) let regionStruct (i : int) (name : string) : string = name ^ "_r" ^ (string_of_int i) let foldRegions (fn : int -> 'a -> 'a) (base : 'a) : 'a = let rec helper (i : int) : 'a = if i <= numRegions then fn i (helper (i + 1)) else base in helper 1 let rec getTypeName (t : typ) : string = match t with | TVoid _ -> "void" | TInt _ -> "int" | TFloat _ -> "float" | TComp (cinfo, _) -> "comp_" ^ cinfo.cname | TNamed (tinfo, _) -> "td_" ^ tinfo.tname | TPtr (bt, _) -> "ptr_" ^ (getTypeName bt) | TArray (bt, _, _) -> "array_" ^ (getTypeName bt) | TFun _ -> "fn" | _ -> E.s (unimp "typename") let isAllocFunction (fn : exp) : bool = match fn with | Lval (Var vinfo, NoOffset) when vinfo.vname = "malloc" -> true | _ -> false let isExternalFunction (fn : exp) : bool = match fn with | Lval (Var vinfo, NoOffset) when vinfo.vstorage = Extern -> true | _ -> false let types : (int * typsig, typ) Hashtbl.t = Hashtbl.create 113 let typeInfos : (int * string, typeinfo) Hashtbl.t = Hashtbl.create 113 let compInfos : (int * int, compinfo) Hashtbl.t = Hashtbl.create 113 let varTypes : (typsig, typ) Hashtbl.t = Hashtbl.create 113 let varCompInfos : (typsig, compinfo) Hashtbl.t = Hashtbl.create 113 let rec sliceCompInfo (i : int) (cinfo : compinfo) : compinfo = try Hashtbl.find compInfos (i, cinfo.ckey) with Not_found -> mkCompInfo cinfo.cstruct (regionStruct i cinfo.cname) (fun cinfo' -> Hashtbl.add compInfos (i, cinfo.ckey) cinfo'; List.fold_right (fun finfo rest -> let t = sliceType i finfo.ftype in if not (isVoidType t) then (finfo.fname, t, finfo.fbitfield, finfo.fattr, finfo.floc) :: rest else rest) cinfo.cfields []) cinfo.cattr and sliceTypeInfo (i : int) (tinfo : typeinfo) : typeinfo = try Hashtbl.find typeInfos (i, tinfo.tname) with Not_found -> let result = { tinfo with tname = regionStruct i tinfo.tname; ttype = sliceType i tinfo.ttype; } in Hashtbl.add typeInfos (i, tinfo.tname) result; result and sliceType (i : int) (t : typ) : typ = let ts = typeSig t in try Hashtbl.find types (i, ts) with Not_found -> let result = match t with | TVoid _ -> t | TInt (_, attrs) -> if checkRegion i attrs then t else TVoid [] | TFloat (_, attrs) -> if checkRegion i attrs then t else TVoid [] | TComp (cinfo, attrs) -> TComp (sliceCompInfo i cinfo, attrs) | TNamed (tinfo, attrs) -> TNamed (sliceTypeInfo i tinfo, attrs) | TPtr (TVoid _, _) -> t (* Avoid discarding void*. *) | TPtr (bt, attrs) -> let bt' = sliceType i bt in if not (isVoidType bt') then TPtr (bt', attrs) else TVoid [] | TArray (bt, eo, attrs) -> TArray (sliceType i bt, applyOption (sliceExp 1) eo, attrs) | TFun (ret, args, va, attrs) -> if checkRegion i attrs then TFun (sliceTypeAll ret, applyOption (List.map (fun (aname, atype, aattrs) -> (aname, sliceTypeAll atype, aattrs))) args, va, attrs) else TVoid [] | TBuiltin_va_list _ -> t | _ -> E.s (unimp "type %a" d_type t) in Hashtbl.add types (i, ts) result; result and sliceTypeAll (t : typ) : typ = begin match t with | TComp (cinfo, _) when hasAttribute "var_type_sliced" cinfo.cattr -> E.s (bug "tried to slice twice") | _ -> () end; let ts = typeSig t in try Hashtbl.find varTypes ts with Not_found -> let cinfo = let name = ("var_" ^ (getTypeName t)) in if debug then ignore (E.log "creating %s\n" name); try Hashtbl.find varCompInfos ts with Not_found -> mkCompInfo true name (fun cinfo -> Hashtbl.add varCompInfos ts cinfo; foldRegions (fun i rest -> let t' = sliceType i t in if not (isVoidType t') then (regionField i, t', None, [], !curLocation) :: rest else rest) []) [Attr ("var_type_sliced", [])] in let t' = if List.length cinfo.cfields > 1 then begin newGlobals := GCompTag (cinfo, !curLocation) :: !newGlobals; TComp (cinfo, []) end else t in Hashtbl.add varTypes ts t'; t' and sliceLval (i : int) (lv : lval) : lval = if debug then ignore (E.log "lval %a\n" d_lval lv); let lh, offset = lv in match lh with | Var vinfo -> let t = sliceTypeAll vinfo.vtype in let offset' = match t with | TComp (cinfo, _) when hasAttribute "var_type_sliced" cinfo.cattr -> Field (getCompField cinfo (regionField i), offset) | _ -> offset in Var vinfo, offset' | Mem e -> Mem (sliceExp i e), offset and sliceExp (i : int) (e : exp) : exp = if debug then ignore (E.log "exp %a\n" d_exp e); match e with | Const c -> Const c | Lval lv -> Lval (sliceLval i lv) | UnOp (op, e1, t) -> UnOp (op, sliceExp i e1, sliceType i t) | BinOp (op, e1, e2, t) -> BinOp (op, sliceExp i e1, sliceExp i e2, sliceType i t) | CastE (t, e) -> sliceCast i t e | AddrOf lv -> AddrOf (sliceLval i lv) | StartOf lv -> StartOf (sliceLval i lv) | SizeOf t -> SizeOf (sliceTypeAll t) | _ -> E.s (unimp "exp %a" d_exp e) and sliceCast (i : int) (t : typ) (e : exp) : exp = let te = typeOf e in match t, te with | TInt (k1, _), TInt (k2, attrs2) when k1 = k2 -> (* Note: We strip off integer cast operations. *) sliceExp (getRegion attrs2) e | TInt (k1, _), TPtr _ -> (* Note: We strip off integer cast operations. *) sliceExp i e | TPtr _, _ when isZero e -> CastE (sliceType i t, sliceExp i e) | TPtr (bt1, _), TPtr (bt2, _) when (typeSig bt1) = (typeSig bt2) -> CastE (sliceType i t, sliceExp i e) | _ -> E.s (unimp "sketchy cast (%a) -> (%a)\n" d_type te d_type t) and sliceExpAll (e : exp) (l : location) : instr list * exp = let t = typeOf e in let t' = sliceTypeAll t in match t' with | TComp (cinfo, _) when hasAttribute "var_type_sliced" cinfo.cattr -> let vinfo = makeTempVar !curFundec t in let instrs = foldRegions (fun i rest -> try let finfo = getCompField cinfo (regionField i) in if not (isVoidType finfo.ftype) then Set ((Var vinfo, Field (finfo, NoOffset)), sliceExp i e, l) :: rest else rest with Not_found -> rest) [] in instrs, Lval (var vinfo) | _ -> [], sliceExp 1 e let sliceVar (vinfo : varinfo) : unit = if hasAttribute "var_sliced" vinfo.vattr then E.s (bug "tried to slice a var twice"); let t = sliceTypeAll vinfo.vtype in if debug then ignore (E.log "setting %s type to %a\n" vinfo.vname d_type t); vinfo.vattr <- addAttribute (Attr ("var_sliced", [])) vinfo.vattr; vinfo.vtype <- t let sliceInstr (inst : instr) : instr list = match inst with | Set (lv, e, loc) -> if debug then ignore (E.log "set %a %a\n" d_lval lv d_exp e); let t = typeOfLval lv in foldRegions (fun i rest -> if not (isVoidType (sliceType i t)) then Set (sliceLval i lv, sliceExp i e, loc) :: rest else rest) [] | Call (ret, fn, args, l) when isAllocFunction fn -> let lv = match ret with | Some lv -> lv | None -> E.s (bug "malloc call has no return lval") in let t = typeOfLval lv in foldRegions (fun i rest -> if not (isVoidType (sliceType i t)) then Call (Some (sliceLval i lv), sliceExp 1 fn, List.map (sliceExp i) args, l) :: rest else rest) [] | Call (ret, fn, args, l) when isExternalFunction fn -> [Call (applyOption (sliceLval 1) ret, sliceExp 1 fn, List.map (sliceExp 1) args, l)] | Call (ret, fn, args, l) -> let ret', set = match ret with | Some lv -> let vinfo = makeTempVar !curFundec (typeOfLval lv) in Some (var vinfo), [Set (lv, Lval (var vinfo), l)] | None -> None, [] in let instrs, args' = List.fold_right (fun arg (restInstrs, restArgs) -> let instrs, arg' = sliceExpAll arg l in instrs @ restInstrs, (arg' :: restArgs)) args ([], []) in instrs @ (Call (ret', sliceExp 1 fn, args', l) :: set) | _ -> E.s (unimp "inst %a" d_instr inst) let sliceReturnExp (eo : exp option) (l : location) : stmtkind = match eo with | Some e -> begin match sliceExpAll e l with | [], e' -> Return (Some e', l) | instrs, e' -> Block (mkBlock [mkStmt (Instr instrs); mkStmt (Return (Some e', l))]) end | None -> Return (None, l) let rec sliceStmtKind (sk : stmtkind) : stmtkind = match sk with | Instr instrs -> Instr (List.flatten (List.map sliceInstr instrs)) | Block b -> Block (sliceBlock b) | If (e, b1, b2, l) -> If (sliceExp 1 e, sliceBlock b1, sliceBlock b2, l) | Break l -> Break l | Continue l -> Continue l | Return (eo, l) -> sliceReturnExp eo l | Switch (e, b, sl, l) -> Switch (sliceExp 1 e, sliceBlock b, List.map sliceStmt sl, l) | Loop (b, l, so1, so2) -> Loop (sliceBlock b, l, applyOption sliceStmt so1, applyOption sliceStmt so2) | Goto _ -> sk | _ -> E.s (unimp "statement") and sliceStmt (s : stmt) : stmt = (* Note: We update statements destructively so that goto/switch work. *) s.skind <- sliceStmtKind s.skind; s and sliceBlock (b : block) : block = ignore (List.map sliceStmt b.bstmts); b let sliceFundec (fd : fundec) (l : location) : unit = curFundec := fd; curLocation := l; ignore (sliceBlock fd.sbody); curFundec := dummyFunDec; curLocation := locUnknown let sliceGlobal (g : global) : unit = match g with | GType (tinfo, l) -> newGlobals := foldRegions (fun i rest -> GType (sliceTypeInfo i tinfo, l) :: rest) !newGlobals | GCompTag (cinfo, l) -> newGlobals := foldRegions (fun i rest -> GCompTag (sliceCompInfo i cinfo, l) :: rest) !newGlobals | GCompTagDecl (cinfo, l) -> newGlobals := foldRegions (fun i rest -> GCompTagDecl (sliceCompInfo i cinfo, l) :: rest) !newGlobals | GFun (fd, l) -> sliceFundec fd l; newGlobals := GFun (fd, l) :: !newGlobals | GVarDecl _ | GVar _ -> (* Defer processing of vars until end. *) newGlobals := g :: !newGlobals | _ -> E.s (unimp "global %a\n" d_global g) let sliceGlobalVars (g : global) : unit = match g with | GFun (fd, l) -> curFundec := fd; curLocation := l; List.iter sliceVar fd.slocals; List.iter sliceVar fd.sformals; setFunctionType fd (sliceType 1 fd.svar.vtype); curFundec := dummyFunDec; curLocation := locUnknown; | GVar (vinfo, _, l) -> curLocation := l; sliceVar vinfo; curLocation := locUnknown | _ -> () class dropAttrsVisitor = object inherit nopCilVisitor method vvrbl (vinfo : varinfo) = vinfo.vattr <- dropAttribute "var_sliced" vinfo.vattr; DoChildren method vglob (g : global) = begin match g with | GCompTag (cinfo, _) -> cinfo.cattr <- dropAttribute "var_type_sliced" cinfo.cattr; | _ -> () end; DoChildren end let sliceFile (f : file) : unit = newGlobals := []; List.iter sliceGlobal f.globals; List.iter sliceGlobalVars f.globals; f.globals <- List.rev !newGlobals; visitCilFile (new dropAttrsVisitor) f let feature : featureDescr = { fd_name = "DataSlicing"; fd_enabled = ref false; fd_description = "data slicing"; fd_extraopt = []; fd_doit = sliceFile; fd_post_check = true; }