diff options
Diffstat (limited to 'cil/src/ext/partial.ml')
-rw-r--r-- | cil/src/ext/partial.ml | 851 |
1 files changed, 0 insertions, 851 deletions
diff --git a/cil/src/ext/partial.ml b/cil/src/ext/partial.ml deleted file mode 100644 index 4beca3fc..00000000 --- a/cil/src/ext/partial.ml +++ /dev/null @@ -1,851 +0,0 @@ -(* See copyright notice at the end of the file *) -(***************************************************************************** - * Partial Evaluation & Constant Folding - * - * Soundness Assumptions: - * (1) Whole program analysis. You may call functions that are not defined - * (e.g., library functions) but they may not call back. - * (2) An undefined function may not return the address of a function whose - * address is not already taken in the code I can see. - * (3) A function pointer call may only call a function that has its - * address visibly taken in the code I can see. - * - * (More assumptions in the comments below) - *****************************************************************************) -open Cil -open Pretty - -(***************************************************************************** - * A generic signature for Alias Analysis information. Used to compute the - * call graph and do symbolic execution. - ****************************************************************************) -module type AliasInfo = - sig - val can_have_the_same_value : Cil.exp -> Cil.exp -> bool - val resolve_function_pointer : Cil.exp -> (Cil.fundec list) - end - -(***************************************************************************** - * A generic signature for Symbolic Execution execution algorithms. Such - * algorithms are used below to perform constant folding and dead-code - * elimination. You write a "basic-block" symex algorithm, we'll make it - * a whole-program CFG-pruner. - ****************************************************************************) -module type Symex = - sig - type t (* the type of a symex algorithm state object *) - val empty : t (* all values unknown *) - val equal : t -> t -> bool (* are these the same? *) - val assign : t -> Cil.lval -> Cil.exp -> (Cil.exp * t) - (* incorporate an assignment, return the RHS *) - val unassign : t -> Cil.lval -> t - (* lose all information about the given lvalue: assume an - * unknown external value has been assigned to it *) - val assembly : t -> Cil.instr -> t (* handle ASM *) - val assume : t -> Cil.exp -> t (* incorporate an assumption *) - val evaluate : t -> Cil.exp -> Cil.exp (* symbolic evaluation *) - val join : (t list) -> t (* join a bunch of states *) - val call : t -> Cil.fundec -> (Cil.exp list) -> (Cil.exp list * t) - (* we are calling the given function with the given actuals *) - val return : t -> Cil.fundec -> t - (* we are returning from the given function *) - val call_to_unknown_function : t -> t - (* throw away information that may have been changed *) - val debug : t -> unit - end - -(***************************************************************************** - * A generic signature for whole-progam call graphs. - ****************************************************************************) -module type CallGraph = - sig - type t (* the type of a call graph *) - val compute : Cil.file -> t (* file for which we compute the graph *) - val can_call : t -> Cil.fundec -> (Cil.fundec list) - val can_be_called_by : t -> Cil.fundec -> (Cil.fundec list) - val fundec_of_varinfo : t -> Cil.varinfo -> Cil.fundec - end - -(***************************************************************************** - * My cheap-o Alias Analysis. Assume all expressions can have the same - * value and any function with its address taken can be the target of - * any function pointer. - * - * Soundness Assumptions: - * (1) Someone must call "find_all_functions_With_address_taken" before the - * results are valid. This is already done in the code below. - ****************************************************************************) -let all_functions_with_address_taken = ref [] -let find_all_functions_with_address_taken (f : Cil.file) = - iterGlobals f (fun g -> match g with - GFun(fd,_) -> if fd.svar.vaddrof then - all_functions_with_address_taken := fd :: - !all_functions_with_address_taken - | _ -> ()) - -module EasyAlias = - struct - let can_have_the_same_value e1 e2 = true - let resolve_function_pointer e1 = !all_functions_with_address_taken - end - -(***************************************************************************** - * My particular method for computing the Call Graph. - ****************************************************************************) -module EasyCallGraph = functor (A : AliasInfo) -> - struct - type callGraphNode = { - fd : Cil.fundec ; - mutable calledBy : Cil.fundec list ; - mutable calls : Cil.fundec list ; - } - type t = (Cil.varinfo, callGraphNode) Hashtbl.t - - let cgCreateNode cg fundec = - let newnode = { fd = fundec ; calledBy = [] ; calls = [] } in - Hashtbl.add cg fundec.svar newnode - - let cgFindNode cg svar = Hashtbl.find cg svar - - let cgAddEdge cg caller callee = - try - let n1 = cgFindNode cg caller in - let n2 = cgFindNode cg callee in - n1.calls <- n2.fd :: n1.calls ; - n1.calledBy <- n1.fd :: n1.calledBy - with _ -> () - - class callGraphVisitor cg = object - inherit nopCilVisitor - val the_fun = ref None - - method vinst i = - let _ = match i with - Call(_,Lval(Var(callee),NoOffset),_,_) -> begin - (* known function call *) - match !the_fun with - None -> failwith "callGraphVisitor: call outside of any function" - | Some(enclosing) -> cgAddEdge cg enclosing callee - end - | Call(_,e,_,_) -> begin - (* unknown function call *) - match !the_fun with - None -> failwith "callGraphVisitor: call outside of any function" - | Some(enclosing) -> let lst = A.resolve_function_pointer e in - List.iter (fun possible_target_fd -> - cgAddEdge cg enclosing possible_target_fd.svar) lst - end - | _ -> () - in SkipChildren - - method vfunc f = the_fun := Some(f.svar) ; DoChildren - end - - let compute (f : Cil.file) = - let cg = Hashtbl.create 511 in - iterGlobals f (fun g -> match g with - GFun(fd,_) -> cgCreateNode cg fd - | _ -> () - ) ; - visitCilFileSameGlobals (new callGraphVisitor cg) f ; - cg - - let can_call cg fd = - let n = cgFindNode cg fd.svar in n.calls - let can_be_called_by cg fd = - let n = cgFindNode cg fd.svar in n.calledBy - let fundec_of_varinfo cg vi = - let n = cgFindNode cg vi in n.fd - end (* END OF: module EasyCallGraph *) - -(***************************************************************************** - * Necula's Constant Folding Strategem (re-written to be applicative) - * - * Soundness Assumptions: - * (1) Inline assembly does not affect constant folding. - ****************************************************************************) -module OrderedInt = - struct - type t = int - let compare = compare - end -module IntMap = Map.Make(OrderedInt) - -module NeculaFolding = functor (A : AliasInfo) -> - struct - (* Register file. Maps identifiers of local variables to expressions. - * We also remember if the expression depends on memory or depends on - * variables that depend on memory *) - type reg = { - rvi : varinfo ; - rval : exp ; - rmem : bool - } - type t = reg IntMap.t - let empty = IntMap.empty - let equal t1 t2 = (compare t1 t2 = 0) (* use OCAML here *) - let dependsOnMem = ref false - (* Rewrite an expression based on the current register file *) - class rewriteExpClass (regFile : t) = object - inherit nopCilVisitor - method vexpr = function - | Lval (Var v, NoOffset) -> begin - try - let defined = (IntMap.find v.vid regFile) in - if (defined.rmem) then dependsOnMem := true; - (match defined.rval with - | Const(x) -> ChangeTo (defined.rval) - | _ -> DoChildren) - with Not_found -> DoChildren - end - | Lval (Mem _, _) -> dependsOnMem := true; DoChildren - | _ -> DoChildren - end - (* Rewrite an expression and return the new expression along with an - * indication of whether it depends on memory *) - let rewriteExp r (e: exp) : exp * bool = - dependsOnMem := false; - let e' = constFold true (visitCilExpr (new rewriteExpClass r) e) in - e', !dependsOnMem - let eval r e = - let new_e, depends = rewriteExp r e in - new_e - - let setMemory regFile = - (* Get a list of all mappings that depend on memory *) - let depids = ref [] in - IntMap.iter (fun id v -> if v.rmem then depids := id :: !depids) regFile; - (* And remove them from the register file *) - List.fold_left (fun acc id -> IntMap.remove id acc) regFile !depids - - let setRegister regFile (v: varinfo) ((e,b): exp * bool) = - IntMap.add v.vid { rvi = v ; rval = e ; rmem = b; } regFile - - let resetRegister regFile (id: int) = - IntMap.remove id regFile - - class findLval lv contains = object - inherit nopCilVisitor - method vlval l = - if l = lv then - (contains := true ; SkipChildren) - else - DoChildren - end - - let removeMappingsThatDependOn regFile l = - (* Get a list of all mappings that depend on l *) - let depids = ref [] in - IntMap.iter (fun id reg -> - let found = ref false in - ignore (visitCilExpr (new findLval l found) reg.rval) ; - if !found then - depids := id :: !depids - ) regFile ; - (* And remove them from the register file *) - List.fold_left (fun acc id -> IntMap.remove id acc) regFile !depids - - let assign r l e = - let (newe,b) = rewriteExp r e in - let r' = match l with - (Var v, NoOffset) -> - let r'' = setRegister r v (newe,b) in - removeMappingsThatDependOn r'' l - | (Mem _, _) -> setMemory r - | _ -> r - in newe, r' - - let unassign r l = - let r' = match l with - (Var v, NoOffset) -> - let r'' = resetRegister r v.vid in - removeMappingsThatDependOn r'' l - | (Mem _, _) -> setMemory r - | _ -> r - in r' - - let assembly r i = r (* no-op in Necula-world *) - let assume r e = r (* no-op in Necula-world *) - - let evaluate r e = - let (newe,_) = rewriteExp r e in - newe - - (* Join two symex states *) - let join2 (r1 : t) (r2 : t) = - let keep = ref [] in - IntMap.iter (fun id reg -> - try - let reg' = IntMap.find id r2 in - if reg'.rval = reg.rval && reg'.rmem = reg.rmem then - keep := (id,reg) :: !keep - with _ -> () - ) r1 ; - List.fold_left (fun acc (id,v) -> - IntMap.add id v acc) (IntMap.empty) !keep - - let join (lst : t list) = match lst with - [] -> failwith "empty list" - | r :: tl -> List.fold_left - (fun (acc : t) (elt : t) -> join2 acc elt) r tl - - let call r fd el = - let new_arg_list = ref [] in - let final_r = List.fold_left2 (fun r vi e -> - let newe, r' = assign r ((Var(vi),NoOffset)) e in - new_arg_list := newe :: !new_arg_list ; - r' - ) r fd.sformals el in - (List.rev !new_arg_list), final_r - - let return r fd = - let regFile = - List.fold_left (fun r vi -> IntMap.remove vi.vid r) r fd.sformals - in - (* Get a list of all globals *) - let depids = ref [] in - IntMap.iter (fun vid reg -> - if reg.rvi.vglob || reg.rvi.vaddrof then depids := vid :: !depids - ) regFile ; - (* And remove them from the register file *) - List.fold_left (fun acc id -> IntMap.remove id acc) regFile !depids - - - let call_to_unknown_function r = - setMemory r - - let debug r = - IntMap.iter (fun key reg -> - ignore (Pretty.printf "%s <- %a (%b)@!" reg.rvi.vname d_exp reg.rval reg.rmem) - ) r - end (* END OF: NeculaFolding *) - -(***************************************************************************** - * A transformation to make every function call end its statement. So - * { x=1; Foo(); y=1; } - * becomes at least: - * { { x=1; Foo(); } - * { y=1; } } - * But probably more like: - * { { x=1; } { Foo(); } { y=1; } } - ****************************************************************************) -let rec contains_call il = match il with - [] -> false - | Call(_) :: tl -> true - | _ :: tl -> contains_call tl - -class callBBVisitor = object - inherit nopCilVisitor - - method vstmt s = - match s.skind with - Instr(il) when contains_call il -> begin - let list_of_stmts = List.map (fun one_inst -> - mkStmtOneInstr one_inst) il in - let block = mkBlock list_of_stmts in - ChangeDoChildrenPost(s, (fun _ -> - s.skind <- Block(block) ; - s)) - end - | _ -> DoChildren - - method vvdec _ = SkipChildren - method vexpr _ = SkipChildren - method vlval _ = SkipChildren - method vtype _ = SkipChildren -end - -let calls_end_basic_blocks f = - let thisVisitor = new callBBVisitor in - visitCilFileSameGlobals thisVisitor f - -(***************************************************************************** - * A transformation that gives each variable a unique identifier. - ****************************************************************************) -class vidVisitor = object - inherit nopCilVisitor - val count = ref 0 - - method vvdec vi = - vi.vid <- !count ; - incr count ; SkipChildren -end - -let globally_unique_vids f = - let thisVisitor = new vidVisitor in - visitCilFileSameGlobals thisVisitor f - -(***************************************************************************** - * The Weimeric Partial Evaluation Data-Flow Engine - * - * This functor performs flow-sensitive, context-insensitive whole-program - * data-flow analysis with an eye toward partial evaluation and constant - * folding. - * - * Toposort the whole-program inter-procedural CFG to compute - * (1) the number of actual predecessors for each statement - * (2) the global toposort ordering - * - * Perform standard data-flow analysis (joins, etc) on the ICFG until you - * hit a fixed point. If this changed the structure of the ICFG (by - * removing an IF-branch or an empty function call), redo the whole thing. - * - * Soundness Assumptions: - * (1) A "call instruction" is the last thing in its statement. - * Use "calls_end_basic_blocks" to get this. cil/src/main.ml does - * this when you pass --makeCFG. - * (2) All variables have globally unique identifiers. - * Use "globally_unique_vids" to get this. cil/src/main.ml does - * this when you pass --makeCFG. - * (3) This may not be a strict soundness requirement, but I wrote this - * assuming that the input file has all switch/break/continue - * statements removed. - ****************************************************************************) -module MakePartial = - functor (S : Symex) -> - functor (C : CallGraph) -> - functor (A : AliasInfo) -> - struct - - let debug = false - - (* We keep this information about every statement. Ideally this should - * be put in the stmt itself, but CIL doesn't give us space. *) - type sinfo = { (* statement info *) - incoming_state : (int, S.t) Hashtbl.t ; - (* mapping from stmt.sid to Symex.state *) - reachable_preds : (int, bool) Hashtbl.t ; - (* basically a set of all of the stmt.sids that can really - * reach this statement *) - mutable last_used_state : S.t option ; - (* When we last did the Post() of this statement, what - * incoming state did we use? If our new incoming state is - * the same, we don't have to do it again. *) - mutable priority : int ; - (* Whole-program toposort priority. High means "do me first". - * The first stmt in "main()" will have the highest priority. - *) - } - let sinfo_ht = Hashtbl.create 511 - let clear_sinfo () = Hashtbl.clear sinfo_ht - - (* We construct sinfo nodes lazily: if you ask for one that isn't - * there, we build it. *) - let get_sinfo stmt = - try - Hashtbl.find sinfo_ht stmt.sid - with _ -> - let new_sinfo = { incoming_state = Hashtbl.create 3 ; - reachable_preds = Hashtbl.create 3 ; - last_used_state = None ; - priority = (-1) ; } in - Hashtbl.add sinfo_ht stmt.sid new_sinfo ; - new_sinfo - - (* Topological Sort is a DFS in which you assign a priority right as - * you finished visiting the children. While we're there we compute - * the actual number of unique predecessors for each statement. The CIL - * information may be out of date because we keep changing the CFG by - * removing IFs and whatnot. *) - let toposort_counter = ref 1 - let add_edge s1 s2 = - let si2 = get_sinfo s2 in - Hashtbl.replace si2.reachable_preds s1.sid true - - let rec toposort c stmt = - let si = get_sinfo stmt in - if si.priority >= 0 then - () (* already visited! *) - else begin - si.priority <- 0 ; (* currently visiting *) - (* handle function calls in this basic block *) - (match stmt.skind with - (Instr(il)) -> - List.iter (fun i -> - let fd_list = match i with - Call(_,Lval(Var(vi),NoOffset),_,_) -> - begin - try - let fd = C.fundec_of_varinfo c vi in - [fd] - with e -> [] (* calling external function *) - end - | Call(_,e,_,_) -> - A.resolve_function_pointer e - | _ -> [] - in - List.iter (fun fd -> - if List.length fd.sbody.bstmts > 0 then - let fun_stmt = List.hd fd.sbody.bstmts in - add_edge stmt fun_stmt ; - toposort c fun_stmt - ) fd_list - ) il - | _ -> ()); - List.iter (fun succ -> - add_edge stmt succ ; toposort c succ) stmt.succs ; - si.priority <- !toposort_counter ; - incr toposort_counter - end - - (* we set this to true whenever we eliminate an IF or otherwise - * change the CFG *) - let changed_cfg = ref false - - (* Partially evaluate / constant fold a statement. Basically this just - * asks the Symex algorithm to evaluate the RHS in the current state - * and then compute a new state that incorporates the assignment. - * - * However, we have special handling for ifs and calls. If we can - * evaluate an if predicate to a constant, we remove the if. - * - * If we are going to make a call to a function with an empty body, we - * remove the function call. *) - let partial_stmt c state stmt handle_funcall = - let result = match stmt.skind with - Instr(il) -> - let state = ref state in - let new_il = List.map (fun i -> - if debug then begin - ignore (Pretty.printf "Instr %a@!" d_instr i ) - end ; - match i with - | Set(l,e,loc) -> - let e', state' = S.assign !state l e in - state := state' ; - [Set(l,e',loc)] - | Call(lo,(Lval(Var(vi),NoOffset)),al,loc) -> - let result = begin - try - let fd = C.fundec_of_varinfo c vi in - begin - match fd.sbody.bstmts with - [] -> [] (* no point in making this call *) - | hd :: tl -> - let al', state' = S.call !state fd al in - handle_funcall stmt hd state' ; - let state'' = S.return state' fd in - state := state'' ; - [Call(lo,(Lval(Var(vi),NoOffset)),al',loc)] - end - with e -> - let state'' = S.call_to_unknown_function !state in - let al' = List.map (S.evaluate !state) al in - state := state'' ; - [Call(lo,(Lval(Var(vi),NoOffset)),al',loc)] - end in - (* handle return value *) - begin - match lo with - Some(lv) -> state := S.unassign !state lv - | _ -> () - end ; - result - | Call(lo,f,al,loc) -> - let al' = List.map (S.evaluate !state) al in - state := S.call_to_unknown_function !state ; - (match lo with - Some(lv) -> state := S.unassign !state lv - | None -> ()) ; - [Call(lo,f,al',loc)] - | Asm(_) -> state := S.assembly !state i ; [i] - ) il in - stmt.skind <- Instr(List.flatten new_il) ; - if debug then begin - ignore (Pretty.printf "New Stmt is %a@!" d_stmt stmt) ; - end ; - !state - - | If(e,b1,b2,loc) -> - let e' = S.evaluate state e in - (* Pretty.printf "%a evals to %a\n" d_exp e d_exp e' ; *) - - (* helper function to remove an IF branch *) - let remove b remains = begin - changed_cfg := true ; - (match b.bstmts with - | [] -> () - | hd :: tl -> - stmt.succs <- List.filter (fun succ -> succ.sid <> hd.sid) - stmt.succs - ) - end in - - if (e' = one) then begin - if b2.bstmts = [] && b2.battrs = [] then begin - stmt.skind <- Block(b1) ; - match b1.bstmts with - [] -> failwith "partial: completely empty if" - | hd :: tl -> stmt.succs <- [hd] - end else - stmt.skind <- Block( - { bstmts = - [ mkStmt (Block(b1)) ; - mkStmt (If(zero,b2,{bstmts=[];battrs=[];},loc)) ] ; - battrs = [] } ) ; - remove b2 b1 ; - state - end else if (e' = zero) then begin - if b1.bstmts = [] && b1.battrs = [] then begin - stmt.skind <- Block(b2) ; - match b2.bstmts with - [] -> failwith "partial: completely empty if" - | hd :: tl -> stmt.succs <- [hd] - end else - stmt.skind <- Block( - { bstmts = - [ mkStmt (Block(b2)) ; - mkStmt (If(zero,b1,{bstmts=[];battrs=[];},loc)) ] ; - battrs = [] } ) ; - remove b1 b2 ; - state - end else begin - stmt.skind <- If(e',b1,b2,loc) ; - state - end - - | Return(Some(e),loc) -> - let e' = S.evaluate state e in - stmt.skind <- Return(Some(e'),loc) ; - state - - | Block(b) -> - if debug && List.length stmt.succs > 1 then begin - ignore (Pretty.printf "(%a) has successors [%a]@!" - d_stmt stmt - (docList ~sep:(chr '@') (d_stmt ())) - stmt.succs) - end ; - state - - | _ -> state - in result - - (* - * This is the main conceptual entry-point for the partial evaluation - * data-flow functor. - *) - let dataflow (file : Cil.file) (* whole program *) - (c : C.t) (* control-flow graph *) - (initial_state : S.t) (* any assumptions? *) - (initial_stmt : Cil.stmt) (* entry point *) - = begin - (* count the total number of statements in the program *) - let num_stmts = ref 1 in - iterGlobals file (fun g -> match g with - GFun(fd,_) -> begin - match fd.smaxstmtid with - Some(i) -> if i > !num_stmts then num_stmts := i - | None -> () - end - | _ -> () - ) ; - (if debug then - Printf.printf "Dataflow: at most %d statements in program\n" !num_stmts); - - (* create a priority queue in which to store statements *) - let worklist = Heap.create !num_stmts in - - let finished = ref false in - let passes = ref 0 in - - (* add something to the work queue *) - let enqueue caller callee state = begin - let si = get_sinfo callee in - Hashtbl.replace si.incoming_state caller.sid state ; - Heap.insert worklist si.priority callee - end in - - (* we will be finished when we complete a round of data-flow that - * does not change the ICFG *) - while not !finished do - clear_sinfo () ; - incr passes ; - - (* we must recompute the ordering and the predecessor information - * because we may have changed it by removing IFs *) - (if debug then Printf.printf "Dataflow: Topological Sorting & Reachability\n" ); - toposort c initial_stmt ; - - let initial_si = get_sinfo initial_stmt in - Heap.insert worklist initial_si.priority initial_stmt ; - - while not (Heap.is_empty worklist) do - let (p,s) = Heap.extract_max worklist in - if debug then begin - ignore (Pretty.printf "Working on stmt %d (%a) %a@!" - s.sid - (docList ~sep:(chr ',' ++ break) (fun s -> dprintf "%d" s.sid)) - s.succs - d_stmt s) ; - flush stdout ; - end ; - let si = get_sinfo s in - - (* Even though this stmt is on the worklist, we may not have - * to do anything with it if the join of all of the incoming - * states is the same as the last state we used here. *) - let must_recompute, incoming_state = - begin - let list_of_incoming_states = ref [] in - Hashtbl.iter (fun true_pred_sid b -> - let this_pred_state = - try - Hashtbl.find si.incoming_state true_pred_sid - with _ -> - (* this occurs when we're evaluating a statement and we - * have not yet evaluated all of its predecessors (the - * first time we look at a loop head, say). We must be - * conservative. We'll come back later with better - * information (as we work toward the fix-point). *) - S.empty - in - if debug then begin - Printf.printf " Incoming State from %d\n" true_pred_sid ; - S.debug this_pred_state ; - flush stdout ; - end ; - list_of_incoming_states := this_pred_state :: - !list_of_incoming_states - ) si.reachable_preds ; - let merged_incoming_state = - if !list_of_incoming_states = [] then - (* this occurs when we're looking at the first statement - * in "main" -- it has no preds *) - initial_state - else - S.join !list_of_incoming_states - in - if debug then begin - Printf.printf " Merged State:\n" ; - S.debug merged_incoming_state ; - flush stdout ; - end ; - let must_recompute = match si.last_used_state with - None -> true - | Some(last) -> not (S.equal merged_incoming_state last) - in must_recompute, merged_incoming_state - end - in - if must_recompute then begin - si.last_used_state <- Some(incoming_state) ; - let outgoing_state = - (* partially evaluate and optimize the statement *) - partial_stmt c incoming_state s enqueue in - let fresh_succs = s.succs in - (* touch every successor so that we will reconsider it *) - List.iter (fun succ -> - enqueue s succ outgoing_state - ) fresh_succs ; - end else begin - if debug then begin - Printf.printf "No need to recompute.\n" - end - end - done ; - (if debug then Printf.printf "Dataflow: Pass %d Complete\n" !passes) ; - if !changed_cfg then begin - (if debug then Printf.printf "Dataflow: Restarting (CFG Changed)\n") ; - changed_cfg := false - end else - finished := true - done ; - (if debug then Printf.printf "Dataflow: Completed (%d passes)\n" !passes) - - end - - let simplify file c fd (assumptions : (Cil.lval * Cil.exp) list) = - let starting_state = List.fold_left (fun s (l,e) -> - let e',s' = S.assign s l e in - s' - ) S.empty assumptions in - dataflow file c starting_state (List.hd fd.sbody.bstmts) - - end - - -(* - * Currently our partial-eval optimizer is built out of basically nothing. - * The alias analysis is fake, the call grpah is cheap, and we're using - * George's old basic-block symex. Still, it works. - *) -(* Don't you love Functor application? *) -module BasicCallGraph = EasyCallGraph(EasyAlias) -module BasicSymex = NeculaFolding(EasyAlias) -module BasicPartial = MakePartial(BasicSymex)(BasicCallGraph)(EasyAlias) - -(* - * A very easy entry-point to partial evaluation/symbolic execution. - * You pass the Cil file and a list of assumptions (lvalue, exp pairs that - * should be treated as assignments that occur before the program starts). - * - * We partially evaluate and optimize starting from "main". The Cil.file - * is modified in place. - *) -let partial (f : Cil.file) (assumptions : (Cil.lval * Cil.exp) list) = - try - find_all_functions_with_address_taken f ; - let c = BasicCallGraph.compute f in - try - iterGlobals f (fun g -> match g with - GFun(fd,_) when fd.svar.vname = "main" -> - BasicPartial.simplify f c fd assumptions - | _ -> ()) ; - with e -> begin - Printf.printf "Error in DataFlow: %s\n" (Printexc.to_string e) ; - raise e - end - with e -> begin - Printf.printf "Error in Partial: %s\n" (Printexc.to_string e) ; - raise e - end - -let feature : featureDescr = - { fd_name = "partial"; - fd_enabled = Cilutil.doPartial; - fd_description = "interprocedural partial evaluation and constant folding" ; - fd_extraopt = []; - fd_doit = (function (f: file) -> - if not !Cilutil.makeCFG then begin - Errormsg.s (Errormsg.error "--dopartial: you must also specify --domakeCFG\n") - end ; - partial f [] ) ; - fd_post_check = false; - } - -(* - * - * Copyright (c) 2001-2002, - * George C. Necula <necula@cs.berkeley.edu> - * Scott McPeak <smcpeak@cs.berkeley.edu> - * Wes Weimer <weimer@cs.berkeley.edu> - * 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. - * - *) |