Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion src/common/framework/cfgTools.ml
Original file line number Diff line number Diff line change
Expand Up @@ -559,7 +559,16 @@ struct
| FunctionEntry _ -> ["shape=box"]
in
let styles = String.concat "," (label @ shape @ extraNodeStyles n) in
Format.fprintf out ("\t%a [%s];\n") p_node n styles
Format.fprintf out ("\t%a [%s];\n") p_node n styles;
match n with
| Statement s when get_bool "dbg.cfg.loop-unrolling" ->
begin match LoopUnrolling0.find_copyof s with
| Some s' ->
let n' = Statement s' in
Format.fprintf out "\t%a -> %a [style=dotted];\n" p_node n p_node n'
| None -> ()
end
| _ -> ()
end

let fprint_dot (module CfgPrinters: CfgPrinters) iter_edges out =
Expand Down
19 changes: 19 additions & 0 deletions src/common/util/loopUnrolling0.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
open GoblintCil

module CopyOfHashTable = Hashtbl.Make(struct
type t = stmt
(* Identity by physical equality. *)
let equal = (==)
(* Hash only labels and skind (statement itself) because they should remain unchanged between now
and lookup after analysis.
CFG construction modifies sid, succs, preds and fallthrough, which are empty here.*)
let hash (s: stmt) = Hashtbl.hash (s.skind, s.labels)
end)
let copyof = CopyOfHashTable.create 113

let find_copyof = CopyOfHashTable.find_opt copyof

let rec find_original s =
match find_copyof s with
| None -> s
| Some s' -> (find_original [@tailcall]) s'
6 changes: 6 additions & 0 deletions src/config/options.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1991,6 +1991,12 @@
"description": "Add loop SCC clusters to CFG .dot output.",
"type": "boolean",
"default": false
},
"loop-unrolling": {
"title": "dbg.cfg.loop-unrolling",
"description": "Add dotted loop unrolling copy-of edges to CFG .dot output.",
"type": "boolean",
"default": false
}
},
"additionalProperties": false
Expand Down
8 changes: 6 additions & 2 deletions src/util/loopUnrolling.ml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
(** Syntactic loop unrolling. *)

open GobConfig
open GoblintCil

module M = Messages

(*loop unroll heuristics*)
(*used if AutoTune is activated*)

Expand Down Expand Up @@ -384,6 +384,8 @@ class patchLabelsGotosVisitor(newtarget) = object
| _ -> DoChildren
end

include LoopUnrolling0

(*
Makes a copy, replacing top-level breaks with goto loopEnd and top-level continues with
goto currentIterationEnd
Expand All @@ -401,6 +403,8 @@ class copyandPatchLabelsVisitor(loopEnd, currentIterationEnd, gotos) = object
let new_labels = List.map (function Label(str,loc,b) -> Label (Cil.freshLabel str,loc,b) | x -> x) sn.labels in
(* this makes new physical copy*)
let new_s = {sn with labels = new_labels} in
CopyOfHashTable.replace copyof new_s s;
if M.tracing then M.trace "cfg" "Marking %a as copy of %a" CilType.Stmt.pretty new_s CilType.Stmt.pretty s;
if new_s.labels <> [] then
(* Use original s, ns might be temporay e.g. if the type of statement changed *)
(* record that goto s; appearing in the current fragment should later be patched to goto new_s *)
Expand Down
9 changes: 9 additions & 0 deletions src/util/loopUnrolling.mli
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
(** Syntactic loop unrolling. *)

val unroll_loops: GoblintCil.fundec -> int -> unit
(** Unroll loops in a function.

@param totalLoops total number of loops from autotuner *)

val find_original: GoblintCil.stmt -> GoblintCil.stmt
(** Find original un-unrolled instance of the statement. *)
13 changes: 13 additions & 0 deletions tests/regression/55-loop-unrolling/11-unrolled-loop-invariant.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
int main() {
int i = 0;
while (i < 10)
i++;

int j = 0, k = 0;
while (j < 10) {
while (k < 100)
k++;
j++;
}
return 0;
}
230 changes: 230 additions & 0 deletions tests/regression/55-loop-unrolling/11-unrolled-loop-invariant.t

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions tests/regression/55-loop-unrolling/dune
Original file line number Diff line number Diff line change
@@ -1,2 +1,8 @@
(cram
(deps (glob_files *.c)))

(cram
(applies_to 11-unrolled-loop-invariant)
(enabled_if %{bin-available:graph-easy})
(deps
%{bin:cfgDot}))
25 changes: 23 additions & 2 deletions tests/regression/cfg/util/cfgDot.ml
Original file line number Diff line number Diff line change
@@ -1,12 +1,31 @@
open Goblint_lib

let usage_msg = "cfgDot [--unroll <n>] <file>"

let files = ref []
let unroll = ref 0

let anon_fun filename =
files := filename :: !files

let speclist = [
("--unroll", Arg.Set_int unroll, "Unroll loops");
]

let main () =
Goblint_logs.Logs.Level.current := Info;
Cilfacade.init ();
GobConfig.set_bool "dbg.cfg.loop-unrolling" true;
GobConfig.set_int "exp.unrolling-factor" !unroll;

let ast = Cilfacade.getAST (Fpath.v Sys.argv.(1)) in
assert (List.length !files = 1);
let ast = Cilfacade.getAST (Fpath.v (List.hd !files)) in
CilCfg0.end_basic_blocks ast;
(* Part of CilCfg.createCFG *)
GoblintCil.iterGlobals ast (function
| GFun (fd, _) ->
if !unroll > 0 then
LoopUnrolling.unroll_loops fd (-1);
GoblintCil.prepareCFG fd;
GoblintCil.computeCFGInfo fd true
| _ -> ()
Expand Down Expand Up @@ -52,4 +71,6 @@ let main () =
| _ -> ()
)

let () = main ()
let () =
Arg.parse speclist anon_fun usage_msg;
main ()
1 change: 1 addition & 0 deletions tests/regression/cfg/util/dune
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
goblint-cil
goblint_logs
goblint_common
goblint_lib ; TODO: avoid: extract LoopUnrolling from goblint_lib
fpath
goblint.sites.dune
goblint.build-info.dune)
Expand Down