Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Loop Unrolling for the first exp.unrolling-factor Iterations #563

Merged
merged 26 commits into from
Mar 15, 2022
Merged
Changes from 1 commit
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
acb0b7e
loop unrolling option implemented
mikcp Jan 21, 2022
44def90
Merge branch 'master' into unrolling
mikcp Jan 21, 2022
84d94f2
Comment explaining reason behind replicated stmts
mikcp Jan 25, 2022
7456571
add_label_to_remainder_loop does not remove previous labels of st_loo…
mikcp Jan 25, 2022
29bc829
is_remainder_loop recurses all labels of the stmt
mikcp Jan 25, 2022
26b97ab
redundant check removed
mikcp Jan 25, 2022
e37c29d
by calling the unrolling visitor after preparedCFG, breaks and contin…
mikcp Jan 25, 2022
8a7fc15
basic tests added
mikcp Jan 26, 2022
bdbb726
test to add continue support failed
mikcp Jan 28, 2022
6643236
support for continue instructions added
mikcp Jan 31, 2022
db5da87
Revert "by calling the unrolling visitor after preparedCFG, breaks an…
mikcp Jan 31, 2022
b6f3fd7
Merge branch 'old-version-unrolling' into unrolling
mikcp Jan 31, 2022
99b382e
extra tests deleted
mikcp Jan 31, 2022
5e03d86
Merge branch 'master' into unrolling
michael-schwarz Feb 16, 2022
62a161d
Merge branch 'master' into unrolling
michael-schwarz Mar 12, 2022
72fab58
Add failing example
michael-schwarz Mar 12, 2022
bf5a3fa
Some clenaup
michael-schwarz Mar 12, 2022
10f7e8c
fix example
michael-schwarz Mar 12, 2022
930fae8
Alternative loop unrolling
michael-schwarz Mar 12, 2022
ac9ed15
fix comments
michael-schwarz Mar 12, 2022
4975cf1
fix comments
michael-schwarz Mar 12, 2022
44b162f
add failing test
michael-schwarz Mar 14, 2022
29b1714
One last(?) rewrite
michael-schwarz Mar 14, 2022
cdc512c
typo
michael-schwarz Mar 14, 2022
3acce11
Make Hashtable for patching gotos less fragile
michael-schwarz Mar 15, 2022
3f6e3b8
add test with unrolled array
michael-schwarz Mar 15, 2022
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
96 changes: 35 additions & 61 deletions src/util/cilfacade.ml
Original file line number Diff line number Diff line change
Expand Up @@ -80,71 +80,45 @@ let end_basic_blocks f =
let thisVisitor = new allBBVisitor in
visitCilFileSameGlobals thisVisitor f

class removeLabelsVisitor = object
inherit nopCilVisitor

val nestingDepth = ref 0
michael-schwarz marked this conversation as resolved.
Show resolved Hide resolved

method! vstmt s =
let new_labels = List.filter_map (function Label(str,loc,b) -> None | x -> Some x) s.labels in
match s.skind with
| Loop _ -> nestingDepth := !nestingDepth+1; ChangeDoChildrenPost({s with labels = new_labels}, fun x -> nestingDepth := !nestingDepth-1; x)
| Continue loc -> if !nestingDepth = 0 then
ChangeTo({s with labels = new_labels; skind = Break loc})
michael-schwarz marked this conversation as resolved.
Show resolved Hide resolved
else
ChangeTo({s with labels = new_labels})
| _ -> ChangeDoChildrenPost({s with labels = new_labels}, Fun.id)
end

class loopUnrollingVisitor = object
(* Labels are simply handled by removing them. It is always correct to simply jump into the non-unrolled part of the loop! *)
(* The price we need to pay for this simpler handling of labels and still wanting to benefit from unrolling nested loops is that *)
(* we need to put our unrolled code into fake while(1) loops such that breaks have something to break out of, add artificial breaks *)
(* at the end of loops, and replacing (top level!) continues with breaks. *)
(* This is still a lot more intuitive than complicated handling of gotos as would be required when doing it after the unrolling *)
inherit nopCilVisitor

