-
Notifications
You must be signed in to change notification settings - Fork 273
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
optimizes reconstructor, symtab, and brancher performance #855
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,51 +19,47 @@ type reconstructor = t | |
let create f = Reconstructor f | ||
let run (Reconstructor f) = f | ||
|
||
let find_calls name roots cfg = | ||
let starts = Addr.Table.create () in | ||
List.iter roots ~f:(fun addr -> | ||
Hashtbl.set starts ~key:addr ~data:(name addr)); | ||
Cfg.nodes cfg |> Seq.iter ~f:(fun blk -> | ||
let () = | ||
if Seq.is_empty (Cfg.Node.inputs blk cfg) then | ||
let addr = Block.addr blk in | ||
Hashtbl.set starts ~key:addr ~data:(name addr) in | ||
let term = Block.terminator blk in | ||
if Insn.(is call) term then | ||
Seq.iter (Cfg.Node.outputs blk cfg) | ||
~f:(fun e -> | ||
if Cfg.Edge.label e <> `Fall then | ||
let w = Block.addr (Cfg.Edge.dst e) in | ||
Hashtbl.set starts ~key:w ~data:(name w))); | ||
starts | ||
let roots_of_blk roots cfg blk = | ||
let addr = Block.addr blk in | ||
let term = Block.terminator blk in | ||
let init = | ||
if Set.mem roots addr || Seq.is_empty (Cfg.Node.inputs blk cfg) | ||
then [blk] | ||
else [] in | ||
if Insn.(is call) term then | ||
Seq.fold ~init (Cfg.Node.outputs blk cfg) | ||
~f:(fun rs e -> | ||
if Cfg.Edge.label e <> `Fall then Cfg.Edge.dst e :: rs | ||
else rs) | ||
else init | ||
|
||
let find_calls cfg roots = | ||
let roots = List.fold ~init:Addr.Set.empty ~f:Set.add roots in | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is |
||
Graphlib.depth_first_search (module Cfg) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You don't need DFS for that as you do not need any specific ordering. Just iterate over all nodes. Also, you don't need roots, to check that root is root and add it to roots. This function is simply: let find_calls cfg roots = Cfg.nodes cfg |> Seq.fold ~init:roots ~f:(fun roots n ->
Set.union roots (roots_of_node n)) |
||
cfg ~init:Block.Set.empty | ||
~enter_node:(fun _ blk all -> | ||
roots_of_blk roots cfg blk |> | ||
List.fold ~init:all ~f:Set.add) | ||
|
||
let reconstruct name roots cfg = | ||
let roots = find_calls name roots cfg in | ||
let init = | ||
Cfg.nodes cfg |> Seq.fold ~init:Cfg.empty ~f:(fun cfg n -> | ||
Cfg.Node.insert n cfg) in | ||
let filtered = | ||
Cfg.edges cfg |> Seq.fold ~init ~f:(fun cfg e -> | ||
if Hashtbl.mem roots (Block.addr (Cfg.Edge.dst e)) then cfg | ||
else Cfg.Edge.insert e cfg) in | ||
let find_block addr = | ||
Cfg.nodes cfg |> Seq.find ~f:(fun blk -> | ||
Addr.equal addr (Block.addr blk)) in | ||
Hashtbl.fold roots ~init:Symtab.empty | ||
~f:(fun ~key:entry ~data:name syms -> | ||
match find_block entry with | ||
| None -> syms | ||
| Some entry -> | ||
let cfg : cfg = | ||
with_return (fun {return} -> | ||
Graphlib.depth_first_search (module Cfg) | ||
filtered ~start:entry ~init:Cfg.empty | ||
~enter_edge:(fun _ -> Cfg.Edge.insert) | ||
~start_tree:(fun n t -> | ||
if Block.equal n entry | ||
then Cfg.Node.insert n t | ||
else return t)) in | ||
Symtab.add_symbol syms (name,entry,cfg)) | ||
let roots = find_calls cfg roots in | ||
let filtered = Set.fold roots ~init:cfg | ||
~f:(fun g root -> | ||
let inputs = Cfg.Node.inputs root cfg in | ||
Seq.fold inputs ~init:g ~f:(fun g e -> Cfg.Edge.remove e g)) in | ||
Set.fold roots ~init:Symtab.empty | ||
~f:(fun syms entry -> | ||
let name = name (Block.addr entry) in | ||
let cfg : cfg = | ||
with_return (fun {return} -> | ||
Graphlib.depth_first_search (module Cfg) | ||
filtered ~start:entry ~init:Cfg.empty | ||
~enter_edge:(fun _ -> Cfg.Edge.insert) | ||
~start_tree:(fun n t -> | ||
if Block.equal n entry | ||
then Cfg.Node.insert n t | ||
else return t)) in | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So the main difference here is that instead of checking each destination for is it a call or not, we just iterate over all calls and ask for incoming edges of a call. That makes sense, and this is indeed faster, provided that majority of calls are reachable from the whole program CFG. |
||
Symtab.add_symbol syms (name,entry,cfg)) | ||
|
||
let of_blocks syms = | ||
let reconstruct (cfg : cfg) = | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,7 +17,7 @@ type cfg = Cfg.t [@@deriving compare] | |
|
||
type fn = string * block * cfg [@@deriving compare] | ||
|
||
let sexp_of_fn (name,block,cfg) = | ||
let sexp_of_fn (name,block,_cfg) = | ||
Sexp.List [sexp_of_string name; sexp_of_addr (Block.addr block)] | ||
|
||
module Fn = Opaque.Make(struct | ||
|
@@ -38,7 +38,7 @@ let compare t1 t2 = | |
|
||
type symtab = t [@@deriving compare, sexp_of] | ||
|
||
let span ((name,entry,cfg) as fn) = | ||
let span ((_name,_entry,cfg) as fn) = | ||
Cfg.nodes cfg |> Seq.fold ~init:Memmap.empty ~f:(fun map blk -> | ||
Memmap.add map (Block.memory blk) fn) | ||
|
||
|
@@ -52,19 +52,27 @@ let merge m1 m2 = | |
Memmap.to_sequence m2 |> Seq.fold ~init:m1 ~f:(fun m1 (mem,x) -> | ||
Memmap.add m1 mem x) | ||
|
||
let filter_mem mem name entry = | ||
Memmap.filter mem ~f:(fun (n,e,_) -> | ||
not(String.(name = n) || Block.(entry = e))) | ||
|
||
let remove t (name,entry,_) : t = { | ||
names = Map.remove t.names name; | ||
addrs = Map.remove t.addrs (Block.addr entry); | ||
memory = Memmap.filter t.memory ~f:(fun (n,e,_) -> | ||
not(String.(name = n) || Block.(entry = e))) | ||
memory = filter_mem t.memory name entry; | ||
} | ||
|
||
let filter t ((name,entry,_ ) as fn) = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. OK, so from the IRL discussion, the remove function is inefficient since it filters the memory mapping even if the symbol is not in the symtab, the fact that could be checked very fast with the Set membership operation. However, this is the problem of the |
||
if Map.mem t.names name || Map.mem t.addrs (Block.addr entry) then | ||
remove t fn | ||
else t | ||
|
||
let add_symbol t (name,entry,cfg) : t = | ||
let data = name,entry,cfg in | ||
let t = remove t data in | ||
let t = filter t data in | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can you please justify why we can use |
||
{ | ||
addrs = Map.add t.addrs ~key:(Block.addr entry) ~data; | ||
names = Map.add t.names ~key:name ~data; | ||
names = Map.add t.names ~key:name ~data; | ||
memory = merge t.memory (span data); | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If a block address is already in the set of roots then there is no need to add to the set of roots.
also, to count the indegree of a block use the specialized function
Cfg.Node.degree ~dir:In
, thus this functioncould be defined as simple as