diff options
| author | Alasdair | 2018-08-13 22:11:02 +0100 |
|---|---|---|
| committer | Alasdair | 2018-08-13 22:11:02 +0100 |
| commit | 28c720774861d038fb7bbed8e1b3bedc757119e4 (patch) | |
| tree | 8749a86c2690380b53afbbe766bfdae6fc56a0d1 /src | |
| parent | c0d6bfaae9f12696b591f14fc8d3cfe08dabd0c4 (diff) | |
Add graph library graph.ml, and use to correctly sort type definitions
Diffstat (limited to 'src')
| -rw-r--r-- | src/c_backend.ml | 35 | ||||
| -rw-r--r-- | src/graph.ml | 201 | ||||
| -rw-r--r-- | src/graph.mli | 96 |
3 files changed, 317 insertions, 15 deletions
diff --git a/src/c_backend.ml b/src/c_backend.ml index 841f9eb1..ec5fa580 100644 --- a/src/c_backend.ml +++ b/src/c_backend.ml @@ -1902,7 +1902,8 @@ let rec specialize_variants ctx = let sort_ctype_defs cdefs = (* Split the cdefs into type definitions and non type definitions *) let is_ctype_def = function CDEF_type _ -> true | _ -> false in - let ctype_defs = List.filter is_ctype_def cdefs in + let unwrap = function CDEF_type ctdef -> ctdef | _ -> assert false in + let ctype_defs = List.map unwrap (List.filter is_ctype_def cdefs) in let cdefs = List.filter (fun cdef -> not (is_ctype_def cdef)) cdefs in let ctdef_id = function @@ -1915,22 +1916,26 @@ let sort_ctype_defs cdefs = List.fold_left (fun ids (_, ctyp) -> IdSet.union (ctyp_ids ctyp) ids) IdSet.empty ctors in - let depends_on cdef1 cdef2 = - match cdef1, cdef2 with - | CDEF_type ctdef1, CDEF_type ctdef2 -> - let ctdef1_ids = ctdef_ids ctdef1 in - let ctdef2_ids = ctdef_ids ctdef2 in - if IdSet.mem (ctdef_id ctdef1) ctdef2_ids && IdSet.mem (ctdef_id ctdef2) ctdef1_ids then - c_error (Printf.sprintf "Post specialization circular dependency between %s and %s found" - (string_of_id (ctdef_id ctdef1)) - (string_of_id (ctdef_id ctdef2))) - else if IdSet.mem (ctdef_id ctdef1) ctdef2_ids then -1 - else 1 - - | _, _ -> assert false (* We only use this on ctype_defs *) + (* Create a reverse id graph of dependencies between types *) + let module IdGraph = Graph.Make(Id) in + + let graph = + List.fold_left (fun g ctdef -> + List.fold_left (fun g id -> IdGraph.add_edge id (ctdef_id ctdef) g) + (IdGraph.add_edges (ctdef_id ctdef) [] g) (* Make sure even types with no dependencies are in graph *) + (IdSet.elements (ctdef_ids ctdef))) + IdGraph.empty + ctype_defs in - List.sort depends_on ctype_defs @ cdefs + (* Then select the ctypes in the correct order as given by the topsort *) + let ids = IdGraph.topsort graph in + let ctype_defs = + List.map (fun id -> CDEF_type (List.find (fun ctdef -> Id.compare (ctdef_id ctdef) id = 0) ctype_defs)) ids + in + + ctype_defs @ cdefs + (* (* When this optimization fires we know we have bytecode of the form diff --git a/src/graph.ml b/src/graph.ml new file mode 100644 index 00000000..c08823cf --- /dev/null +++ b/src/graph.ml @@ -0,0 +1,201 @@ +(**************************************************************************) +(* Sail *) +(* *) +(* Copyright (c) 2013-2017 *) +(* Kathyrn Gray *) +(* Shaked Flur *) +(* Stephen Kell *) +(* Gabriel Kerneis *) +(* Robert Norton-Wright *) +(* Christopher Pulte *) +(* Peter Sewell *) +(* Alasdair Armstrong *) +(* Brian Campbell *) +(* Thomas Bauereiss *) +(* Anthony Fox *) +(* Jon French *) +(* Dominic Mulligan *) +(* Stephen Kell *) +(* Mark Wassell *) +(* *) +(* All rights reserved. *) +(* *) +(* This software was developed by the University of Cambridge Computer *) +(* Laboratory as part of the Rigorous Engineering of Mainstream Systems *) +(* (REMS) project, funded by EPSRC grant EP/K008528/1. *) +(* *) +(* Redistribution and use in source and binary forms, with or without *) +(* modification, are permitted provided that the following conditions *) +(* are met: *) +(* 1. Redistributions of source code must retain the above copyright *) +(* notice, this list of conditions and the following disclaimer. *) +(* 2. Redistributions in binary form must reproduce the above copyright *) +(* notice, this list of conditions and the following disclaimer in *) +(* the documentation and/or other materials provided with the *) +(* distribution. *) +(* *) +(* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' *) +(* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *) +(* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *) +(* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR *) +(* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, *) +(* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT *) +(* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF *) +(* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND *) +(* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, *) +(* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT *) +(* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF *) +(* SUCH DAMAGE. *) +(**************************************************************************) + +module type OrderedType = + sig + type t + val compare : t -> t -> int + end + +module type S = + sig + type node + type graph + type node_set + + val leaves : graph -> node_set + + val empty : graph + + (** Add an edge from the first node to the second node, creating + the nodes if they do not exist. *) + val add_edge : node -> node -> graph -> graph + val add_edges : node -> node list -> graph -> graph + + (** Return the set of nodes that are reachable from the first set + of nodes (roots), without passing through the second set of + nodes (cuts). *) + val reachable : node_set -> node_set -> graph -> node_set + + (** Prune a graph from roots to cuts. *) + val prune : node_set -> node_set -> graph -> graph + + val remove_self_loops : graph -> graph + + val reverse : graph -> graph + + exception Not_a_DAG of node * graph;; + + (** Topologically sort a graph. Throws Not_a_DAG if the graph is + not directed acyclic. *) + val topsort : graph -> node list + end + +module Make(Ord: OrderedType) = struct + + type node = Ord.t + + (* Node set *) + module NS = Set.Make(Ord) + (* Node map *) + module NM = Map.Make(Ord) + + type graph = NS.t NM.t + + type node_set = NS.t + + let empty = NM.empty + + let leaves cg = + List.fold_left (fun acc (fn, callees) -> NS.filter (fun callee -> callee <> fn) (NS.union acc callees)) NS.empty (NM.bindings cg) + + let fix_leaves cg = + NS.fold (fun leaf cg -> if NM.mem leaf cg then cg else NM.add leaf NS.empty cg) (leaves cg) cg + + (* FIXME: don't use fix_leaves because this is inefficient *) + let add_edge caller callee cg = + try + fix_leaves (NM.add caller (NS.add callee (NM.find caller cg)) cg) + with + | Not_found -> fix_leaves (NM.add caller (NS.singleton callee) cg) + + let add_edges caller callees cg = + let callees = List.fold_left (fun s c -> NS.add c s) NS.empty callees in + try + fix_leaves (NM.add caller (NS.union callees (NM.find caller cg)) cg) + with + | Not_found -> fix_leaves (NM.add caller callees cg) + + let reachable roots cuts cg = + let visited = ref NS.empty in + + let rec reachable' node = + if NS.mem node !visited then () + else if NS.mem node cuts then visited := NS.add node !visited + else + begin + visited := NS.add node !visited; + let children = + try NM.find node cg with + | Not_found -> NS.empty + in + NS.iter reachable' children + end + in + + NS.iter reachable' roots; !visited + + let prune roots cuts cg = + let rs = reachable roots cuts cg in + fix_leaves (NM.filter (fun fn _ -> NS.mem fn rs) cg) + + let remove_self_loops cg = + NM.mapi (fun fn callees -> NS.remove fn callees) cg + + let reverse cg = + let rcg = ref NM.empty in + let find_default fn cg = try NM.find fn cg with Not_found -> NS.empty in + + NM.iter (fun caller -> NS.iter (fun callee -> rcg := NM.add callee (NS.add caller (find_default callee !rcg)) !rcg)) cg; + fix_leaves !rcg + + exception Not_a_DAG of node * graph;; + + let prune_loop node cg = + let down = prune (NS.singleton node) NS.empty cg in + let up = prune (NS.singleton node) NS.empty (reverse down) in + reverse up + + let topsort cg = + let marked = Hashtbl.create (NM.cardinal cg) in + let temp_marked = ref NS.empty in + let list = ref [] in + let keys = NM.bindings cg |> List.map fst in + let find_unmarked keys = List.find (fun node -> not (Hashtbl.mem marked node)) keys in + + let rec visit node = + if NS.mem node !temp_marked + then raise (let lcg = prune_loop node cg in Not_a_DAG (node, lcg)) + else if Hashtbl.mem marked node + then () + else + begin + let children = + try NM.find node cg with + | Not_found -> NS.empty + in + temp_marked := NS.add node !temp_marked; + NS.iter (fun child -> visit child) children; + Hashtbl.add marked node (); + temp_marked := NS.remove node !temp_marked; + list := node :: !list + end + in + + let rec topsort' () = + try + let unmarked = find_unmarked keys in + visit unmarked; topsort' () + with + | Not_found -> () + + in topsort' (); !list + +end diff --git a/src/graph.mli b/src/graph.mli new file mode 100644 index 00000000..748ce717 --- /dev/null +++ b/src/graph.mli @@ -0,0 +1,96 @@ +(**************************************************************************) +(* Sail *) +(* *) +(* Copyright (c) 2013-2017 *) +(* Kathyrn Gray *) +(* Shaked Flur *) +(* Stephen Kell *) +(* Gabriel Kerneis *) +(* Robert Norton-Wright *) +(* Christopher Pulte *) +(* Peter Sewell *) +(* Alasdair Armstrong *) +(* Brian Campbell *) +(* Thomas Bauereiss *) +(* Anthony Fox *) +(* Jon French *) +(* Dominic Mulligan *) +(* Stephen Kell *) +(* Mark Wassell *) +(* *) +(* All rights reserved. *) +(* *) +(* This software was developed by the University of Cambridge Computer *) +(* Laboratory as part of the Rigorous Engineering of Mainstream Systems *) +(* (REMS) project, funded by EPSRC grant EP/K008528/1. *) +(* *) +(* Redistribution and use in source and binary forms, with or without *) +(* modification, are permitted provided that the following conditions *) +(* are met: *) +(* 1. Redistributions of source code must retain the above copyright *) +(* notice, this list of conditions and the following disclaimer. *) +(* 2. Redistributions in binary form must reproduce the above copyright *) +(* notice, this list of conditions and the following disclaimer in *) +(* the documentation and/or other materials provided with the *) +(* distribution. *) +(* *) +(* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' *) +(* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *) +(* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *) +(* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR *) +(* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, *) +(* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT *) +(* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF *) +(* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND *) +(* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, *) +(* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT *) +(* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF *) +(* SUCH DAMAGE. *) +(**************************************************************************) + +(** Generic graph type based on OCaml Set and Map *) + +module type OrderedType = + sig + type t + val compare : t -> t -> int + end + +module type S = + sig + type node + type graph + type node_set + + val leaves : graph -> node_set + + val empty : graph + + (** Add an edge from the first node to the second node, creating + the nodes if they do not exist. *) + val add_edge : node -> node -> graph -> graph + val add_edges : node -> node list -> graph -> graph + + (** Return the set of nodes that are reachable from the first set + of nodes (roots), without passing through the second set of + nodes (cuts). *) + val reachable : node_set -> node_set -> graph -> node_set + + (** Prune a graph from roots to cuts. *) + val prune : node_set -> node_set -> graph -> graph + + val remove_self_loops : graph -> graph + + val reverse : graph -> graph + + exception Not_a_DAG of node * graph;; + + (** Topologically sort a graph. Throws Not_a_DAG if the graph is + not directed acyclic. *) + val topsort : graph -> node list + end + +module Make(Ord: OrderedType) : S + with type node = Ord.t + and type node_set = Set.Make(Ord).t + and type graph = Set.Make(Ord).t Map.Make(Ord).t |
