diff --git a/src/plugins/value/partitioning/auto_loop_unroll.ml b/src/plugins/value/partitioning/auto_loop_unroll.ml index 1096943125a6d635932321a6f89bbbcbf3c8aa55..f044dbdac75c7da0746d9f5782d2faf15df915cd 100644 --- a/src/plugins/value/partitioning/auto_loop_unroll.ml +++ b/src/plugins/value/partitioning/auto_loop_unroll.ml @@ -90,22 +90,15 @@ module Graph = struct in find (get_wto kf) - (* Applies [f acc instr] to all instructions [instr] in the [loop]. - If [backward] is set to true, instructions are browsed in backward order. - If [inner_loop] is set to false, inner loops are ignored. *) - let fold_instr ?(backward=false) ?(inner_loop=true) f loop acc = + (* Applies [f acc instr] to all instructions [instr] in the [loop]. *) + let fold_instr f loop acc = let transfer (_v1, edge, _v2) acc = match edge.edge_transition with | Instr (instr, _stmt) -> f acc instr | _ -> acc in let compute_vertex = G.fold_pred_e transfer loop.graph in - let wto = - if inner_loop - then Wto.flatten loop.wto - else List.filter_map (function Wto.Node v -> Some v | _ -> None) loop.wto - in - let wto = if backward then List.rev wto else wto in + let wto = Wto.flatten loop.wto in List.fold_left (fun acc vertex -> compute_vertex vertex acc) acc wto (* Results for the simple dataflow analysis performed by [compute] below. *) @@ -115,14 +108,16 @@ module Graph = struct Starts with the value [init_value] at the head of the loop [loop.head], applies [transfer] to compute values through transitions in the loop body, and use [join] to merge values coming from different paths. + If [backward] is set to [true], transitions are browsed in backward order. [transfer] has an argument [~inner_loop], which is true in inner loops. Returns the value computed for the loop head after one iteration of the loop. Paths outside the loop body are ignored. *) - let compute loop transfer join init_value = + let compute ?(backward=false) loop transfer join init_value = let results = Results.create (G.nb_vertex loop.graph) in Results.add results loop.head init_value; - let transfer_edge ~inner_loop (v1, edge, _v2) acc = - match Results.find_opt results v1 with + let transfer_edge ~inner_loop (v1, edge, v2) acc = + let v = if backward then v2 else v1 in + match Results.find_opt results v with | None -> acc | Some value -> let value = transfer ~inner_loop value edge.edge_transition in @@ -130,19 +125,21 @@ module Graph = struct | None -> Some value | Some acc -> Some (join acc value) in - let compute_vertex ~inner_loop vertex = - G.fold_pred_e (transfer_edge ~inner_loop) loop.graph vertex None + let compute_vertex = + let fold = if backward then G.fold_succ_e else G.fold_pred_e in + fun ~inner_loop vertex -> + fold (transfer_edge ~inner_loop) loop.graph vertex None in let process_vertex ~inner_loop vertex = Option.iter (Results.add results vertex) (compute_vertex ~inner_loop vertex) in + let sort = if backward then List.rev else fun x -> x in let rec iterate ~inner_loop = function | Wto.Node v -> process_vertex ~inner_loop v | Wto.Component (v, w) -> - process_vertex ~inner_loop v; - List.iter (iterate ~inner_loop:true) w; + List.iter (iterate ~inner_loop:true) (sort (Wto.Node v :: w)); in - List.iter (iterate ~inner_loop:false) loop.wto; + List.iter (iterate ~inner_loop:false) (sort loop.wto); compute_vertex ~inner_loop:false loop.head (* A loop exit condition is an expression and a boolean expression whether the @@ -285,29 +282,47 @@ let find_lonely_candidate eval_ptr loop_effect expr = in aux None lvalues -(* If in the [loop], [lval] is assigned once to the value of another +(* Builds a transfer function suitable for [Graph.compute] that does nothing, + except on assignemnts of [lval]: + - to the value of an expression [expr], it applies [f expr acc]; + - to a function call, or if [inner_loop] is true, it raises [exn]. *) +let transfer_assign lval exn f ~inner_loop acc instr = + let is_lval = Cil_datatype.LvalStructEq.equal lval in + let transfer_instr ~inner_loop acc = function + | Set (lv, expr, _loc) when is_lval lv -> + if inner_loop then raise exn else f expr acc + | Local_init (vi, AssignInit (SingleInit expr), _loc) + when is_lval (Cil.var vi) && not inner_loop -> + f expr acc + | Local_init (vi, _, _) when is_lval (Cil.var vi) -> raise exn + | Call (Some lv, _, _, _) when is_lval lv -> raise exn + | _ -> acc + in + match instr with + | Interpreted_automata.Instr (instr, _stmt) -> + transfer_instr ~inner_loop acc instr + | _ -> acc + +(* If in the [loop], [lval] is always assigned to the value of another lvalue, returns this new lvalue. Otherwise, returns [lval]. *) let cross_equality loop lval = (* If no such single equality can be found, return [lval] unchanged. *) let exception No_equality in - let rec find_lval expr = + let rec find_lval expr x = match expr.enode with | Lval lval -> lval - | Info (e, _) -> find_lval e + | Info (e, _) -> find_lval e x | _ -> raise No_equality in - let cross_instr lval = function - | Set (lv, expr, _loc) when Cil_datatype.LvalStructEq.equal lv lval -> - find_lval expr - | Local_init (varinfo, AssignInit (SingleInit expr), _loc) - when Cil_datatype.LvalStructEq.equal (Cil.var varinfo) lval -> - find_lval expr - | Call (Some lv, _, _, _) when Cil_datatype.LvalStructEq.equal lval lv -> - raise No_equality - | _ -> lval + let transfer ~inner_loop lval instr = + transfer_assign lval No_equality find_lval ~inner_loop lval instr in - try Graph.fold_instr ~backward:true ~inner_loop:false cross_instr loop lval - with No_equality -> lval + let join lv1 lv2 = + if Cil_datatype.LvalStructEq.equal lv1 lv2 then lv1 else raise No_equality + in + match Graph.compute ~backward:true loop transfer join lval with + | Some lval -> lval + | None | exception No_equality -> lval module Make (Abstract: Abstractions.Eva) = struct @@ -344,7 +359,7 @@ module Make (Abstract: Abstractions.Eva) = struct (* Adds to [acc] the increment from the assignement of [lval] to the value of [expr]. Raises NoIncrement if this is not an increment of [lval]. *) - let rec delta_assign lval acc expr = + let rec delta_assign lval expr acc = (* Is the expression [e] equal to the lvalue [lval] (modulo cast)? *) let rec is_lval e = match e.enode with | Lval lv -> Cil_datatype.LvalStructEq.equal lval lv @@ -364,33 +379,17 @@ module Make (Abstract: Abstractions.Eva) = struct else if is_lval e2 && binop = PlusA then add_to_delta binop acc e1 else raise NoIncrement - | CastE (typ, e) when Cil.isIntegralType typ -> delta_assign lval acc e - | Info (e, _) -> delta_assign lval acc e + | CastE (typ, e) when Cil.isIntegralType typ -> delta_assign lval e acc + | Info (e, _) -> delta_assign lval e acc | _ -> raise NoIncrement - let delta_instruction ~inner_loop lval acc = function - | Set (lv, expr, _loc) -> - if Cil_datatype.LvalStructEq.equal lval lv - then if inner_loop then raise NoIncrement else delta_assign lval acc expr - else acc - | Call (Some lv, _, _, _) -> - if Cil_datatype.LvalStructEq.equal lval lv - then raise NoIncrement (* No increment can be computed for a call. *) - else acc - | Call (None, _, _, _) | Local_init _ | Skip _ | Code_annot _ -> acc - | Asm _ -> raise NoIncrement - (* Computes an over-approximation of the increment of [lval] in the [loop]. Only syntactic assignments of [lval] are considered, so [lval] should be a direct access to a variable whose address is not taken, and which should not be global if the loop contains function calls. Returns None if no increment can be computed. *) let compute_delta lval loop = - let transfer ~inner_loop value = function - | Interpreted_automata.Instr (instr, _stmt) -> - delta_instruction ~inner_loop lval value instr - | _ -> value - in + let transfer = transfer_assign lval NoIncrement (delta_assign lval) in let join t1 t2 = { value = Bottom.join Val.join t1.value t2.value; delta = Bottom.join Val.join t1.delta t2.delta; }