-
Notifications
You must be signed in to change notification settings - Fork 5
Flat map statements into blocks, and use in Aslp #217
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
Changes from all commits
0b4751e
3e80e6a
66166bc
a065b1c
d6c9ce9
2ae82ed
aee7b60
a908d88
f9892e1
045d47b
ce192a5
224968c
4d908c9
3c276e1
7e789a3
4507b64
9467549
6d6cbcb
a9aef85
778eef6
58145f0
d3af79c
876cc14
729de80
5ae181d
7ba1401
00a9f7a
527643d
9c6ccf2
71d0674
63f0aa0
329827e
62722ba
27bb038
dd211e9
1919e34
f1a00f1
7fd0929
c59fd93
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 |
|---|---|---|
|
|
@@ -403,8 +403,7 @@ let decl_block_exn p name ?(phis = []) | |
| let p = add_block p id ~phis ~stmts ~successors ~attrib () in | ||
| (p, id) | ||
|
|
||
| let modify_block p id | ||
| (f : (Var.t, BasilExpr.t) Block.t -> (Var.t, BasilExpr.t) Block.t) = | ||
| let modify_block p id (f : _ Block.t -> _ Block.t) = | ||
| let open Edge in | ||
| let open G in | ||
| let block = f (find_block p id) in | ||
|
|
@@ -456,7 +455,10 @@ let replace_edge p id (block : (Var.t, BasilExpr.t) Block.t) = | |
| [to_] is modified to additionally have the original outgoing edges of | ||
| [from_]. | ||
|
|
||
| This includes any edges to [Return] nodes, if they exist on [from_]. *) | ||
| This includes any edges to [Return] nodes, if they exist on [from_]. | ||
|
|
||
| TODO: how does this interact with phi nodes?? probably impossible to handle. | ||
| *) | ||
| let transplant_outgoing_edges p ~from ~to_ : _ t = | ||
| let replace_outgoing_uses ~from ~to_ g = | ||
| G.fold_succ_e | ||
|
|
@@ -478,6 +480,21 @@ let transplant_incoming_edges p ~from ~to_ : _ t = | |
| in | ||
| p |> map_graph (replace_incoming_uses ~from:(Begin from) ~to_:(Begin to_)) | ||
|
|
||
| (** Replaces uses of the old block ID with the new [(first, last)] block IDs. | ||
| The incoming edges to [old] will be redirected to [first] and the outgoing | ||
| edges of [old] will be rebased to originate from [last]. | ||
|
|
||
| [first] and [last] may be the same. One or both of [first]/[last] may be the | ||
| same as [old]. If neither is the same as [old], [old] will be removed from | ||
| the procedure. *) | ||
| let replace_block ~old ~new_:(new_first, new_last) proc = | ||
| proc | ||
| |> transplant_incoming_edges ~from:old ~to_:new_first | ||
| |> transplant_outgoing_edges ~from:old ~to_:new_last | ||
| |> | ||
| if ID.equal old new_first || ID.equal old new_last then Fun.id | ||
| else Fun.flip remove_block old | ||
|
|
||
| let lookup_local_decl p v = | ||
| Var.Decls.find_opt (local_decls p) v | ||
| |> Option.or_lazy ~else_:(fun () -> | ||
|
|
@@ -649,6 +666,122 @@ let flat_map_stmts_topo_rev ?visit rewriter p = | |
| update_block p bid (Block.flat_map ~rev:true ~phi:Fun.id rewriter b)) | ||
| p blocks | ||
|
|
||
| type ('v, 'e) mapped_stmt = | ||
| [ `Stmts of (Var.t, Var.t, BasilExpr.t) Stmt.t list | ||
| (** Zero or more straight-line statements. *) | ||
| | `Blocks of (Var.t, BasilExpr.t) Block.t list | ||
| (** Zero or more straight-line blocks. *) | ||
| | `Graph of ID.t * ID.t * ('v, 'e) t | ||
| (** Multi-block subgraph. [`Graph (begin, end, proc)] represents | ||
| control-flow entering at [begin] and exiting at the [end], and with any | ||
| control-flow between them. | ||
|
|
||
| [proc] is the procedure modified to include fresh blocks [first] and | ||
| [end], as well as any other blocks or control-flow edges {i between} | ||
| them. [proc] should be unchanged aside from the addition of fresh blocks | ||
| and control-flow between them. [begin] should have no predcessors, and | ||
| dominate all new blocks, including [end]. [end] should have no | ||
| successors. | ||
|
|
||
| [begin] and [end] may be the same block. *) ] | ||
| (** Program fragment to replace a statement during {!cfg_concatmap_block}: | ||
| either a list of sequential statements, list of sequential blocks, {i or} a | ||
| the procedure including an additional cfg fragment bounded by fresh entry | ||
| and exit block ids. *) | ||
|
|
||
| (** [cfg_concatmap_block ~f bid proc] takes a function [f] from statement to a | ||
| code fragment, and replaces block [bid] in [proc] with the concatenated | ||
| result of mapping its statements through through [f]. [f] may return either | ||
| a statement list, block list, or [proc] modified to include a new CFG | ||
| fragment bounded by a begining or end block. See {!type-mapped_stmt} for | ||
| details about [f]'s return type. | ||
|
|
||
| {b Returns} [(first, last, proc)] where [proc] is the updated procedure and | ||
| [first] / [last] is the first / last block of the concatenated output | ||
| program. *) | ||
| let cfg_concatmap_block ~(f : proc:_ t -> _ Stmt.t -> _ mapped_stmt) base_bid | ||
| proc = | ||
| let existing_bids = lazy (IDSet.of_iter (Iter.map fst (iter_blocks proc))) in | ||
| let is_fresh = fun bid -> not (IDSet.mem bid (Lazy.force existing_bids)) in | ||
|
|
||
| (* Applies the [f] function while recording the current block ID for | ||
| [`Stmts] insertion, if available. | ||
|
|
||
| Also normalises the output into either: | ||
| - an iter of already-inserted [(first,last)] bookend IDs, or | ||
| - a [(bid, stmts)] which will be inserted later into the specified [bid]. *) | ||
| let apply_f ~proc (cur_stmts_bid : ID.t option) stmt : | ||
| (_ t * ID.t option) | ||
| * ((ID.t * ID.t) Iter.t, ID.t * _ Stmt.t Iter.t) Either.t = | ||
| match f ~proc stmt with | ||
| | `Blocks [] | `Stmts [] -> ((proc, cur_stmts_bid), Left Iter.empty) | ||
| | `Blocks bs -> | ||
| let[@warning "+missing-record-field-pattern"] proc, bids = | ||
| List.fold_map | ||
| (fun proc { Block.attrib; stmts; phis } -> | ||
| fresh_block proc ~attrib ~phis ~stmts:(CCVector.to_list stmts) ()) | ||
| proc bs | ||
| in | ||
| ((proc, None), Left (Iter.map Pair.dup (Iter.of_list bids))) | ||
| | `Graph (first, last, proc) -> | ||
| if is_fresh first && is_fresh last then | ||
| ((proc, None), Left (Iter.singleton (first, last))) | ||
| else failwith "cfg_concatmap_block: `Graph blocks should be fresh" | ||
| | `Stmts stmts -> | ||
| let proc, bid = | ||
| match cur_stmts_bid with | ||
| | None -> fresh_block proc ~name:(ID.name base_bid) ~stmts:[] () | ||
| | Some bid -> (proc, bid) | ||
| in | ||
| ((proc, Some bid), Right (bid, Iter.of_list stmts)) | ||
| in | ||
|
|
||
| (* Map, while generating block names for bare statements returned by the mapping function. *) | ||
| let (proc, _), mapped = | ||
| find_block proc base_bid |> Block.stmts_iter |> Iter.to_list | ||
| |> List.fold_map (fun (proc, b) -> apply_f ~proc b) (proc, Some base_bid) | ||
| in | ||
| (* Collects adjacent bare statements into a basic block, and inserts those statements. *) | ||
| let proc = modify_block proc base_bid Block.clear_stmts in | ||
| let proc, block_id_pairs = | ||
| Extras.group_succ_either mapped | ||
| |> List.fold_flat_map | ||
| (fun proc -> function | ||
| | Either.Left (hd, tl) -> (proc, Iter.(to_list (append_l (hd :: tl)))) | ||
| | Either.Right ((bid, hd), rest) -> | ||
| let stmts = | ||
| Iter.append hd (Iter.flat_map snd (Iter.of_list rest)) | ||
| |> CCVector.of_iter |> CCVector.freeze | ||
| in | ||
| ( modify_block proc bid (fun b -> { b with stmts }), | ||
| [ (bid, bid) ] )) | ||
| proc | ||
| in | ||
| (* Transplant predecessors and successors of the original block as needed. *) | ||
| let first, last = | ||
| ( List.head_opt block_id_pairs |> Option.map_or fst ~default:base_bid, | ||
| List.last_opt block_id_pairs |> Option.map_or snd ~default:base_bid ) | ||
| in | ||
| let proc = | ||
| proc | ||
| |> transplant_outgoing_edges ~from:base_bid ~to_:last | ||
| |> | ||
| if not ID.(equal first base_bid) then | ||
| add_goto ~from:base_bid ~targets:[ first ] | ||
| else Fun.id | ||
| in | ||
|
Owner
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. Might help clarify the API if you also require that |
||
| (* Insert gotos between mapped blocks. This must happen after transplanting | ||
| so we do not transplant these edges. *) | ||
| let proc = | ||
| List.combine_gen block_id_pairs (List.drop 1 block_id_pairs) | ||
| |> Iter.of_gen | ||
| |> Iter.fold | ||
| (fun proc ((_, prev), (next, _)) -> | ||
| add_goto proc ~from:prev ~targets:[ next ]) | ||
| proc | ||
| in | ||
| (first, last, proc) | ||
|
|
||
| let pretty_spec show_var show_expr (p : ('a, 'b) proc_spec) = | ||
| let open Containers_pp in | ||
| let ml f v = if List.is_empty v then [] else [ f v ] in | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -93,9 +93,9 @@ let pretty_declaration d = | |
| | Procedure { definition } -> pretty_proc definition | ||
|
|
||
| (*match definition with | ||
| | Some d -> | ||
| | Some d -> | ||
| let param, rt = Types.uncurry (Var.typ binding) in | ||
| let param = | ||
| let param = | ||
| text "let " ^ text (Var.name binding) ^ text (Var.to_decl_string_il binding) | ||
| | None -> text @@ Var.to_decl_string_il binding) | ||
| *) | ||
|
|
@@ -284,6 +284,16 @@ let flat_map_decls f p = | |
| else { prog with declarations = IDMap.remove i prog.declarations }) | ||
| p | ||
|
|
||
| (** Iterates over global variables in the given program, including both read and | ||
| assigned variables. Order is unspecified and may have duplicates. *) | ||
| let referenced_vars_of_prog = | ||
| procs | ||
| %> Iter.flat_map | ||
| (snd %> Procedure.iter_blocks | ||
| %> Iter.flat_map (fun (_, b) -> | ||
| Iter.append (Block.read_vars_iter b) (Block.assigned_vars_iter b))) | ||
| %> Iter.filter Var.is_global | ||
|
Owner
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. It should be a requirement that this should always be less than
Collaborator
Author
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. Program.global_vars filters the decls list, but here we need to get referenced variables which may or may not have been declared. |
||
|
|
||
| let pretty_to_chan chan (p : t) = | ||
| let p = prog_pretty p in | ||
| flush chan; | ||
|
|
||
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 declaring the type it may as well be a regular variant as it provides better type inference and errors.
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.
Variants are kinda annoying to use because you have to qualify them with the module. Also, a variant like
Procedure.Blocksis rather confusing I think, so it would need a different name anyway. And it's really just a special type used in this one function that is only declared separately so I can ocamldoc it.Unless you feel strongly, I will leave it.