(* * * Copyright (c) 2001-2002, * George C. Necula * Scott McPeak * Wes Weimer * 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. * *) (** Compute dominator information for the statements in a function *) open Cil open Pretty module E = Errormsg module H = Hashtbl module U = Util module IH = Inthash module DF = Dataflow let debug = false (* For each statement we maintain a set of statements that dominate it *) module BS = Set.Make(struct type t = Cil.stmt let compare v1 v2 = Pervasives.compare v1.sid v2.sid end) (** Customization module for dominators *) module DT = struct let name = "dom" let debug = ref debug type t = BS.t (** For each statement in a function we keep the set of dominator blocks. * Indexed by statement id *) let stmtStartData: t IH.t = IH.create 17 let copy (d: t) = d let pretty () (d: t) = dprintf "{%a}" (docList (fun s -> dprintf "%d" s.sid)) (BS.elements d) let computeFirstPredecessor (s: stmt) (d: BS.t) : BS.t = (* Make sure we add this block to the set *) BS.add s d let combinePredecessors (s: stmt) ~(old: BS.t) (d: BS.t) : BS.t option = (* First, add this block to the data from the predecessor *) let d' = BS.add s d in if BS.subset old d' then None else Some (BS.inter old d') let doInstr (i: instr) (d: t) = DF.Default let doStmt (s: stmt) (d: t) = DF.SDefault let doGuard condition _ = DF.GDefault let filterStmt _ = true end module Dom = DF.ForwardsDataFlow(DT) let getStmtDominators (data: BS.t IH.t) (s: stmt) : BS.t = try IH.find data s.sid with Not_found -> BS.empty (* Not reachable *) let getIdom (idomInfo: stmt option IH.t) (s: stmt) = try IH.find idomInfo s.sid with Not_found -> E.s (E.bug "Immediate dominator information not set for statement %d" s.sid) (** Check whether one block dominates another. This assumes that the "idom" * field has been computed. *) let rec dominates (idomInfo: stmt option IH.t) (s1: stmt) (s2: stmt) = s1 == s2 || (let s2idom = getIdom idomInfo s2 in match s2idom with None -> false | Some s2idom -> dominates idomInfo s1 s2idom) let computeIDom ?(doCFG:bool=true) (f: fundec) : stmt option IH.t = (* We must prepare the CFG info first *) if doCFG then begin prepareCFG f; computeCFGInfo f false end; IH.clear DT.stmtStartData; let idomData: stmt option IH.t = IH.create 13 in let _ = match f.sbody.bstmts with [] -> () (* function has no body *) | start :: _ -> begin (* We start with only the start block *) IH.add DT.stmtStartData start.sid (BS.singleton start); Dom.compute [start]; (* Dump the dominators information *) if debug then List.iter (fun s -> let sdoms = getStmtDominators DT.stmtStartData s in if not (BS.mem s sdoms) then begin (* It can be that the block is not reachable *) if s.preds <> [] then E.s (E.bug "Statement %d is not in its list of dominators" s.sid); end; ignore (E.log "Dominators for %d: %a\n" s.sid DT.pretty (BS.remove s sdoms))) f.sallstmts; (* Now fill the immediate dominators for all nodes *) let rec fillOneIdom (s: stmt) = try ignore (IH.find idomData s.sid) (* Already set *) with Not_found -> begin (* Get the dominators *) let sdoms = getStmtDominators DT.stmtStartData s in (* Fill the idom for the dominators first *) let idom = BS.fold (fun d (sofar: stmt option) -> if d.sid = s.sid then sofar (* Ignore the block itself *) else begin (* fill the idom information recursively *) fillOneIdom d; match sofar with None -> Some d | Some sofar' -> (* See if d is dominated by sofar. We know that the * idom information has been computed for both sofar * and for d*) if dominates idomData sofar' d then Some d else sofar end) sdoms None in IH.replace idomData s.sid idom end in (* Scan all blocks and compute the idom *) List.iter fillOneIdom f.sallstmts end in idomData type tree = stmt option * BS.t IH.t (* returns the IDoms and a map from statement ids to the set of statements that are dominated *) let computeDomTree ?(doCFG:bool=true) (f: fundec) : stmt option IH.t * tree = (* We must prepare the CFG info first *) if doCFG then begin prepareCFG f; computeCFGInfo f false end; IH.clear DT.stmtStartData; let treeData: BS.t IH.t = IH.create 64 in let idomData: stmt option IH.t = IH.create 64 in let _ = match f.sbody.bstmts with [] -> () (* function has no body *) | start :: _ -> begin (* We start with only the start block *) IH.add DT.stmtStartData start.sid (BS.singleton start); Dom.compute [start]; (* Dump the dominators information *) if debug then List.iter (fun s -> let sdoms = getStmtDominators DT.stmtStartData s in if not (BS.mem s sdoms) then begin (* It can be that the block is not reachable *) if s.preds <> [] then E.s (E.bug "Statement %d is not in its list of dominators" s.sid); end; ignore (E.log "Dominators for %d: %a\n" s.sid DT.pretty (BS.remove s sdoms))) f.sallstmts; (* Now fill the immediate dominators for all nodes *) let rec fillOneIdom (s: stmt) = try ignore (IH.find idomData s.sid) (* Already set *) with Not_found -> begin (* Get the dominators *) let sdoms = getStmtDominators DT.stmtStartData s in (* Fill the idom for the dominators first *) let idom = BS.fold (fun d (sofar: stmt option) -> if d.sid = s.sid then sofar (* Ignore the block itself *) else begin (* fill the idom information recursively *) fillOneIdom d; match sofar with None -> Some d | Some sofar' -> (* See if d is dominated by sofar. We know that the * idom information has been computed for both sofar * and for d*) if dominates idomData sofar' d then Some d else sofar end) sdoms None in IH.replace idomData s.sid idom; match idom with | None -> () | Some d -> begin match IH.tryfind treeData d.sid with | None -> IH.add treeData d.sid (BS.singleton s) | Some bs -> IH.replace treeData d.sid (BS.add s bs) end end in (* Scan all blocks and compute the idom *) List.iter fillOneIdom f.sallstmts end in try idomData, (Some(List.hd f.sbody.bstmts), treeData) with Failure "hd" -> idomData, (None, treeData) type order = PreOrder | PostOrder let rec domTreeIter (f: stmt -> unit) (o : order) (t: tree) : unit = let doChildren s = match IH.tryfind (snd t) s.sid with | None -> () (* No children *) | Some bs -> begin BS.iter (fun s -> domTreeIter f o (Some s, snd t)) bs end in match fst t with | None -> () | Some s -> begin (* s is the current root *) match o with | PreOrder -> begin (* do s first *) f s; doChildren s end | PostOrder -> begin (* do s's children first *) doChildren s; f s end end let children (t: tree) (s: stmt) : stmt list = match IH.tryfind (snd t) s.sid with | None -> [] | Some bs -> BS.elements bs (** Compute the start of the natural loops. For each start, keep a list of * origin of a back edge. The loop consists of the loop start and all * predecessors of the origins of back edges, up to and including the loop * start *) let findNaturalLoops (f: fundec) (idomData: stmt option IH.t) : (stmt * stmt list) list = let loops = List.fold_left (fun acc b -> (* Iterate over all successors, and see if they are among the * dominators for this block *) List.fold_left (fun acc s -> if dominates idomData s b then (* s is the start of a natural loop *) let rec addNaturalLoop = function [] -> [(s, [b])] | (s', backs) :: rest when s'.sid = s.sid -> (s', b :: backs) :: rest | l :: rest -> l :: addNaturalLoop rest in addNaturalLoop acc else acc) acc b.succs) [] f.sallstmts in if debug then ignore (E.log "Natural loops:\n%a\n" (docList ~sep:line (fun (s, backs) -> dprintf " Start: %d, backs:%a" s.sid (docList (fun b -> num b.sid)) backs)) loops); loops