method! vstmt s =
(*
All the duplicated stmts need to be re-created or the sid will fail, so we create a copy of st, which is physically unequal to it.
A new sid is computed later (as seen in http://goblint.in.tum.de/assets/goblint-cil/api/Cil.html#TYPEstmt), as long as we perform this copies.
*)
let newsid st = { st with sid = st.sid } in
let rec newsid_stmt st =
let mkb stml = mkBlock (List.map newsid_stmt stml) in
match st.skind with
| Instr _ -> newsid st
| If(e,b1,b2,l1,l2) -> mkStmt(If(e,(mkb b1.bstmts),(mkb b2.bstmts),l1,l2))
| Loop(bl,l1,l2,s1,s2) ->
let create_stmt = mkStmt(Loop((mkb bl.bstmts),l1,l2,s1,s2)) in
create_stmt.labels <- st.labels;
create_stmt
| Block(bl) -> mkStmt(Block(mkb bl.bstmts))
| Switch(e,bl,stl,l1,l2) -> mkStmt(Switch(e,(mkb bl.bstmts),(List.map newsid_stmt stl),l1,l2))
| _ -> newsid st in
let create_shallow_copies bl = List.map newsid_stmt bl in
let labelsRemover = new removeLabelsVisitor in
michael-schwarz marked this conversation as resolved.
Show resolved Hide resolved
match s.skind with
| Loop(b, loc, _, _, _) ->
let get_unrolling_factor = GobConfig.get_int "exp.unrolling-factor" in
(* All unrollings will leave the original loop at the end. The label makes sure it's not unrolled twice.*)
let rec is_remainder_loop loop_stmt_labels =
match loop_stmt_labels with
| Label(lab,_,_)::tl -> if BatString.starts_with lab "remainder_loop" then true else is_remainder_loop tl
| _ -> false in
(* Because this is executed before preparedCFG, there are still Breaks instead of Gotos.*)
(* We need to transform them so we can replicate the loop's body outside of the loop.*)
let break_stmt = mkStmt (Instr []) in
break_stmt.labels <- [Label(Cil.freshLabel "unroll_while_break",loc,false)] ;
let continue_stmt = mkStmt (Instr []) in
continue_stmt.labels <- [Label(Cil.freshLabel "unroll_while_continue",loc,false)] ;
let rec rm_breaks_st st =
let rm_breaks_st_list stl = mkBlock (List.map rm_breaks_st stl) in
match st.skind with
| Break(l) -> mkStmt (Goto (ref break_stmt, loc))
| Continue(l) -> mkStmt (Goto (ref continue_stmt, loc))
| If(e,tb,fb,l1,l2) -> mkStmt (If (e,(rm_breaks_st_list tb.bstmts), (rm_breaks_st_list fb.bstmts),l1,l2))
| Block(bl) -> mkStmt (Block (rm_breaks_st_list bl.bstmts))
| Switch(e,bl,stl,l1,l2) -> mkStmt (Switch (e, (rm_breaks_st_list bl.bstmts), (List.map rm_breaks_st stl),l1, l2))
| _ -> st in
let body = List.map rm_breaks_st b.bstmts in
(* Prepare remainder loop for unrolling*)
let prepare_remainder_loop st_loop loc_loop =
st_loop.labels <- (Label(Cil.freshLabel "remainder_loop", loc_loop, false))::st_loop.labels;
mkStmt (Block (mkBlock([continue_stmt;st_loop]))) in
(* Unrolling *)
let rec unroll sl factor =
match factor with
|0-> sl
|_ ->
let duplicate_body bd = create_shallow_copies bd in
let x = (duplicate_body body) @ sl in
unroll x (factor-1) in
let unroll_helper st =
let x = unroll [(prepare_remainder_loop st loc)] get_unrolling_factor in
mkStmt (Block (mkBlock (x @ [break_stmt]))) in
let is_loop_unrollable s = not @@ is_remainder_loop s.labels in
let check_type_loop =
if is_loop_unrollable s then ChangeDoChildrenPost ((unroll_helper s), fun x -> x)
else DoChildren in
check_type_loop
| Loop (b,loc, loc2, break , continue) ->
let duplicate_and_rem_labels s =
match s.skind with
| Loop (b,loc, loc2, break , continue) ->
let s = { s with sid = s.sid } in
michael-schwarz marked this conversation as resolved.
Show resolved Hide resolved
let factor = GobConfig.get_int "exp.unrolling-factor" in
let bstmts = b.bstmts @ [mkStmt @@ Break loc] in
let copies = List.init (factor) (fun _ -> visitCilStmt labelsRemover (mkStmt (Loop (mkBlock bstmts,loc,loc2,None,None)))) in
michael-schwarz marked this conversation as resolved.
Show resolved Hide resolved
mkStmt (Block (mkBlock (copies@[s])))
| _ -> failwith "invariant broken"
in
ChangeDoChildrenPost({s with sid = s.sid},duplicate_and_rem_labels)
| _ -> DoChildren
end

Expand Down