[sledge] Combine multiple passes in Llair.Func.mk, fix termination bug

Summary:
Llair.Func.mk makes two passes over the CFG to resolve block parents,
jump destinations, and eliminate jumps to jumps. This is not
economical, but more importantly the current code mistakenly uses the
`retreating` metadata before it is set correctly. This diff combines
these passes into a single one, which also incorporates setting the
retreating field from Llair.Program.mk.

This avoids nontermination on code that contains immediate self-jumps
such as `L: goto L;` that LLVM 11 can now generate.

Reviewed By: jvillard

Differential Revision: D27262512

fbshipit-source-id: 0543ba669
master
Josh Berdine 4 years ago committed by Facebook GitHub Bot
parent c531096f97
commit 45c155fdf1

@ -660,6 +660,9 @@ module Func = struct
let find name functions = let find name functions =
Function.Map.find (Function.counterfeit name) functions Function.Map.find (Function.counterfeit name) functions
let lookup cfg lbl =
Iter.find_exn (IArray.to_iter cfg) ~f:(fun k -> String.equal lbl k.lbl)
let mk ~name ~formals ~freturn ~fthrow ~entry ~cfg ~loc = let mk ~name ~formals ~freturn ~fthrow ~entry ~cfg ~loc =
let locals = let locals =
let locals_cmnd locals cmnd = let locals_cmnd locals cmnd =
@ -671,51 +674,45 @@ module Func = struct
IArray.fold ~f:locals_block cfg (locals_block entry Reg.Set.empty) IArray.fold ~f:locals_block cfg (locals_block entry Reg.Set.empty)
in in
let func = {name; formals; freturn; fthrow; locals; entry; loc} in let func = {name; formals; freturn; fthrow; locals; entry; loc} in
let resolve_parent_and_jumps block = let rec resolve_parent_and_jumps ancestors src =
block.parent <- func ; src.parent <- func ;
let lookup cfg lbl : block = let ancestors = Block_label.Set.add src ancestors in
Iter.find_exn (IArray.to_iter cfg) ~f:(fun k -> let jump jmp =
String.equal lbl k.lbl ) let dst = lookup cfg jmp.dst.lbl in
in if Block_label.Set.mem dst ancestors then (
let set_dst jmp = jmp.dst <- lookup cfg jmp.dst.lbl in jmp.dst <- dst ;
match block.term with jmp.retreating <- true ;
| Switch {tbl; els; _} -> jmp )
IArray.iter tbl ~f:(fun (_, jmp) -> set_dst jmp) ; else
set_dst els match resolve_parent_and_jumps ancestors dst with
| Iswitch {tbl; _} -> IArray.iter tbl ~f:set_dst | None ->
| Call {return; throw; _} | ICall {return; throw; _} -> jmp.dst <- dst ;
set_dst return ; jmp
Option.iter throw ~f:set_dst | Some tgt ->
| Return _ | Throw _ | Unreachable -> ()
in
let elim_jumps_to_jumps block =
let rec find_dst retreating jmp =
match jmp.dst.term with
| Switch {tbl; els; _}
when IArray.is_empty tbl && IArray.is_empty jmp.dst.cmnd ->
find_dst (retreating || els.retreating) els
| _ -> jmp
in
let set_dst jmp =
let tgt = find_dst jmp.retreating jmp in
if tgt != jmp then (
jmp.dst <- tgt.dst ; jmp.dst <- tgt.dst ;
jmp.retreating <- tgt.retreating ) jmp.retreating <- tgt.retreating ;
tgt
in in
match block.term with let jump' jmp = ignore (jump jmp) in
match src.term with
| Switch {tbl; els; _} -> | Switch {tbl; els; _} ->
IArray.iter tbl ~f:(fun (_, jmp) -> set_dst jmp) ; IArray.iter ~f:(fun (_, jmp) -> jump' jmp) tbl ;
set_dst els let tgt = jump els in
| Iswitch {tbl; _} -> IArray.iter tbl ~f:set_dst if IArray.is_empty tbl && IArray.is_empty src.cmnd then Some tgt
else None
| Iswitch {tbl; _} ->
IArray.iter ~f:jump' tbl ;
None
| Call {return; throw; _} | ICall {return; throw; _} -> | Call {return; throw; _} | ICall {return; throw; _} ->
set_dst return ; jump' return ;
Option.iter throw ~f:set_dst Option.iter ~f:jump' throw ;
| Return _ | Throw _ | Unreachable -> () None
| Return _ | Throw _ | Unreachable -> None
in
let resolve_parent_and_jumps block =
ignore (resolve_parent_and_jumps Block_label.Set.empty block)
in in
resolve_parent_and_jumps entry ; resolve_parent_and_jumps entry ;
IArray.iter cfg ~f:resolve_parent_and_jumps ;
elim_jumps_to_jumps entry ;
IArray.iter cfg ~f:elim_jumps_to_jumps ;
func |> check invariant func |> check invariant
end end
@ -738,14 +735,12 @@ let set_derived_metadata functions =
in in
let topsort roots = let topsort roots =
let tips_to_roots = BlockQ.create () in let tips_to_roots = BlockQ.create () in
let rec visit ancestors func src = let rec visit ancestors src =
if BlockQ.mem tips_to_roots src then () if BlockQ.mem tips_to_roots src then ()
else else
let ancestors = Block_label.Set.add src ancestors in let ancestors = Block_label.Set.add src ancestors in
let jump jmp = let jump jmp =
if Block_label.Set.mem jmp.dst ancestors then if jmp.retreating then () else visit ancestors jmp.dst
jmp.retreating <- true
else visit ancestors func jmp.dst
in in
( match src.term with ( match src.term with
| Switch {tbl; els; _} -> | Switch {tbl; els; _} ->
@ -755,7 +750,7 @@ let set_derived_metadata functions =
| Call ({callee; return; throw; _} as cal) -> | Call ({callee; return; throw; _} as cal) ->
if Block_label.Set.mem callee.entry ancestors then if Block_label.Set.mem callee.entry ancestors then
cal.recursive <- true cal.recursive <- true
else visit ancestors func callee.entry ; else visit ancestors callee.entry ;
jump return ; jump return ;
Option.iter ~f:jump throw Option.iter ~f:jump throw
| ICall ({return; throw; _} as call) -> | ICall ({return; throw; _} as call) ->
@ -767,7 +762,7 @@ let set_derived_metadata functions =
BlockQ.enqueue_back_exn tips_to_roots src () BlockQ.enqueue_back_exn tips_to_roots src ()
in in
FuncQ.iter roots ~f:(fun root -> FuncQ.iter roots ~f:(fun root ->
visit Block_label.Set.empty root root.entry ) ; visit Block_label.Set.empty root.entry ) ;
tips_to_roots tips_to_roots
in in
let set_sort_indices tips_to_roots = let set_sort_indices tips_to_roots =

Loading…
Cancel
Save