diff --git a/.gitignore b/.gitignore index 693699042b1..53eaa21960d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ /target **/*.rs.bk -Cargo.lock diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 00000000000..f1031442ff5 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,603 @@ +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "assert_cli" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "colored 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "difference 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "environment 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "skeptic 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "atty" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "backtrace" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "backtrace-sys" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bitflags" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bitflags" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bytecount" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "byteorder" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cargo_metadata" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "semver 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.43 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.43 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cc" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cfg-if" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "clap" +version = "2.31.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "atty 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "colored" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "datafrog" +version = "0.1.0" +source = "git+https://github.com/frankmcsherry/datafrog?rev=c963caead#c963caeadc31ed497922ba246c6ee4e1aaf9486f" + +[[package]] +name = "difference" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "dtoa" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "environment" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "error-chain" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "failure" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "failure_derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", + "synstructure 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "glob" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "histo" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "streaming-stats 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "itoa" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "lazy_static" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libc" +version = "0.2.40" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "num-traits" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "polonius" +version = "0.1.0" +dependencies = [ + "assert_cli 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", + "clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)", + "datafrog 0.1.0 (git+https://github.com/frankmcsherry/datafrog?rev=c963caead)", + "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "histo 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "structopt 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "proc-macro2" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "pulldown-cmark" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quote" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "quote" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "redox_syscall" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "redox_termios" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "remove_dir_all" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "same-file" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "semver" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.43 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde" +version = "1.0.43" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde_derive" +version = "1.0.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive_internals 0.23.1 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_derive_internals" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_json" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.43 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "skeptic" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytecount 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cargo_metadata 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "pulldown-cmark 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "streaming-stats" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "num-traits 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "strsim" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "structopt" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)", + "structopt-derive 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "structopt-derive" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "syn" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "syn" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "synom" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "synstructure" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tempdir" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "termion" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "textwrap" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-width" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-xid" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-xid" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "vec_map" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "walkdir" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "same-file 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +"checksum assert_cli 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "72342c21057a3cb5f7c2d849bf7999a83795434dd36d74fa8c24680581bd1930" +"checksum atty 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "2fc4a1aa4c24c0718a250f0681885c1af91419d242f29eb8f2ab28502d80dbd1" +"checksum backtrace 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ebbe525f66f42d207968308ee86bc2dd60aa5fab535b22e616323a173d097d8e" +"checksum backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "44585761d6161b0f57afc49482ab6bd067e4edef48c12a152c237eb0203f7661" +"checksum bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" +"checksum bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d0c54bb8f454c567f21197eefcdbf5679d0bd99f2ddbe52e84c77061952e6789" +"checksum bytecount 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "af27422163679dea46a1a7239dffff64d3dcdc3ba5fe9c49c789fbfe0eb949de" +"checksum byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "73b5bdfe7ee3ad0b99c9801d58807a9dbc9e09196365b0203853b99889ab3c87" +"checksum cargo_metadata 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1f56ec3e469bca7c276f2eea015aa05c5e381356febdbb0683c2580189604537" +"checksum cc 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)" = "0ebb87d1116151416c0cf66a0e3fb6430cccd120fd6300794b4dfaa050ac40ba" +"checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de" +"checksum clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0f16b89cbb9ee36d87483dc939fe9f1e13c05898d56d7b230a0d4dff033a536" +"checksum colored 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b0aa3473e85a3161b59845d6096b289bb577874cafeaf75ea1b1beaa6572c7fc" +"checksum datafrog 0.1.0 (git+https://github.com/frankmcsherry/datafrog?rev=c963caead)" = "" +"checksum difference 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3304d19798a8e067e48d8e69b2c37f0b5e9b4e462504ad9e27e9f3fce02bba8" +"checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab" +"checksum environment 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1f4b14e20978669064c33b4c1e0fb4083412e40fe56cbea2eae80fd7591503ee" +"checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3" +"checksum failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "934799b6c1de475a012a02dab0ace1ace43789ee4b99bcfbf1a2e3e8ced5de82" +"checksum failure_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c7cdda555bb90c9bb67a3b670a0f42de8e73f5981524123ad8578aafec8ddb8b" +"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" +"checksum fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" +"checksum histo 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5a4195184b16b1e8e50353b41f83ad743b80d687b4c41a6f1d0d50231325e258" +"checksum itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c069bbec61e1ca5a596166e55dfe4773ff745c3d16b700013bcaff9a6df2c682" +"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +"checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73" +"checksum libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)" = "6fd41f331ac7c5b8ac259b8bf82c75c0fb2e469bbf37d2becbba9a6a2221965b" +"checksum num-traits 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "775393e285254d2f5004596d69bb8bc1149754570dcc08cf30cabeba67955e28" +"checksum proc-macro2 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1b06e2f335f48d24442b35a19df506a835fb3547bc3c06ef27340da9acf5cae7" +"checksum pulldown-cmark 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d6fdf85cda6cadfae5428a54661d431330b312bc767ddbc57adbedc24da66e32" +"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" +"checksum quote 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9949cfe66888ffe1d53e6ec9d9f3b70714083854be20fd5e271b232a017401e8" +"checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5" +"checksum redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "0d92eecebad22b767915e4d529f89f28ee96dbbf5a4810d2b844373f136417fd" +"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" +"checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" +"checksum rustc-demangle 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11fb43a206a04116ffd7cfcf9bcb941f8eb6cc7ff667272246b0a1c74259a3cb" +"checksum same-file 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d931a44fdaa43b8637009e7632a02adc4f2b2e0733c08caa4cf00e8da4a117a7" +"checksum semver 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bee2bc909ab2d8d60dab26e8cad85b25d795b14603a0dcb627b78b9d30b6454b" +"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +"checksum serde 1.0.43 (registry+https://github.com/rust-lang/crates.io-index)" = "0c855d888276f20d140223bd06515e5bf1647fd6d02593cb5792466d9a8ec2d0" +"checksum serde_derive 1.0.43 (registry+https://github.com/rust-lang/crates.io-index)" = "aa113e5fc4b008a626ba2bbd41330b56c9987d667f79f7b243e5a2d03d91ed1c" +"checksum serde_derive_internals 0.23.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9d30c4596450fd7bbda79ef15559683f9a79ac0193ea819db90000d7e1cae794" +"checksum serde_json 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)" = "8c6c4e049dc657a99e394bd85c22acbf97356feeec6dbf44150f2dcf79fb3118" +"checksum skeptic 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c8431f8fca168e2db4be547bd8329eac70d095dff1444fee4b0fa0fabc7df75a" +"checksum streaming-stats 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)" = "dbc32bf233385fb1ae0c4cf13a8dfd11efec71f6a9de95feac8c870788bc8b25" +"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" +"checksum structopt 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b96fc11ba8cf80bfa5cdd6de538c9f7c66f519f83e8caabc554e431bf3e36d" +"checksum structopt-derive 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "95063e55a45976cfce9f03fcd26dff356ee622f1a14147bfae068ab8bed153a6" +"checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" +"checksum syn 0.13.4 (registry+https://github.com/rust-lang/crates.io-index)" = "90d5efaad92a0f96c629ae16302cc9591915930fd49ff0dcc6b4cde146782397" +"checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" +"checksum synstructure 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a761d12e6d8dcb4dcf952a7a89b475e3a9d69e4a69307e01a470977642914bd" +"checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" +"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" +"checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693" +"checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f" +"checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" +"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" +"checksum vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887b5b631c2ad01628bbbaa7dd4c869f80d3186688f8d0b6f58774fbe324988c" +"checksum walkdir 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "bb08f9e670fab86099470b97cd2b252d6527f0b3cc1401acdb595ffc9dd288ff" +"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" +"checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3" +"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" +"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml index 6b704469c5c..b89dad5a59a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,11 +7,8 @@ authors = ["Niko Matsakis "] assert_cli = "0.5.4" [dependencies] -abomonation = "0.5.0" -abomonation_derive = "0.3.0" -differential-dataflow = "0.5.0" +datafrog = { git = "https://github.com/frankmcsherry/datafrog", rev = "c963caead" } failure = "0.1.1" -timely = "0.5.1" fxhash = "0.2.1" structopt = "0.2.8" clap = "2.31.2" diff --git a/src/cli.rs b/src/cli.rs index 43800086ffc..7f485e8e6dc 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -12,16 +12,19 @@ arg_enum! { #[derive(Debug, Clone, Copy)] pub enum Algorithm { Naive, - TimelyOpt, - LocationInsensitive, + DatafrogOpt, + // LocationInsensitive, } } #[derive(StructOpt, Debug)] #[structopt(name = "borrow-check")] pub struct Opt { - #[structopt(short = "a", default_value = "naive", - raw(possible_values = "&Algorithm::variants()", case_insensitive = "true"))] + #[structopt( + short = "a", + default_value = "naive", + raw(possible_values = "&Algorithm::variants()", case_insensitive = "true") + )] algorithm: Algorithm, #[structopt(long = "skip-tuples")] skip_tuples: bool, @@ -35,24 +38,20 @@ pub struct Opt { output_directory: Option, #[structopt(raw(required = "true"))] fact_dirs: Vec, - #[structopt(short = "w", long = "workers", default_value = "1")] - workers: u32, } pub fn main(opt: Opt) -> Result<(), Error> { do catch { - let output_directory = opt - .output_directory - .map(|x| {Path::new(&x).to_owned()} ); + let output_directory = opt.output_directory.map(|x| Path::new(&x).to_owned()); for facts_dir in opt.fact_dirs { let tables = &mut intern::InternerTables::new(); let result: Result<(Duration, Output), Error> = do catch { let verbose = opt.verbose | opt.stats; let algorithm = opt.algorithm; - let workers = opt.workers; - let all_facts = tab_delim::load_tab_delimited_facts(tables, &Path::new(&facts_dir))?; - timed(|| Output::compute(&all_facts, algorithm, verbose, workers)) + let all_facts = + tab_delim::load_tab_delimited_facts(tables, &Path::new(&facts_dir))?; + timed(|| Output::compute(&all_facts, algorithm, verbose)) }; match result { @@ -65,9 +64,11 @@ pub fn main(opt: Opt) -> Result<(), Error> { println!("Time: {:0.3}s", seconds + millis); if opt.verbose { - println!("Max region graph in/out-degree: {} {}", - output.region_degrees.max_in_degree(), - output.region_degrees.max_out_degree()); + println!( + "Max region graph in/out-degree: {} {}", + output.region_degrees.max_in_degree(), + output.region_degrees.max_out_degree() + ); if output.region_degrees.has_multidegree() { println!("Found multidegree"); } else { @@ -82,7 +83,9 @@ pub fn main(opt: Opt) -> Result<(), Error> { } } if !opt.skip_tuples { - output.dump(&output_directory, tables).expect("Failed to write output"); + output + .dump(&output_directory, tables) + .expect("Failed to write output"); } } diff --git a/src/facts.rs b/src/facts.rs index d2048f9eeac..de180760d9f 100644 --- a/src/facts.rs +++ b/src/facts.rs @@ -1,5 +1,3 @@ -use abomonation_derive::Abomonation; - /// The "facts" which are the basis of the NLL borrow analysis. #[derive(Clone, Default)] crate struct AllFacts { @@ -25,15 +23,17 @@ crate struct AllFacts { } macro_rules! index_type { - ($t: ident) => { - #[derive(Ord, PartialOrd, Eq, PartialEq, Clone, Copy, Abomonation, Debug, Hash)] + ($t:ident) => { + #[derive(Ord, PartialOrd, Eq, PartialEq, Clone, Copy, Debug, Hash)] pub(crate) struct $t { index: u32, } impl From for $t { fn from(index: usize) -> $t { - $t { index: index as u32 } + $t { + index: index as u32, + } } } @@ -44,7 +44,7 @@ macro_rules! index_type { } impl $t { - fn index(self) -> usize { + pub(crate) fn index(self) -> usize { self.into() } } @@ -54,4 +54,3 @@ macro_rules! index_type { index_type!(Region); index_type!(Loan); index_type!(Point); - diff --git a/src/intern.rs b/src/intern.rs index cc5f3a53ee6..ba84f06a4fd 100644 --- a/src/intern.rs +++ b/src/intern.rs @@ -56,7 +56,7 @@ crate trait InternTo { } macro_rules! intern_impl { - ($t: ident, $field: ident) => { + ($t:ident, $field:ident) => { impl InternTo<$t> for &str { fn intern(tables: &mut InternerTables, input: &str) -> $t { tables.$field.intern(input) diff --git a/src/lib.rs b/src/lib.rs index 168f12a66de..eafd9366831 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,16 +4,12 @@ #![feature(proc_macro)] #![feature(in_band_lifetimes)] #![feature(termination_trait_test)] - #![allow(dead_code)] -extern crate abomonation; -extern crate abomonation_derive; -extern crate differential_dataflow; +extern crate datafrog; extern crate failure; extern crate fxhash; extern crate histo; -extern crate timely; extern crate structopt; #[macro_use] diff --git a/src/main.rs b/src/main.rs index 1c644a48f43..29600d4d1aa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,5 @@ -extern crate polonius; extern crate failure; +extern crate polonius; extern crate structopt; use structopt::StructOpt; diff --git a/src/output/datafrog_opt.rs b/src/output/datafrog_opt.rs new file mode 100644 index 00000000000..fc2add21f88 --- /dev/null +++ b/src/output/datafrog_opt.rs @@ -0,0 +1,356 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::collections::{BTreeMap, BTreeSet}; +use std::time::Instant; + +use crate::facts::{AllFacts, Loan, Point, Region}; +use crate::output::Output; + +use datafrog::{Iteration, Relation}; + +pub(super) fn compute(dump_enabled: bool, mut all_facts: AllFacts) -> Output { + // Declare that each universal region is live at every point. + let all_points: BTreeSet = all_facts + .cfg_edge + .iter() + .map(|&(p, _)| p) + .chain(all_facts.cfg_edge.iter().map(|&(_, q)| q)) + .collect(); + + for &r in &all_facts.universal_region { + for &p in &all_points { + all_facts.region_live_at.push((r, p)); + } + } + + let timer = Instant::now(); + + let mut result = Output::new(dump_enabled); + + let borrow_live_at = { + // Create a new iteration context, ... + let mut iteration = Iteration::new(); + + // .. some variables and different indices for `subset`. + let subset = iteration.variable::<(Region, Region, Point)>("subset"); + let subset_1 = iteration.variable_indistinct("subset_1"); + let subset_2 = iteration.variable_indistinct("subset_2"); + let subset_r1p = iteration.variable_indistinct("subset_r1p"); + let subset_r2p = iteration.variable_indistinct("subset_r2p"); + let subset_p = iteration.variable_indistinct("subset_p"); + + // temporaries as we perform a multi-way join, and more indices + let requires = iteration.variable::<(Region, Loan, Point)>("requires"); + let requires_1 = iteration.variable_indistinct("requires_1"); + let requires_2 = iteration.variable_indistinct("requires_2"); + let requires_bp = iteration.variable_indistinct("requires_bp"); + let requires_rp = iteration.variable_indistinct("requires_rp"); + + let borrow_live_at = iteration.variable::<(Loan, Point)>("borrow_live_at"); + + let live_to_dead_regions = + iteration.variable::<(Region, Region, Point, Point)>("live_to_dead_regions"); + let live_to_dead_regions_1 = iteration.variable_indistinct("live_to_dead_regions_1"); + let live_to_dead_regions_2 = iteration.variable_indistinct("live_to_dead_regions_2"); + let live_to_dead_regions_p = iteration.variable_indistinct("live_to_dead_regions_p"); + let live_to_dead_regions_r2pq = iteration.variable_indistinct("live_to_dead_regions_r2pq"); + + let dead_region_requires = + iteration.variable::<((Region, Point, Point), Loan)>("dead_region_requires"); + let dead_region_requires_1 = iteration.variable_indistinct("dead_region_requires_1"); + let dead_region_requires_2 = iteration.variable_indistinct("dead_region_requires_2"); + + let dead_can_reach_origins = + iteration.variable::<((Region, Point), Point)>("dead_can_reach_origins"); + let dead_can_reach = iteration.variable::<(Region, Region, Point, Point)>("dead_can_reach"); + let dead_can_reach_1 = iteration.variable_indistinct("dead_can_reach_1"); + let dead_can_reach_r2q = iteration.variable_indistinct("dead_can_reach_r2q"); + // nmatsakis: I tried to merge `dead_can_reach_r2q` and + // `dead_can_reach`, but the result was ever so slightly slower, at least on clap. + + let dead_can_reach_live = + iteration.variable::<((Region, Point, Point), Region)>("dead_can_reach_live"); + let dead_can_reach_live_r1pq = iteration.variable_indistinct("dead_can_reach_live_r1pq"); + + // We need both relation and variable forms of this (for join and antijoin). + let region_live_at_rel = + Relation::from(all_facts.region_live_at.iter().map(|&(r, p)| (r, p))); + let region_live_at_var = iteration.variable::<((Region, Point), ())>("region_live_at"); + + let cfg_edge_p = iteration.variable::<(Point, Point)>("cfg_edge_p"); + + let killed = all_facts.killed.into(); + + // load initial facts. + subset.insert(all_facts.outlives.into()); + requires.insert(all_facts.borrow_region.into()); + region_live_at_var.insert(Relation::from( + all_facts.region_live_at.iter().map(|&(r, p)| ((r, p), ())), + )); + cfg_edge_p.insert(all_facts.cfg_edge.into()); + + // .. and then start iterating rules! + while iteration.changed() { + // remap fields to re-index by the different keys + subset_r1p.from_map(&subset, |&(r1, r2, p)| ((r1, p), r2)); + subset_r2p.from_map(&subset, |&(r1, r2, p)| ((r2, p), r1)); + subset_p.from_map(&subset, |&(r1, r2, p)| (p, (r1, r2))); + + requires_bp.from_map(&requires, |&(r, b, p)| ((b, p), r)); + requires_rp.from_map(&requires, |&(r, b, p)| ((r, p), b)); + + live_to_dead_regions_p + .from_map(&live_to_dead_regions, |&(r1, r2, p, q)| (p, (r1, r2, q))); + live_to_dead_regions_r2pq + .from_map(&live_to_dead_regions, |&(r1, r2, p, q)| ((r2, p, q), r1)); + + dead_can_reach_r2q.from_map(&dead_can_reach, |&(r1, r2, p, q)| ((r2, q), (r1, p))); + dead_can_reach_live_r1pq + .from_map(&dead_can_reach_live, |&((r1, p, q), r2)| ((r1, p, q), r2)); + + // it's now time ... to datafrog: + + // .decl subset(R1, R2, P) + // + // At the point P, R1 <= R2. + // + // subset(R1, R2, P) :- outlives(R1, R2, P). + // -> already loaded; outlives is a static input. + + // .decl requires(R, B, P) -- at the point, things with region R + // may depend on data from borrow B + // + // requires(R, B, P) :- borrow_region(R, B, P). + // -> already loaded; borrow_region is a static input. + + // .decl live_to_dead_regions(R1, R2, P, Q) + // + // The regions `R1` and `R2` are "live to dead" + // on the edge `P -> Q` if: + // + // - In P, `R1` <= `R2` + // - In Q, `R1` is live but `R2` is dead. + // + // In that case, `Q` would like to add all the + // live things reachable from `R2` to `R1`. + // + // live_to_dead_regions(R1, R2, P, Q) :- + // subset(R1, R2, P), + // cfg_edge(P, Q), + // region_live_at(R1, Q), + // !region_live_at(R2, Q). + live_to_dead_regions_1.from_join(&subset_p, &cfg_edge_p, |&p, &(r1, r2), &q| { + ((r1, q), (r2, p)) + }); + live_to_dead_regions_2.from_join( + &live_to_dead_regions_1, + ®ion_live_at_var, + |&(r1, q), &(r2, p), &()| ((r2, q), (r1, p)), + ); + live_to_dead_regions.from_antijoin( + &live_to_dead_regions_2, + ®ion_live_at_rel, + |&(r2, q), &(r1, p)| (r1, r2, p, q), + ); + + // .decl dead_region_requires((R, P, Q), B) + // + // The region `R` requires the borrow `B`, but the + // region `R` goes dead along the edge `P -> Q` + // + // dead_region_requires((R, P, Q), B) :- + // requires(R, B, P), + // !killed(B, P), + // cfg_edge(P, Q), + // !region_live_at(R, Q). + dead_region_requires_1.from_antijoin(&requires_bp, &killed, |&(b, p), &r| (p, (b, r))); + dead_region_requires_2.from_join( + &dead_region_requires_1, + &cfg_edge_p, + |&p, &(b, r), &q| ((r, q), (b, p)), + ); + dead_region_requires.from_antijoin( + &dead_region_requires_2, + ®ion_live_at_rel, + |&(r, q), &(b, p)| ((r, p, q), b), + ); + + // .decl dead_can_reach_origins(R, P, Q) + // + // Contains dead regions where we are interested + // in computing the transitive closure of things they + // can reach. + dead_can_reach_origins.from_map(&live_to_dead_regions, |&(_r1, r2, p, q)| ((r2, p), q)); + dead_can_reach_origins.from_map(&dead_region_requires, |&((r, p, q), _b)| ((r, p), q)); + + // .decl dead_can_reach(R1, R2, P, Q) + // + // Indicates that the region `R1`, which is dead + // in `Q`, can reach the region `R2` in P. + // + // This is effectively the transitive subset + // relation, but we try to limit it to regions + // that are dying on the edge P -> Q. + // + // dead_can_reach(R1, R2, P, Q) :- + // dead_can_reach_origins(R1, P, Q), + // subset(R1, R2, P). + dead_can_reach.from_join(&dead_can_reach_origins, &subset_r1p, |&(r1, p), &q, &r2| { + (r1, r2, p, q) + }); + + // dead_can_reach(R1, R3, P, Q) :- + // dead_can_reach(R1, R2, P, Q), + // !region_live_at(R2, Q), + // subset(R2, R3, P). + // + // This is the "transitive closure" rule, but + // note that we only apply it with the + // "intermediate" region R2 is dead at Q. + dead_can_reach_1.from_antijoin( + &dead_can_reach_r2q, + ®ion_live_at_rel, + |&(r2, q), &(r1, p)| ((r2, p), (r1, q)), + ); + dead_can_reach.from_join( + &dead_can_reach_1, + &subset_r1p, + |&(_r2, p), &(r1, q), &r3| (r1, r3, p, q), + ); + + // .decl dead_can_reach_live(R1, R2, P, Q) + // + // Indicates that, along the edge `P -> Q`, the + // dead (in Q) region R1 can reach the live (in Q) + // region R2 via a subset relation. This is a + // subset of the full `dead_can_reach` relation + // where we filter down to those cases where R2 is + // live in Q. + dead_can_reach_live.from_join( + &dead_can_reach_r2q, + ®ion_live_at_var, + |&(r2, q), &(r1, p), &()| ((r1, p, q), r2), + ); + + // subset(R1, R2, Q) :- + // subset(R1, R2, P) :- + // cfg_edge(P, Q), + // region_live_at(R1, Q), + // region_live_at(R2, Q). + // + // Carry `R1 <= R2` from P into Q if both `R1` and + // `R2` are live in Q. + subset_1.from_join(&subset_p, &cfg_edge_p, |&_p, &(r1, r2), &q| ((r1, q), r2)); + subset_2.from_join(&subset_1, ®ion_live_at_var, |&(r1, q), &r2, &()| { + ((r2, q), r1) + }); + subset.from_join(&subset_2, ®ion_live_at_var, |&(r2, q), &r1, &()| { + (r1, r2, q) + }); + + // subset(R1, R3, Q) :- + // live_to_dead_regions(R1, R2, P, Q), + // dead_can_reach_live(R2, R3, P, Q). + subset.from_join( + &live_to_dead_regions_r2pq, + &dead_can_reach_live_r1pq, + |&(_r2, _p, q), &r1, &r3| (r1, r3, q), + ); + + // requires(R2, B, Q) :- + // dead_region_requires(R1, B, P, Q), + // dead_can_reach_live(R1, R2, P, Q). + // + // Communicate a `R1 requires B` relation across + // an edge `P -> Q` where `R1` is dead in Q; in + // that case, for each region `R2` live in `Q` + // where `R1 <= R2` in P, we add `R2 requires B` + // to `Q`. + requires.from_join( + &dead_region_requires, + &dead_can_reach_live_r1pq, + |&(_r1, _p, q), &b, &r2| (r2, b, q), + ); + + // requires(R, B, Q) :- + // requires(R, B, P), + // !killed(B, P), + // cfg_edge(P, Q), + // region_live_at(R, Q). + requires_1.from_antijoin(&requires_bp, &killed, |&(b, p), &r| (p, (r, b))); + requires_2.from_join(&requires_1, &cfg_edge_p, |&_p, &(r, b), &q| ((r, q), b)); + requires.from_join(&requires_2, ®ion_live_at_var, |&(r, q), &b, &()| { + (r, b, q) + }); + + // .decl borrow_live_at(B, P) -- true if the restrictions of the borrow B + // need to be enforced at the point P + // + // borrow_live_at(B, P) :- requires(R, B, P), region_live_at(R, P) + borrow_live_at.from_join(&requires_rp, ®ion_live_at_var, |&(_r, p), &b, &()| { + (b, p) + }); + } + + if dump_enabled { + for (region, location) in ®ion_live_at_rel.elements { + result + .region_live_at + .entry(*location) + .or_insert(vec![]) + .push(*region); + } + + let subset = subset.complete(); + for (r1, r2, location) in &subset.elements { + result + .subset + .entry(*location) + .or_insert(BTreeMap::new()) + .entry(*r1) + .or_insert(BTreeSet::new()) + .insert(*r2); + result.region_degrees.update_degrees(*r1, *r2, *location); + } + + let requires = requires.complete(); + for (region, borrow, location) in &requires.elements { + result + .restricts + .entry(*location) + .or_insert(BTreeMap::new()) + .entry(*region) + .or_insert(BTreeSet::new()) + .insert(*borrow); + } + } + + borrow_live_at.complete() + }; + + if dump_enabled { + println!( + "borrow_live_at is complete: {} tuples, {:?}", + borrow_live_at.len(), + timer.elapsed() + ); + } + + for (borrow, location) in &borrow_live_at.elements { + result + .borrow_live_at + .entry(*location) + .or_insert(Vec::new()) + .push(*borrow); + } + + result +} diff --git a/src/output/dump.rs b/src/output/dump.rs index 6919fa5b7f6..8e3ba9998a2 100644 --- a/src/output/dump.rs +++ b/src/output/dump.rs @@ -14,12 +14,15 @@ crate trait OutputDump { ); } -crate fn dump_rows(stream: &mut Write, - intern: &InternerTables, - value: &impl OutputDump) -> io::Result<()> { +crate fn dump_rows( + stream: &mut Write, + intern: &InternerTables, + value: &impl OutputDump, +) -> io::Result<()> { let mut rows = Vec::new(); OutputDump::push_all(value, intern, &mut vec![], &mut rows); - let col_width: usize = rows.iter() + let col_width: usize = rows + .iter() .map(|cols| cols.iter().map(|s| s.len()).max().unwrap_or(0)) .max() .unwrap_or(0); diff --git a/src/output/mod.rs b/src/output/mod.rs index 9b1779ff53a..526b5d04954 100644 --- a/src/output/mod.rs +++ b/src/output/mod.rs @@ -17,13 +17,14 @@ use std::collections::{BTreeMap, BTreeSet}; use std::io::{self, Write}; use std::path::PathBuf; +mod datafrog_opt; mod dump; -mod tracking; -mod location_insensitive; mod naive; -mod timely_opt; -mod timely_util; +mod tracking; +// NOTE: the following module was commented out as it contains a location-insensitive analysis +// made using Differential Dataflow before the switch to datafrog, and needs to be converted to this different API. +// mod location_insensitive; #[derive(Clone, Debug)] crate struct Output { @@ -41,11 +42,11 @@ crate struct Output { } impl Output { - crate fn compute(all_facts: &AllFacts, algorithm: Algorithm, dump_enabled: bool, workers: u32) -> Self { + crate fn compute(all_facts: &AllFacts, algorithm: Algorithm, dump_enabled: bool) -> Self { match algorithm { - Algorithm::Naive => naive::compute(dump_enabled, all_facts.clone(), workers), - Algorithm::TimelyOpt => timely_opt::compute(dump_enabled, all_facts.clone(), workers), - Algorithm::LocationInsensitive => location_insensitive::compute(dump_enabled, all_facts.clone(), workers), + Algorithm::Naive => naive::compute(dump_enabled, all_facts.clone()), + Algorithm::DatafrogOpt => datafrog_opt::compute(dump_enabled, all_facts.clone()), + // Algorithm::LocationInsensitive => location_insensitive::compute(dump_enabled, all_facts.clone(), workers), } } @@ -63,14 +64,34 @@ impl Output { } crate fn dump(&self, output_dir: &Option, intern: &InternerTables) -> io::Result<()> { - dump::dump_rows(&mut writer_for(output_dir, "borrow_live_at")?, intern, &self.borrow_live_at)?; + dump::dump_rows( + &mut writer_for(output_dir, "borrow_live_at")?, + intern, + &self.borrow_live_at, + )?; if self.dump_enabled { - dump::dump_rows(&mut writer_for(output_dir, "restricts")?, intern, &self.restricts)?; - dump::dump_rows(&mut writer_for(output_dir, "restricts_anywhere")?, intern, &self.restricts_anywhere)?; - dump::dump_rows(&mut writer_for(output_dir, "region_live_at")?, intern, &self.region_live_at)?; + dump::dump_rows( + &mut writer_for(output_dir, "restricts")?, + intern, + &self.restricts, + )?; + dump::dump_rows( + &mut writer_for(output_dir, "restricts_anywhere")?, + intern, + &self.restricts_anywhere, + )?; + dump::dump_rows( + &mut writer_for(output_dir, "region_live_at")?, + intern, + &self.region_live_at, + )?; dump::dump_rows(&mut writer_for(output_dir, "subset")?, intern, &self.subset)?; - dump::dump_rows(&mut writer_for(output_dir, "subset_anywhere")?, intern, &self.subset_anywhere)?; + dump::dump_rows( + &mut writer_for(output_dir, "subset_anywhere")?, + intern, + &self.subset_anywhere, + )?; } return Ok(()); @@ -85,7 +106,7 @@ impl Output { let mut of = dir.join(name); of.set_extension("facts"); Box::new(fs::File::create(of)?) - }, + } None => { let mut stdout = io::stdout(); write!(&mut stdout, "# {}\n\n", name)?; diff --git a/src/output/naive.rs b/src/output/naive.rs index de005078954..4863df81726 100644 --- a/src/output/naive.rs +++ b/src/output/naive.rs @@ -8,258 +8,233 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! Timely dataflow runs on its own thread. +//! A version of the Naive datalog analysis using Datafrog. -use crate::facts::AllFacts; -use crate::output::Output; -use crate::output::timely_util::populate_args_for_differential_dataflow; -use differential_dataflow::collection::Collection; -use differential_dataflow::operators::*; use std::collections::{BTreeMap, BTreeSet}; -use std::mem; -use std::sync::mpsc; -use std::sync::Arc; -use std::sync::Mutex; -use timely; -use timely::dataflow::operators::*; - -pub(super) fn compute(dump_enabled: bool, all_facts: AllFacts, workers: u32) -> Output { - let result = Arc::new(Mutex::new(Output::new(dump_enabled))); - - // Use a channel to send `all_facts` to one worker (and only one) - let (tx, rx) = mpsc::channel(); - tx.send(all_facts).unwrap(); - mem::drop(tx); - let rx = Mutex::new(rx); - let dataflow_arg = populate_args_for_differential_dataflow(workers); - timely::execute_from_args(dataflow_arg.into_iter(), { - let result = result.clone(); - move |worker| { - // First come, first serve: one worker gets all the facts; - // the others get empty vectors. - let my_facts = rx.lock() - .unwrap() - .recv() - .unwrap_or_else(|_| AllFacts::default()); - - worker.dataflow::<(), _, _>(|scope| { - macro_rules! let_collections { - (let ($($facts:ident,)*) = ..$base:expr;) => { - let ($($facts),*) = ( - $(Collection::<_, _, isize>::new( - $base.$facts - .to_stream(scope) - .map(|datum| (datum, Default::default(), 1)), - ),)* - ); - } - } - - let_collections! { - let ( - borrow_region, - universal_region, - cfg_edge, - killed, - outlives, - region_live_at, - ) = ..my_facts; - } - - // .decl subset(Ra, Rb, P) -- at the point P, R1 <= R2 holds - let subset = outlives.iterate(|subset| { - let outlives = outlives.enter(&subset.scope()); - let cfg_edge = cfg_edge.enter(&subset.scope()); - let region_live_at = region_live_at.enter(&subset.scope()); - let universal_region = universal_region.enter(&subset.scope()); - - // subset(R1, R2, P) :- outlives(R1, R2, P). - let subset1 = outlives.clone(); - - // subset(R1, R3, P) :- - // subset(R1, R2, P), - // subset(R2, R3, P). - let subset2 = subset - .map(|(r1, r2, q)| ((r2, q), r1)) - .join(&subset.map(|(r2, r3, p)| ((r2, p), r3))) - .map(|((_r2, p), r1, r3)| (r1, r3, p)); - - // subset(R1, R2, Q) :- - // subset(R1, R2, P), - // cfg_edge(P, Q), - // (region_live_at(R1, Q); universal_region(R1)), - // (region_live_at(R2, Q); universal_region(R2)). - let subset3base0 = subset.map(|(r1, r2, p)| (p, (r1, r2))).join(&cfg_edge); - let subset3base1 = subset3base0 - .map(|(_p, (r1, r2), q)| ((r1, q), r2)) - .semijoin(®ion_live_at); - let subset3a = subset3base1 - .map(|((r1, q), r2)| ((r2, q), r1)) - .semijoin(®ion_live_at) - .map(|((r2, q), r1)| (r1, r2, q)); - let subset3b = subset3base1 - .map(|((r1, q), r2)| (r2, (q, r1))) - .semijoin(&universal_region) - .map(|(r2, (q, r1))| (r1, r2, q)); - let subset3base2 = subset3base0 - .map(|(_p, (r1, r2), q)| (r1, (q, r2))) - .semijoin(&universal_region); - let subset3c = subset3base2 - .map(|(r1, (q, r2))| (r2, (q, r1))) - .semijoin(&universal_region) - .map(|(r2, (q, r1))| (r1, r2, q)); - let subset3d = subset3base2 - .map(|(r1, (q, r2))| ((r2, q), r1)) - .semijoin(®ion_live_at) - .map(|((r2, q), r1)| (r1, r2, q)); - - subset1 - .concat(&subset2) - .concat(&subset3a) - .concat(&subset3b) - .concat(&subset3c) - .concat(&subset3d) - .distinct() - }); - - // .decl requires(R, B, P) -- at the point, things with region R - // may depend on data from borrow B - let requires = borrow_region.iterate(|requires| { - let borrow_region = borrow_region.enter(&requires.scope()); - let subset = subset.enter(&requires.scope()); - let killed = killed.enter(&requires.scope()); - let region_live_at = region_live_at.enter(&requires.scope()); - let cfg_edge = cfg_edge.enter(&requires.scope()); - let universal_region = universal_region.enter(&requires.scope()); - - // requires(R, B, P) :- borrow_region(R, B, P). - let requires1 = borrow_region.clone(); - - // requires(R2, B, P) :- - // requires(R1, B, P), - // subset(R1, R2, P). - let requires2 = requires - .map(|(r1, b, p)| ((r1, p), b)) - .join(&subset.map(|(r1, r2, p)| ((r1, p), r2))) - .map(|((_r1, p), b, r2)| (r2, b, p)); - - // requires(R, B, Q) :- - // requires(R, B, P), - // !killed(B, P), - // cfg_edge(P, Q), - // (region_live_at(R, Q); universal_region(R)). - let requires_propagate_base = requires - .map(|(r, b, p)| ((b, p), r)) - .antijoin(&killed) - .map(|((b, p), r)| (p, (r, b))) - .join(&cfg_edge); - let requires3 = requires_propagate_base - .map(|(_p, (r, b), q)| ((r, q), b)) - .semijoin(®ion_live_at) - .map(|((r, q), b)| (r, b, q)); - let requires4 = requires_propagate_base - .map(|(_p, (r, b), q)| (r, (q, b))) - .semijoin(&universal_region) - .map(|(r, (q, b))| (r, b, q)); - - requires1 - .concat(&requires2) - .concat(&requires3) - .concat(&requires4) - .distinct() - }); - - // .decl borrow_live_at(B, P) -- true if the restrictions of the borrow B - // need to be enforced at the point P - let borrow_live_at = { - // borrow_live_at(B, P) :- requires(R, B, P), region_live_at(R, P) - let borrow_live_at1 = requires - .map(|(r, b, p)| ((r, p), b)) - .semijoin(®ion_live_at) - .map(|((_r, p), b)| (b, p)); - - // borrow_live_at(B, P) :- requires(R, B, P), universal_region(R). - let borrow_live_at2 = requires - .map(|(r, b, p)| (r, (p, b))) - .semijoin(&universal_region) - .map(|(_r, (p, b))| (b, p)); - - borrow_live_at1.concat(&borrow_live_at2).distinct() - }; - - if dump_enabled { - region_live_at.inspect_batch({ - let result = result.clone(); - move |_timestamp, facts| { - let mut result = result.lock().unwrap(); - for ((region, location), _timestamp, multiplicity) in facts { - assert_eq!(*multiplicity, 1); - result - .region_live_at - .entry(*location) - .or_insert(vec![]) - .push(*region); - } - } - }); - - subset.inspect_batch({ - let result = result.clone(); - move |_timestamp, facts| { - let mut result = result.lock().unwrap(); - for ((r1, r2, location), _timestamp, multiplicity) in facts { - assert_eq!(*multiplicity, 1); - result - .subset - .entry(*location) - .or_insert(BTreeMap::new()) - .entry(*r1) - .or_insert(BTreeSet::new()) - .insert(*r2); - result.region_degrees.update_degrees(*r1, *r2, *location); - } - } - }); - - requires.inspect_batch({ - let result = result.clone(); - move |_timestamp, facts| { - let mut result = result.lock().unwrap(); - for ((region, borrow, location), _timestamp, multiplicity) in facts { - assert_eq!(*multiplicity, 1); - result - .restricts - .entry(*location) - .or_insert(BTreeMap::new()) - .entry(*region) - .or_insert(BTreeSet::new()) - .insert(*borrow); - } - } - }); - } - - borrow_live_at.inspect_batch({ - let result = result.clone(); - move |_timestamp, facts| { - let mut result = result.lock().unwrap(); - for ((borrow, location), _timestamp, multiplicity) in facts { - assert_eq!(*multiplicity, 1); - result - .borrow_live_at - .entry(*location) - .or_insert(Vec::new()) - .push(*borrow); - } - } - }); +use std::time::Instant; + +use crate::facts::{AllFacts, Loan, Point, Region}; +use crate::output::Output; + +use datafrog::{Iteration, Relation}; + +// NOTE: The implementation could be simplified and optimized: +// - some indices could be shared between Iterations +// - having more than 1 Iteration is not absolutely necessary +pub(super) fn compute(dump_enabled: bool, mut all_facts: AllFacts) -> Output { + let all_points: BTreeSet = all_facts + .cfg_edge + .iter() + .map(|&(p, _)| p) + .chain(all_facts.cfg_edge.iter().map(|&(_, q)| q)) + .collect(); + + for &r in &all_facts.universal_region { + for &p in &all_points { + all_facts.region_live_at.push((r, p)); + } + } + + let mut result = Output::new(dump_enabled); + + let subset_start = Instant::now(); + + // compute the subsets rules, but indexed ((Region, Point), Region) for the next iteration + let subset = { + // Create a new iteration context, ... + let mut iteration1 = Iteration::new(); + + // .. some variables, .. + let subset = iteration1.variable::<(Region, Region, Point)>("subset"); + + // different indices for `subset`. + let subset_r1p = iteration1.variable::<((Region, Point), Region)>("subset_r1p"); + let subset_r2p = iteration1.variable::<((Region, Point), Region)>("subset_r2p"); + let subset_p = iteration1.variable::<(Point, (Region, Region))>("subset_p"); + + // temporaries as we perform a multi-way join. + let subset_1 = iteration1.variable::<((Region, Point), Region)>("subset_1"); + let subset_2 = iteration1.variable::<((Region, Point), Region)>("subset_2"); + + let region_live_at = iteration1.variable::<((Region, Point), ())>("region_live_at"); // redundantly computed index + let cfg_edge_p = iteration1.variable::<(Point, Point)>("cfg_edge_p"); // redundantly computed index + + // load initial facts. + subset.insert(all_facts.outlives.into()); + region_live_at.insert(Relation::from( + all_facts.region_live_at.iter().map(|&(r, p)| ((r, p), ())), + )); + cfg_edge_p.insert(all_facts.cfg_edge.clone().into()); + + // .. and then start iterating rules! + while iteration1.changed() { + // remap fields to re-index by keys. + subset_r1p.from_map(&subset, |&(r1, r2, p)| ((r1, p), r2)); + subset_r2p.from_map(&subset, |&(r1, r2, p)| ((r2, p), r1)); + subset_p.from_map(&subset, |&(r1, r2, p)| (p, (r1, r2))); + + // subset(R1, R2, P) :- outlives(R1, R2, P). + // Already loaded; outlives is static. + + // subset(R1, R3, P) :- + // subset(R1, R2, P), + // subset(R2, R3, P). + subset.from_join(&subset_r2p, &subset_r1p, |&(_r2, p), &r1, &r3| (r1, r3, p)); + + // subset(R1, R2, Q) :- + // subset(R1, R2, P), + // cfg_edge(P, Q), + // region_live_at(R1, Q), + // region_live_at(R2, Q). + + subset_1.from_join(&subset_p, &cfg_edge_p, |&_p, &(r1, r2), &q| ((r1, q), r2)); + subset_2.from_join(&subset_1, ®ion_live_at, |&(r1, q), &r2, &()| { + ((r2, q), r1) }); + subset.from_join(&subset_2, ®ion_live_at, |&(r2, q), &r1, &()| (r1, r2, q)); + } + + subset_r1p.complete() + }; + + if dump_enabled { + println!( + "subset is complete: {} tuples, {:?}", + subset.len(), + subset_start.elapsed() + ); + + for ((r1, location), r2) in &subset.elements { + result + .subset + .entry(*location) + .or_insert(BTreeMap::new()) + .entry(*r1) + .or_insert(BTreeSet::new()) + .insert(*r2); + result.region_degrees.update_degrees(*r1, *r2, *location); + } + } + + let requires_start = Instant::now(); + + // compute the requires rules, but indexed ((Region, Point), Loan) for the next iteration + let requires = { + // Create a new iteration context, ... + let mut iteration2 = Iteration::new(); + + // .. some variables, .. + let requires = iteration2.variable::<(Region, Loan, Point)>("requires"); + requires.insert(all_facts.borrow_region.into()); + + // some indexes + let requires_rp = iteration2.variable::<((Region, Point), Loan)>("requires_rp"); + let requires_bp = iteration2.variable::<((Loan, Point), Region)>("requires_bp"); + + let requires_1 = iteration2.variable::<(Point, (Loan, Region))>("requires_1"); + let requires_2 = iteration2.variable::<((Region, Point), Loan)>("requires_2"); + + // since we're using subset mapped ((r, p), r) we can use it directly out of iteration 1 + let subset_r1p = iteration2.variable::<((Region, Point), Region)>("subset_r1p"); + subset_r1p.insert(subset); + + let killed = all_facts.killed.into(); + let region_live_at = iteration2.variable::<((Region, Point), ())>("region_live_at"); // redundantly computed index + let cfg_edge_p = iteration2.variable::<(Point, Point)>("cfg_edge_p"); // redundantly computed index + + // load initial facts. + region_live_at.insert(Relation::from( + all_facts.region_live_at.iter().map(|&(r, p)| ((r, p), ())), + )); + cfg_edge_p.insert(all_facts.cfg_edge.into()); + + // .. and then start iterating rules! + while iteration2.changed() { + requires_rp.from_map(&requires, |&(r, b, p)| ((r, p), b)); + requires_bp.from_map(&requires, |&(r, b, p)| ((b, p), r)); + + // requires(R, B, P) :- borrow_region(R, B, P). + // Already loaded; borrow_region is static. + + // requires(R2, B, P) :- + // requires(R1, B, P), + // subset(R1, R2, P). + requires.from_join(&requires_rp, &subset_r1p, |&(_r1, p), &b, &r2| (r2, b, p)); + + // requires(R, B, Q) :- + // requires(R, B, P), + // !killed(B, P), + // cfg_edge(P, Q), + // region_live_at(R, Q). + requires_1.from_antijoin(&requires_bp, &killed, |&(b, p), &r| (p, (b, r))); + requires_2.from_join(&requires_1, &cfg_edge_p, |&_p, &(b, r), &q| ((r, q), b)); + requires.from_join(&requires_2, ®ion_live_at, |&(r, q), &b, &()| (r, b, q)); + } + + requires_rp.complete() + }; + + if dump_enabled { + println!( + "requires is complete: {} tuples, {:?}", + requires.len(), + requires_start.elapsed() + ); + + for ((region, location), borrow) in &requires.elements { + result + .restricts + .entry(*location) + .or_insert(BTreeMap::new()) + .entry(*region) + .or_insert(BTreeSet::new()) + .insert(*borrow); + } + } + + let borrow_live_at_start = Instant::now(); + + let borrow_live_at = { + // Create a new iteration context, ... + let mut iteration3 = Iteration::new(); + + // .. some variables, .. + let borrow_live_at = iteration3.variable::<(Loan, Point)>("borrow_live_at"); + + // since we're using requires mapped ((r, p), b) we can use it directly out of iteration 2 + let requires_rp = iteration3.variable::<((Region, Point), Loan)>("requires_rp"); + requires_rp.insert(requires.into()); + + let region_live_at = iteration3.variable::<((Region, Point), ())>("region_live_at"); // redundantly computed index + + // load initial facts. + region_live_at.insert(Relation::from( + all_facts.region_live_at.iter().map(|&(r, p)| ((r, p), ())), + )); + + while iteration3.changed() { + // borrow_live_at(B, P) :- requires(R, B, P), region_live_at(R, P) + borrow_live_at.from_join(&requires_rp, ®ion_live_at, |&(_r, p), &b, &()| (b, p)); } - }).unwrap(); - // Remove from the Arc and Mutex, since it is fully populated now. - Arc::try_unwrap(result) - .unwrap_or_else(|_| panic!("somebody still has a handle to this arc")) - .into_inner() - .unwrap() + borrow_live_at.complete() + }; + + if dump_enabled { + println!( + "borrow_live_at is complete: {} tuples, {:?}", + borrow_live_at.len(), + borrow_live_at_start.elapsed() + ); + } + + for (borrow, location) in &borrow_live_at.elements { + result + .borrow_live_at + .entry(*location) + .or_insert(Vec::new()) + .push(*borrow); + } + + result } diff --git a/src/output/timely_opt.rs b/src/output/timely_opt.rs deleted file mode 100644 index cb06d60ea01..00000000000 --- a/src/output/timely_opt.rs +++ /dev/null @@ -1,406 +0,0 @@ -// Copyright 2017 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Timely dataflow runs on its own thread. - -use crate::facts::{AllFacts, Loan, Point, Region}; -use crate::output::Output; -use crate::output::timely_util::populate_args_for_differential_dataflow; -use differential_dataflow::collection::Collection; -use differential_dataflow::operators::arrange::{ArrangeByKey, ArrangeBySelf}; -use differential_dataflow::operators::iterate::Variable; -use differential_dataflow::operators::*; -use std::collections::{BTreeMap, BTreeSet}; -use std::mem; -use std::sync::mpsc; -use std::sync::Arc; -use std::sync::Mutex; -use timely; -use timely::dataflow::operators::*; -use timely::dataflow::Scope; - -pub(super) fn compute(dump_enabled: bool, mut all_facts: AllFacts, workers: u32) -> Output { - // Declare that each universal region is live at every point. - let all_points: BTreeSet = all_facts - .cfg_edge - .iter() - .map(|&(p, _)| p) - .chain(all_facts.cfg_edge.iter().map(|&(_, q)| q)) - .collect(); - - for &r in &all_facts.universal_region { - for &p in &all_points { - all_facts.region_live_at.push((r, p)); - } - } - - let result = Arc::new(Mutex::new(Output::new(dump_enabled))); - - // Use a channel to send `all_facts` to one worker (and only one) - let (tx, rx) = mpsc::channel(); - tx.send(all_facts).unwrap(); - mem::drop(tx); - let rx = Mutex::new(rx); - let dataflow_arg = populate_args_for_differential_dataflow(workers); - timely::execute_from_args(dataflow_arg.into_iter(), { - let result = result.clone(); - move |worker| { - // First come, first serve: one worker gets all the facts; - // the others get empty vectors. - let my_facts = rx.lock() - .unwrap() - .recv() - .unwrap_or_else(|_| AllFacts::default()); - - worker.dataflow::<(), _, _>(|scope| { - macro_rules! let_collections { - (let ($($facts:ident,)*) = ..$base:expr;) => { - let ($($facts),*) = ( - $(Collection::<_, _, isize>::new( - $base.$facts - .to_stream(scope) - .map(|datum| (datum, Default::default(), 1)), - ),)* - ); - } - } - - let_collections! { - let ( - borrow_region, - cfg_edge, - killed, - outlives, - region_live_at, - ) = ..my_facts; - } - - let cfg_edge = cfg_edge.arrange_by_key(); - let region_live_at_by_self = region_live_at.arrange_by_self(); - - let (subset, requires) = scope.scoped(|subscope| { - let outlives = outlives.enter(&subscope); - let cfg_edge = cfg_edge.enter(&subscope); - let region_live_at = region_live_at.enter(&subscope); - let region_live_at_by_self = region_live_at_by_self.enter(&subscope); - let borrow_region = borrow_region.enter(&subscope); - let killed = killed.enter(&subscope); - - // .decl subset(R1, R2, P) - // - // At the point P, R1 <= R2. - // - // subset(R1, R2, P) :- outlives(R1, R2, P). - let subset = Variable::from(Collection::new( - None::<((Region, Region, Point), _, _)> - .into_iter() - .to_stream(subscope), - )); - let subset0 = outlives.clone(); - - // .decl requires(R, B, P) -- at the point, things with region R - // may depend on data from borrow B - // - // requires(R, B, P) :- borrow_region(R, B, P). - let requires = Variable::from(Collection::new( - None::<((Region, Loan, Point), _, _)> - .into_iter() - .to_stream(subscope), - )); - let requires0 = borrow_region.clone(); - - // .decl live_to_dead_regions(R1, R2, P, Q) - // - // The regions `R1` and `R2` are "live to dead" - // on the edge `P -> Q` if: - // - // - In P, `R1` <= `R2` - // - In Q, `R1` is live but `R2` is dead. - // - // In that case, `Q` would like to add all the - // live things reachable from `R2` to `R1`. - // - // live_to_dead_regions(R1, R2, P, Q) :- - // subset(R1, R2, P), - // cfg_edge(P, Q), - // region_live_at(R1, Q), - // !region_live_at(R2, Q). - let live_to_dead_regions = { - subset - .map(|(r1, r2, p)| (p, (r1, r2))) - .join_core(&cfg_edge, |&p, &(r1, r2), &q| Some(((r1, q), (r2, p)))) - .join_core(®ion_live_at_by_self, |&k, &v, _| Some((k, v))) - .map(|((r1, q), (r2, p))| ((r2, q), (r1, p))) - .antijoin(®ion_live_at) - .map(|((r2, q), (r1, p))| (r1, r2, p, q)) - }; - - // .decl dead_region_requires(R, B, P, Q) - // - // The region `R` requires the borrow `B`, but the - // region `R` goes dead along the edge `P -> Q` - // - // dead_region_requires(R, B, P, Q) :- - // requires(R, B, P), - // !killed(B, P), - // cfg_edge(P, Q), - // !region_live_at(R, Q). - let dead_region_requires = { - requires - .map(|(r, b, p)| ((b, p), r)) - .antijoin(&killed) - .map(|((b, p), r)| (p, (r, b))) - .join_core(&cfg_edge, |&p, &(r, b), &q| Some(((r, q), (b, p)))) - .antijoin(®ion_live_at) - .map(|((r, q), (b, p))| (r, b, p, q)) - }; - - // .decl dead_can_reach_origins(R, P, Q) - // - // Contains dead regions where we are interested - // in computing the transitive closure of things they - // can reach. - let dead_can_reach_origins = { - let dead_can_reach_origins0 = - { live_to_dead_regions.map(|(_r1, r2, p, q)| ((r2, p), q)) }; - let dead_can_reach_origins1 = - { dead_region_requires.map(|(r, _b, p, q)| ((r, p), q)) }; - dead_can_reach_origins0 - .concat(&dead_can_reach_origins1) - .distinct_total() - }; - - // .decl dead_can_reach(R1, R2, P, Q) - // - // Indicates that the region `R1`, which is dead - // in `Q`, can reach the region `R2` in P. - // - // This is effectively the transitive subset - // relation, but we try to limit it to regions - // that are dying on the edge P -> Q. - // - // NB. As a micro-optimization, `dead_can_reach` - // is stored as `dead_can_reach((R2, Q), (R1, - // P))`, since that happens to be the ordering - // that is required for the joins it participates - // in. - let dead_can_reach = { - let dead_can_reach = Variable::from(Collection::new( - None::<(((Region, Point), (Region, Point)), _, _)> - .into_iter() - .to_stream(subscope), - )); - - // dead_can_reach(R1, R2, P, Q) :- - // dead_can_reach_origins(R1, P, Q), - // subset(R1, R2, P). - let dead_can_reach0 = { - dead_can_reach_origins.join_map( - &subset.map(|(r1, r2, p)| ((r1, p), r2)), - |&(r1, p), &q, &r2| ((r2, q), (r1, p)), - ) - }; - - // dead_can_reach(R1, R3, P, Q) :- - // dead_can_reach(R1, R2, P, Q), - // !region_live_at(R2, Q), - // subset(R2, R3, P). - // - // This is the "transitive closure" rule, but - // note that we only apply it with the - // "intermediate" region R2 is dead at Q. - let dead_can_reach1 = { - dead_can_reach - .antijoin(®ion_live_at) - .map(|((r2, q), (r1, p))| ((r2, p), (r1, q))) - .join_map( - &subset.map(|(r2, r3, p)| ((r2, p), r3)), - |&(_r2, p), &(r1, q), &r3| ((r3, q), (r1, p)), - ) - }; - - dead_can_reach - .set(&dead_can_reach0.concat(&dead_can_reach1).distinct_total()) - }; - - // .decl dead_can_reach_live(R1, R2, P, Q) - // - // Indicates that, along the edge `P -> Q`, the - // dead (in Q) region R1 can reach the live (in Q) - // region R2 via a subset relation. This is a - // subset of the full `dead_can_reach` relation - // where we filter down to those cases where R2 is - // live in Q. - // - // NB. As a micro-optimization, - // `dead_can_reach_live` is stored as `((r1, p, - // q), r2)` since that happens to be the ordering - // required for the joins that it participates in. - let dead_can_reach_live = { - dead_can_reach - .join_core(®ion_live_at_by_self, |&k, &v, _| Some((k, v))) - .map(|((r2, q), (r1, p))| ((r1, p, q), r2)) - .arrange_by_key() - }; - - // subset(R1, R2, Q) :- - // subset(R1, R2, P) :- - // cfg_edge(P, Q), - // region_live_at(R1, Q), - // region_live_at(R2, Q). - // - // Carry `R1 <= R2` from P into Q if both `R1` and - // `R2` are live in Q. - let subset1 = subset - .map(|(r1, r2, p)| (p, (r1, r2))) - .join_core(&cfg_edge, |_p, &(r1, r2), &q| Some(((r1, q), r2))) - .join_core(®ion_live_at_by_self, |&k, &v, _| Some((k, v))) - .map(|((r1, q), r2)| ((r2, q), r1)) - .join_core(®ion_live_at_by_self, |&k, &v, _| Some((k, v))) - .map(|((r2, q), r1)| (r1, r2, q)); - - // subset(R1, R3, Q) :- - // live_to_dead_regions(R1, R2, P, Q), - // dead_can_reach_live(R2, R3, P, Q). - let subset2 = { - live_to_dead_regions - .map(|(r1, r2, p, q)| ((r2, p, q), r1)) - .join_core(&dead_can_reach_live, |&(_r2, _p, q), &r1, &r3| { - Some((r1, r3, q)) - }) - }; - - // requires(R2, B, Q) :- - // dead_region_requires(R1, B, P, Q), - // dead_can_reach_live(R1, R2, P, Q). - // - // Communicate a `R1 requires B` relation across - // an edge `P -> Q` where `R1` is dead in Q; in - // that case, for each region `R2` live in `Q` - // where `R1 <= R2` in P, we add `R2 requires B` - // to `Q`. - let requires1 = dead_region_requires - .map(|(r1, b, p, q)| ((r1, p, q), b)) - .join_core(&dead_can_reach_live, |&(_r1, _p, q), &b, &r2| { - Some((r2, b, q)) - }); - - // requires(R, B, Q) :- - // requires(R, B, P), - // !killed(B, P), - // cfg_edge(P, Q), - // region_live_at(R, Q). - let requires2 = requires - .map(|(r, b, p)| ((b, p), r)) - .antijoin(&killed) - .map(|((b, p), r)| (p, (r, b))) - .join_core(&cfg_edge, |&_p, &(r, b), &q| Some(((r, q), b))) - .join_core(®ion_live_at_by_self, |&k, &v, _| Some((k, v))) - .map(|((r, q), b)| (r, b, q)); - - let requires = requires.set(&requires0 - .concat(&requires1) - .concat(&requires2) - .distinct_total()); - - let subset = - subset.set(&subset0.concat(&subset1).concat(&subset2).distinct_total()); - - (subset.leave(), requires.leave()) - }); - - // .decl borrow_live_at(B, P) -- true if the restrictions of the borrow B - // need to be enforced at the point P - let borrow_live_at = { - // borrow_live_at(B, P) :- requires(R, B, P), region_live_at(R, P) - let borrow_live_at1 = requires - .map(|(r, b, p)| ((r, p), b)) - .join_core(®ion_live_at_by_self, |&k, &v, _| Some((k, v))) - .map(|((_r, p), b)| (b, p)); - - borrow_live_at1.distinct_total() - }; - - if dump_enabled { - region_live_at.inspect_batch({ - let result = result.clone(); - move |_timestamp, facts| { - let mut result = result.lock().unwrap(); - for ((region, location), _timestamp, multiplicity) in facts { - assert_eq!(*multiplicity, 1); - result - .region_live_at - .entry(*location) - .or_insert(vec![]) - .push(*region); - } - } - }); - - subset.inspect_batch({ - let result = result.clone(); - move |_timestamp, facts| { - let mut result = result.lock().unwrap(); - for ((r1, r2, location), _timestamp, multiplicity) in facts { - assert_eq!(*multiplicity, 1); - result - .subset - .entry(*location) - .or_insert(BTreeMap::new()) - .entry(*r1) - .or_insert(BTreeSet::new()) - .insert(*r2); - result.region_degrees.update_degrees(*r1, *r2, *location); - } - } - }); - - requires.inspect_batch({ - let result = result.clone(); - move |_timestamp, facts| { - let mut result = result.lock().unwrap(); - for ((region, borrow, location), _timestamp, multiplicity) in facts { - assert_eq!(*multiplicity, 1); - result - .restricts - .entry(*location) - .or_insert(BTreeMap::new()) - .entry(*region) - .or_insert(BTreeSet::new()) - .insert(*borrow); - } - } - }); - } - - borrow_live_at.inspect_batch({ - let result = result.clone(); - move |_timestamp, facts| { - let mut result = result.lock().unwrap(); - for ((borrow, location), _timestamp, multiplicity) in facts { - assert_eq!(*multiplicity, 1); - result - .borrow_live_at - .entry(*location) - .or_insert(Vec::new()) - .push(*borrow); - } - } - }); - }); - } - }).unwrap(); - - // Remove from the Arc and Mutex, since it is fully populated now. - Arc::try_unwrap(result) - .unwrap_or_else(|_| panic!("somebody still has a handle to this arc")) - .into_inner() - .unwrap() -} diff --git a/src/output/timely_util.rs b/src/output/timely_util.rs deleted file mode 100644 index 868193f0678..00000000000 --- a/src/output/timely_util.rs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2017 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -pub fn populate_args_for_differential_dataflow(workers: u32) -> Vec { - let mut dataflow_arg = Vec::new(); - if workers > 1 { - dataflow_arg.push(format!("-w")); - dataflow_arg.push(format!("{}", workers)); - } - return dataflow_arg; -} diff --git a/src/output/tracking.rs b/src/output/tracking.rs index 780626d54d1..662cefec4e5 100644 --- a/src/output/tracking.rs +++ b/src/output/tracking.rs @@ -44,16 +44,16 @@ impl RegionDegrees { match self.out_degree.get(region_point) { Some(out_count) => if *out_count > 1 && *in_count > 1 { return true; - } + }, None => {} } } return false; } - crate fn histogram(&self) -> (Histogram,Histogram) { + crate fn histogram(&self) -> (Histogram, Histogram) { let mut histo_in = Histogram::with_buckets(10); - let mut histo_out= Histogram::with_buckets(10); + let mut histo_out = Histogram::with_buckets(10); for v in self.in_degree.values() { histo_in.add(*v as u64); } diff --git a/src/test.rs b/src/test.rs index ab27a9b6e33..ca97d377baf 100644 --- a/src/test.rs +++ b/src/test.rs @@ -1,8 +1,8 @@ #![cfg(test)] +use crate::cli::Algorithm; use crate::intern; use crate::output::Output; -use crate::cli::Algorithm; use crate::tab_delim; use failure::Error; use std::path::Path; @@ -15,13 +15,11 @@ fn test_fn(dir_name: &str, fn_name: &str) -> Result<(), Error> { .join("nll-facts") .join(fn_name); println!("facts_dir = {:?}", facts_dir); - let no_of_workers = 1; let tables = &mut intern::InternerTables::new(); let all_facts = tab_delim::load_tab_delimited_facts(tables, &facts_dir)?; - let naive = Output::compute(&all_facts, Algorithm::Naive, false, no_of_workers); - let timely_opt = Output::compute(&all_facts, Algorithm::TimelyOpt, false, no_of_workers); - assert_eq!(naive.borrow_live_at, timely_opt.borrow_live_at); - // FIXME: check `_result` somehow + let naive = Output::compute(&all_facts, Algorithm::Naive, false); + let opt = Output::compute(&all_facts, Algorithm::DatafrogOpt, false); + assert_eq!(naive.borrow_live_at, opt.borrow_live_at); } }